2016-11-18 77 views
2

我有三个表:如何使用单个PostgreSQL查询执行深层复制?

CREATE TABLE offers 
(
    id serial NOT NULL PRIMARY KEY, 
    title character varying(1000) NOT NULL DEFAULT ''::character varying 
); 

CREATE TABLE items 
(
    id serial NOT NULL PRIMARY KEY, 
    offer_id integer NOT NULL, 
    title character varying(1000) NOT NULL DEFAULT ''::character varying, 
    CONSTRAINT items_offer_id_fkey FOREIGN KEY (offer_id) 
     REFERENCES offers (id) 
); 

CREATE TABLE sizes 
(
    id serial NOT NULL PRIMARY KEY, 
    item_id integer NOT NULL, 
    title character varying(1000) NOT NULL DEFAULT ''::character varying, 
    CONSTRAINT sizes_item_id_fkey FOREIGN KEY (item_id) 
     REFERENCES items (id) 
); 

我有1个报价有2项。每个项目都有2种尺寸:

INSERT INTO offers (title) VALUES ('My Offer'); 
INSERT INTO items (offer_id, title) VALUES (1, 'First Item'); 
INSERT INTO items (offer_id, title) VALUES (1, 'Second Item'); 
INSERT INTO sizes (item_id, title) VALUES (1, 'First Size of Item #1'); 
INSERT INTO sizes (item_id, title) VALUES (1, 'Second Size of Item #1'); 
INSERT INTO sizes (item_id, title) VALUES (2, 'First Size of Item #2'); 
INSERT INTO sizes (item_id, title) VALUES (2, 'Second Size of Item #2'); 

有没有办法来克隆其所有的项目和尺寸的单个查询报价?

我试着用CTE来解决它,这是我的SQL:

WITH tmp_offers AS (
    INSERT INTO offers (title) 
    SELECT title FROM offers WHERE id = 1 
    RETURNING id 
), tmp_items AS (
    INSERT INTO items (offer_id, title) 
    (SELECT (SELECT id FROM tmp_offers), title FROM items WHERE offer_id = 1) 
    RETURNING id 
) 
INSERT INTO sizes (item_id, title) 
(SELECT (SELECT id FROM tmp_items), title FROM sizes WHERE id IN (
    SELECT sizes.id FROM sizes 
    JOIN items ON items.id = sizes.item_id 
    WHERE items.offer_id = 1 
)); 

但这SQL结果错误,我解决不了:

错误:不止一个通过作为表达式的子查询返回的行

非常感谢您的帮助。

P.S.我使用PostgreSQL 9.5

回答

3

这应该工作:

WITH tmp_offers AS (
    INSERT INTO offers (title) 
    SELECT title 
    FROM offers 
    WHERE id = 1 
    RETURNING id 
), tmp_items AS (
    INSERT INTO items (offer_id, title) 
    SELECT o.id, i.title 
    FROM items i 
     cross join tmp_offers o 
    WHERE i.offer_id = 1 
    order by i.id 
    RETURNING items.id 
), numbered_new as (
    select ti.id, 
     row_number() over (order by ti.id) as rn 
    from tmp_items ti 
), numbered_old as (
    select i.id, 
     row_number() over (order by i.id) as rn 
    from items i 
    WHERE i.offer_id = 1 
), item_mapper as (
    select n.id as new_item_id, 
     o.id as old_item_id 
    from numbered_new n 
    join numbered_old o on n.rn = o.rn 
) 
INSERT INTO sizes (item_id, title) 
select im.new_item_id, s.title 
from sizes s 
    join item_mapper im on im.old_item_id = s.item_id; 

在线例如:http://rextester.com/RYQUS11008

+0

谢谢,先生。这简直太神奇了,完全按照要求工作。 –

+0

我想我在这里发现了问题。最后一个查询会**,其中**中的s.id并从* items *表中选择一个id。 –

+0

@IM_AG:你说得对。原来尺寸表中的旧项目ID到新项目ID的映射并不像我想象的那么简单。我的新版本应该照顾到这一点。 –

0

你很接近。这是一个需要工作的最终查询:

WITH tmp_offers AS (
     INSERT INTO offers (title) 
      SELECT title FROM offers WHERE id = 1 
      RETURNING id 
    ), 
    tmp_items AS (
    INSERT INTO items (offer_id, title) 
     SELECT o.id, i.title 
     FROM items i CROSS JOIN 
      (SELECT id FROM tmp_offers) o 
     WHERE i.offer_id = 1 
     RETURNING id, title 
) 
INSERT INTO sizes (item_id, title) 
    SELECT i.id, i.title 
    FROM tmp_items i; 

主要在这里不同的是,现在tmp_items有两列 - 他们似乎是要用于此目的的列。

+0

但sizes.title值是错误的。 –