插入多个表并选择第一个结果
Inserting into multiple tables and selecting the first result
假设我有两个表实现了一个非常简单的发票系统(注意:架构无法更改):
create table invoices(
id serial primary key,
parent_invoice_id int null references invoices(id),
name text not null
);
create table line_items(
id serial primary key,
invoice_id int not null references invoices(id),
amount int not null
);
用户可以"clone" 发票并参考原始"parent" 发票。在系统中,克隆后直接需要invoice(但不需要line_items
)。因此,克隆发票后,必须返回新的发票。这是我用来克隆发票的 SQL:
with new_invoice_row as (
insert into invoices (parent_invoice_id, name)
values (12345/*invoice_to_clone_id*/, 'Hello World')
returning *
),
new_line_item_rows as (
insert into line_items (invoice_id, amount)
select
new_invoice_row.id, line_item.amount
from line_items
cross join new_invoice_row
where
line_item.invoice_id = 12345/*invoice_to_clone_id*/
returning id
)
select * from new_invoice_row;
问题:
cross join
的表现会好吗?我正在考虑能够只删除 cross join
以减少必须进行连接,但它不会 运行(错误:missing FROM-clause entry for table "new_invoice_row"
):
...
insert into line_items (invoice_id, amount)
select
new_invoice_row.id, line_item.amount
from line_items
where
line_item.invoice_id = 12345
returning id
...
是否可以删除 new_line_item_rows
语句的 returning id
部分?不需要新的订单项,因此如果可以提高性能,我想避免额外的开销。
我应该停止使用查询并将所有这些都移到一个函数中吗?该系统最初使用的是MS SQL数据库,所以我更熟悉使用declare
和多个语句使用变量。
create table invoices(
id serial primary key,
parent_invoice_id int null references invoices(id),
name text not null
);
INSERT INTO invoices(parent_invoice_id, name) VALUES
( NULL, 'One')
,( 1, 'two')
,( NULL, 'three')
;
create table line_items(
id serial primary key,
invoice_id int not null references invoices(id),
amount int not null
);
INSERT INTO line_items (invoice_id, amount) VALUES
(1, 10)
,(1, 11)
,(2, 21)
,(2, 22)
,(3, 33)
;
-- for demonstration purposes: the clone+insert as a prepared statement
-- (this is *not* necessary, only convenient)
PREPARE clone_the_invoice (INTEGER, text, INTEGER) AS
WITH new_invoice_row as (
INSERT into invoices (parent_invoice_id, name)
VALUES ( /*invoice_to_clone_id*/, /*name */ )
RETURNING id)
, new_line_item_rows as (
INSERT into line_items (invoice_id, amount)
SELECT new_invoice_row.id, /* amount */
FROM new_invoice_row
RETURNING id
)
SELECT * FROM new_line_item_rows
;
-- call the prepared statement.
-- This will clone invoice#2,
-- and insert one row in items, referring to the cloned row
-- it returns the new item's id, which is sufficient to
-- find the invoice.id too, when needed.
-- -----------------------------------------------------------------
EXECUTE clone_the_invoice (2, 'four', 123);
-- Chek the result
SELECT
iv.id
, iv.parent_invoice_id
, iv.name
, li.id AS lineid
, li.amount
FROM invoices iv
JOIN line_items li ON li.invoice_id = iv.id
;
结果:
CREATE TABLE
INSERT 0 3
CREATE TABLE
INSERT 0 5
PREPARE
id
----
6
(1 row)
id | parent_invoice_id | name | lineid | amount
----+-------------------+-------+--------+--------
1 | | One | 1 | 10
1 | | One | 2 | 11
2 | 1 | two | 3 | 21
2 | 1 | two | 4 | 22
3 | | three | 5 | 33
4 | 2 | four | 6 | 123
(6 rows)
对于非平凡的情况,FK 将需要一个支持索引(这不会自动添加,因此您应该手动添加)
CREATE INDEX ON invoices (parent_invoice_id);
CREATE INDEX ON line_items (invoice_id);
更新:如果您坚持要退回新的发票,就这样吧:
PREPARE clone_the_invoice2 (INTEGER, text, integer) AS
WITH new_invoice_row as (
INSERT into invoices (parent_invoice_id, name)
VALUES ( /*invoice_to_clone_id*/, )
RETURNING *
)
, new_line_item_rows as (
INSERT into line_items (invoice_id, amount)
SELECT new_invoice_row.id,
FROM new_invoice_row
RETURNING *
)
SELECT iv.*
FROM new_invoice_row iv
JOIN new_line_item_rows new ON new.invoice_id = iv.id
;
更新 2(OP 似乎也希望克隆 详细信息行 :
-- Clone an invoice
-- INCLUDING all associated line_items
-- --------------------------------------
PREPARE clone_the_invoice3 (INTEGER, text) AS
WITH new_invoice_row as (
INSERT into invoices (parent_invoice_id, name)
VALUES ( /*invoice_to_clone_id*/
, /* name */
)
RETURNING *
)
, new_line_item_rows as (
INSERT into line_items (invoice_id, amount)
SELECT cl.id -- the cloned invoice
, it.amount
FROM line_items it
CROSS JOIN new_invoice_row cl
WHERE it.invoice_id = -- The original invoice
RETURNING *
)
SELECT iv.*
FROM new_invoice_row iv
JOIN new_line_item_rows new ON new.invoice_id = iv.id
;
EXECUTE clone_the_invoice3 (2, 'four');
第一个查询可以return只有id
和parent_invoice_id
。
使用第二个值以避免重写参数(作为防止打字错误的保护)。
交叉连接是必要的和正确的。
您可以在第二个查询中跳过 returning *
。
函数不是必需的,虽然它可能使用起来很方便。
with new_invoice_row as (
insert into invoices (parent_invoice_id, name)
values (12345, 'Hello World')
returning id, parent_invoice_id
),
new_line_item_rows as (
insert into line_items (invoice_id, amount)
select
new_invoice_row.id, line_items.amount
from line_items
cross join new_invoice_row
where
line_items.invoice_id = new_invoice_row.parent_invoice_id
)
select * from new_invoice_row;
假设我有两个表实现了一个非常简单的发票系统(注意:架构无法更改):
create table invoices(
id serial primary key,
parent_invoice_id int null references invoices(id),
name text not null
);
create table line_items(
id serial primary key,
invoice_id int not null references invoices(id),
amount int not null
);
用户可以"clone" 发票并参考原始"parent" 发票。在系统中,克隆后直接需要invoice(但不需要line_items
)。因此,克隆发票后,必须返回新的发票。这是我用来克隆发票的 SQL:
with new_invoice_row as (
insert into invoices (parent_invoice_id, name)
values (12345/*invoice_to_clone_id*/, 'Hello World')
returning *
),
new_line_item_rows as (
insert into line_items (invoice_id, amount)
select
new_invoice_row.id, line_item.amount
from line_items
cross join new_invoice_row
where
line_item.invoice_id = 12345/*invoice_to_clone_id*/
returning id
)
select * from new_invoice_row;
问题:
cross join
的表现会好吗?我正在考虑能够只删除cross join
以减少必须进行连接,但它不会 运行(错误:missing FROM-clause entry for table "new_invoice_row"
):... insert into line_items (invoice_id, amount) select new_invoice_row.id, line_item.amount from line_items where line_item.invoice_id = 12345 returning id ...
是否可以删除
new_line_item_rows
语句的returning id
部分?不需要新的订单项,因此如果可以提高性能,我想避免额外的开销。我应该停止使用查询并将所有这些都移到一个函数中吗?该系统最初使用的是MS SQL数据库,所以我更熟悉使用
declare
和多个语句使用变量。
create table invoices(
id serial primary key,
parent_invoice_id int null references invoices(id),
name text not null
);
INSERT INTO invoices(parent_invoice_id, name) VALUES
( NULL, 'One')
,( 1, 'two')
,( NULL, 'three')
;
create table line_items(
id serial primary key,
invoice_id int not null references invoices(id),
amount int not null
);
INSERT INTO line_items (invoice_id, amount) VALUES
(1, 10)
,(1, 11)
,(2, 21)
,(2, 22)
,(3, 33)
;
-- for demonstration purposes: the clone+insert as a prepared statement
-- (this is *not* necessary, only convenient)
PREPARE clone_the_invoice (INTEGER, text, INTEGER) AS
WITH new_invoice_row as (
INSERT into invoices (parent_invoice_id, name)
VALUES ( /*invoice_to_clone_id*/, /*name */ )
RETURNING id)
, new_line_item_rows as (
INSERT into line_items (invoice_id, amount)
SELECT new_invoice_row.id, /* amount */
FROM new_invoice_row
RETURNING id
)
SELECT * FROM new_line_item_rows
;
-- call the prepared statement.
-- This will clone invoice#2,
-- and insert one row in items, referring to the cloned row
-- it returns the new item's id, which is sufficient to
-- find the invoice.id too, when needed.
-- -----------------------------------------------------------------
EXECUTE clone_the_invoice (2, 'four', 123);
-- Chek the result
SELECT
iv.id
, iv.parent_invoice_id
, iv.name
, li.id AS lineid
, li.amount
FROM invoices iv
JOIN line_items li ON li.invoice_id = iv.id
;
结果:
CREATE TABLE
INSERT 0 3
CREATE TABLE
INSERT 0 5
PREPARE
id
----
6
(1 row)
id | parent_invoice_id | name | lineid | amount
----+-------------------+-------+--------+--------
1 | | One | 1 | 10
1 | | One | 2 | 11
2 | 1 | two | 3 | 21
2 | 1 | two | 4 | 22
3 | | three | 5 | 33
4 | 2 | four | 6 | 123
(6 rows)
对于非平凡的情况,FK 将需要一个支持索引(这不会自动添加,因此您应该手动添加)
CREATE INDEX ON invoices (parent_invoice_id);
CREATE INDEX ON line_items (invoice_id);
更新:如果您坚持要退回新的发票,就这样吧:
PREPARE clone_the_invoice2 (INTEGER, text, integer) AS
WITH new_invoice_row as (
INSERT into invoices (parent_invoice_id, name)
VALUES ( /*invoice_to_clone_id*/, )
RETURNING *
)
, new_line_item_rows as (
INSERT into line_items (invoice_id, amount)
SELECT new_invoice_row.id,
FROM new_invoice_row
RETURNING *
)
SELECT iv.*
FROM new_invoice_row iv
JOIN new_line_item_rows new ON new.invoice_id = iv.id
;
更新 2(OP 似乎也希望克隆 详细信息行 :
-- Clone an invoice
-- INCLUDING all associated line_items
-- --------------------------------------
PREPARE clone_the_invoice3 (INTEGER, text) AS
WITH new_invoice_row as (
INSERT into invoices (parent_invoice_id, name)
VALUES ( /*invoice_to_clone_id*/
, /* name */
)
RETURNING *
)
, new_line_item_rows as (
INSERT into line_items (invoice_id, amount)
SELECT cl.id -- the cloned invoice
, it.amount
FROM line_items it
CROSS JOIN new_invoice_row cl
WHERE it.invoice_id = -- The original invoice
RETURNING *
)
SELECT iv.*
FROM new_invoice_row iv
JOIN new_line_item_rows new ON new.invoice_id = iv.id
;
EXECUTE clone_the_invoice3 (2, 'four');
第一个查询可以return只有id
和parent_invoice_id
。
使用第二个值以避免重写参数(作为防止打字错误的保护)。
交叉连接是必要的和正确的。
您可以在第二个查询中跳过 returning *
。
函数不是必需的,虽然它可能使用起来很方便。
with new_invoice_row as (
insert into invoices (parent_invoice_id, name)
values (12345, 'Hello World')
returning id, parent_invoice_id
),
new_line_item_rows as (
insert into line_items (invoice_id, amount)
select
new_invoice_row.id, line_items.amount
from line_items
cross join new_invoice_row
where
line_items.invoice_id = new_invoice_row.parent_invoice_id
)
select * from new_invoice_row;