簡體   English   中英

復制具有n:m-relation的數據集

[英]Copy datasets with n:m-relation

我想使用單個SQL語句

insert into T (...) select ... from T where ...

復制很多數據集。 我的問題是從表T到其他表有N:M關系,這些也必須被復制。 如果我不知道哪個原始數據集屬於哪個復制數據集,我該怎么做呢? 讓我舉例說明。

以前數據庫的內容:

T

ID  | COL1 | COL2    
-----------------
1   | A    | B
2   | C    | D

N:來自表T的M表引用表U(表U未顯示):

T   | U              
---------
1   | 100
1   | 101
2   | 100
2   | 102

我的復制操作[???]是我不知道的部分:

insert into T (COL1, COL2) select COL1, COL2 from T
insert into NM (T, U) select [???]

以下數據庫的內容:

T

ID  | COL1 | COL2
-----------------
1   | A    | B
2   | C    | D
3   | A    | B
4   | C    | D

N:M-表:

T   | U
---------
1   | 100
1   | 101
2   | 100
2   | 102
3   | 100
3   | 101
4   | 100
4   | 102

注意:

  • 我有成千上萬的數據集(不只是兩個)
  • 我想使用'insert ... select'來獲得更好的性能

如果你足夠幸運地運行當前的PostgreSQL 9.1 ,那么使用新的數據修改CTE單個命令就可以實現優雅而快速的解決方案。

MySQL不支持公用表表達式(CTE) ,更不用說數據修改CTE了。

假設(col1, col2)最初是唯一的:

查詢1

  • 在這種情況下,您可以輕松地從表中選擇任意切片。
  • 不會浪費t.id序列號。

WITH s AS (
    SELECT id, col1, col2
    FROM   t
--  WHERE  some condition
    )
    ,i AS (
    INSERT INTO t (col1, col2)
    SELECT col1, col2   -- I gather from comments that id is a serial column
    FROM   s
    RETURNING id, col1, col2
    )
INSERT INTO tu (t, u)
SELECT i.id, tu.u
FROM   tu
JOIN   s ON tu.t = s.id
JOIN   i USING (col1, col2);

如果(col1, col2) 不是唯一的 ,我會看到另外兩種方式:

查詢2

  • 使用窗口函數row_number()使非唯一行唯一。
  • t.id空間中INSERT沒有孔的行,就像上面的查詢一樣。

WITH s AS (
    SELECT id, col1, col2
         , row_number() OVER (PARTITION BY col1, col2) AS rn
    FROM   t
--  WHERE some condition
    )
    ,i AS (
    INSERT INTO t (col1, col2)
    SELECT col1, col2
    FROM   s
    RETURNING id, col1, col2
    )
    ,r AS (
    SELECT *
         , row_number() OVER (PARTITION BY col1, col2) AS rn
    FROM   i
    )
INSERT INTO tu (t, u)
SELECT r.id, tu.u
FROM   r
JOIN   s USING (col1, col2, rn)    -- match exactly one id per row
JOIN   tu ON tu.t = s.id;

查詢3

  • 這基於@ypercube已經提供的相同的想法,但是在一個查詢中。
  • 如果當前t.id數字空間中存在漏洞,則相應的新行將被燒毀序列號。
  • 不要忘記將序列重置為新的最大值,否則您將在t中獲取重復的鍵錯誤,從而從序列中繪制id的默認值。 我把它作為最后一步整合到命令中。 這種方式最快,最安全。

WITH s AS (
    SELECT max(id) AS max_id
    FROM   t
    )
    ,i AS (
    INSERT INTO t (id, col1, col2)
    SELECT id + s.max_id, col1, col2
    FROM   t, s
    )
    ,j AS (
    INSERT INTO tu (t, u)
    SELECT tu.t + s.max_id, tu.u
    FROM   tu, s
    )
SELECT setval('t_id_seq', s.max_id + s.max_id)
FROM   s;

有關手冊中setval()的詳細信息。

測試設置

快速測試。

CREATE TEMP TABLE t (id serial primary key, col1 text, col2 text);
INSERT INTO t (col1, col2) VALUES 
 ('A', 'B')
,('C', 'D');

CREATE TEMP TABLE tu (t int, u int);
INSERT INTO tu VALUES
 (1, 100)
,(1, 101)
,(2, 100)
,(2, 102);

SELECT * FROM t;
SELECT * FROM tu;

最近有一個類似的問題 ,我提供了一個類似的答案。 對於沒有CTE和窗口功能的8.3版本的替代品。

步驟1.鎖定(兩個)表或確保僅運行此腳本。 禁用FK檢查。

步驟2.按以下順序使用這兩個INSERT語句:

INSERT INTO NM 
    (T, U) 
  SELECT 
      T + maxID, U
  FROM 
      NM
    CROSS JOIN
      ( SELECT MAX(ID) AS maxID 
        FROM T
      ) AS m

INSERT INTO T 
    (ID, COL1, COL2) 
  SELECT 
      ID+maxID, COL1, COL2 
  FROM 
      T
    CROSS JOIN
      ( SELECT MAX(ID) AS maxID 
        FROM T
      ) AS m

步驟3.重新啟用FK。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM