我们说我有两张表实现了一个非常简单的发票系统(注意:架构无法更改):
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
);
用户有能力克隆"发票并将其引用原始"父母"发票。在系统中,克隆后直接需要发票(但不需要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
...
是否可以删除returning id
语句的new_line_item_rows
部分?我们不需要新的订单项,因此如果可以提高性能,我希望避免额外的开销。
我应该停止使用查询并将所有这些移动到一个函数中吗?系统最初使用的是MS SQL数据库,因此我更熟悉使用declare
并且有多个语句使用该变量。
答案 0 :(得分:1)
第一个查询只能返回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;
答案 1 :(得分:0)
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 ( $1 /*invoice_to_clone_id*/, $2 /*name */ )
RETURNING id)
, new_line_item_rows as (
INSERT into line_items (invoice_id, amount)
SELECT new_invoice_row.id, $3 /* 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 ( $1 /*invoice_to_clone_id*/, $2 )
RETURNING *
)
, new_line_item_rows as (
INSERT into line_items (invoice_id, amount)
SELECT new_invoice_row.id, $3
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 ( $1 /*invoice_to_clone_id*/
, $2 /* 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 = $1 -- 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');