【问题标题】:How do I do a deep copy with a single PostgreSQL query?如何使用单个 PostgreSQL 查询进行深层复制?
【发布时间】:2016-11-18 20:57:09
【问题描述】:

我有三张桌子:

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 导致了一个错误,我无法解决:

错误:用作表达式的子查询返回多行

非常感谢您的帮助。

附注我使用 PostgreSQL 9.5

【问题讨论】:

    标签: sql postgresql common-table-expression bulkinsert


    【解决方案1】:

    这应该可行:

    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

    【讨论】:

    • 谢谢您,先生。这简直太棒了,而且完全符合要求。
    • 我想我在这里发现了一个问题。最后一个查询执行 where s.id in 并从 items 表中选择一个 id。
    • @IM_AG:你是对的。原来旧项目 ID 到尺寸表中新项目 ID 的映射并不像我想象的那么简单。我的新版本应该会解决这个问题。
    • 天哪,这令人印象深刻。惊人的 SQL 技能。非常感谢。
    • 我也很欣赏到 rextester 的链接及其与 postgresql“模拟”的功能。它还具有许多其他语言/功能。
    【解决方案2】:

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

    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 现在有两列——它们似乎是您为此目的所需的列。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-03
      • 2019-08-21
      • 1970-01-01
      • 2021-05-13
      相关资源
      最近更新 更多