使用postgres通过一次查询将数据插入到多个表中
Data insertion into multiple tables with one query using postgres
我想一次性将数据插入到多个相关表中 - 在单个事务中。我正在使用 Postgres 和 Dapper ORM。这是我的查询:
WITH userins AS (
INSERT INTO public."user"
(
"FirstName",
"LastName",
"Email",
"PasswordHash",
"Address",
"City",
"State",
"Zip",
"Phone",
"IsEnabled",
"IsVerified",
"IsDeleted",
"CreatedDate",
)
VALUES (@FirstName, @LastName, @Email, @PasswordHash, @Address, @City, @State, @Zip, @Phone, @IsEnabled, ?IsVerified, ?IsDeleted, @CreatedDate);
RETURNING id AS user_id
)
, clientins AS (
INSERT INTO public.client( "Description", "PlanCustomerId", "IsActive", "IsDeleted", "CreatedBy", "CreatedDate", userid)
VALUES(@Description, @PlanCustomerId, @IsActive,@IsDeleted, user_id, @CreatedDate,user_id) RETURNING id as client_id;
)
, clientsubins
(
INSERT INTO public.client_subscription(
"PlanId",
"IsActive",
"StartDate",
"EndDate",
"IsDeleted",
"CreatedBy",
"CreatedDate",
clientid,
subscriptionid)
VALUES (@PlanId, @IsActive, @StartDate, @EndDate, @IsDeleted, user_id, @CreatedDate, client_id, @subscriptionid);
)
RETURNING client_id
此查询是否有效或我需要更改哪些内容?
postgreSql 对此有不同的语法。几天前我也被困在这个问题上想知道如何处理它。您可以使用 BEGIN 块将数据插入到多个表中,请按照此查询完成您的工作。
BEGIN;
WITH userins AS (
INSERT INTO public.user(
""FirstName"", ""LastName"", ""Email"", ""PasswordHash"",""VerificationCode"", ""Phone"", ""IsEnabled"", ""IsVerified"", ""IsDeleted"", ""CreatedDate"")VALUES(@FirstName, @LastName, @Email, @PasswordHash, @VerificationCode, @Phone, @IsEnabled, @IsVerified, @IsDeleted, @CreatedDate)
RETURNING id AS user_id)
,clientins AS(
INSERT INTO public.client(""Description"", ""PlanCustomerId"", ""IsActive"", ""IsDeleted"", ""CreatedBy"", ""CreatedDate"", UserId)
VALUES(@Description, @PlanCustomerId, @IsActive, @IsDeleted, (SELECT user_id from userins), @CreatedDate, (SELECT user_id from userins)) RETURNING id as client_id)
,clientsubins AS
(
INSERT INTO public.client_subscription(""PlanId"", ""IsActive"",""StartDate"", ""EndDate"", ""IsDeleted"", ""CreatedBy"", ""CreatedDate"", ClientId, SubscriptionId)
VALUES(@PaymentMethodPlanId, @IsActive, @StartDate, @EndDate, @IsDeleted, (SELECT user_id from userins), @CreatedDate, (SELECT client_id from clientins), @PlanId)
)
,clientpurchaseins AS
(
INSERT INTO public.client_purchase_history(""PlanId"", ""InvoiceId"",""StartDate"", ""EndDate"", ""IsDeleted"", ""CreatedBy"", ""CreatedDate"", ClientId)
VALUES(@PlanId, @InvoiceId, @StartDate, @EndDate, @IsDeleted, (SELECT user_id from userins), @CreatedDate, (SELECT client_id FROM clientins))
)
SELECT client_id from clientins;
COMMIT;
这是开始块将处理您的事务,如果发生任何错误,它将自动回滚。
最重要的是,您需要从早期的 CTE SELECT
获得 RETURNING
子句产生的值:
WITH userins AS (
INSERT INTO public."user"
("FirstName", "LastName", "Email", "PasswordHash", "Address", "City", "State", "Zip", "Phone", "IsEnabled", "IsVerified", "IsDeleted", "CreatedDate") -- no dangling ,
VALUES (@FirstName , @LastName , @Email , @PasswordHash , @Address , @City , @State , @Zip , @Phone , @IsEnabled , @IsVerified , @IsDeleted , @CreatedDate) -- no ;
RETURNING id AS userid
)
, clientins AS (
INSERT INTO public.client
("Description", "PlanCustomerId", "IsActive", "IsDeleted", "CreatedBy", "CreatedDate", userid)
SELECT @Description , @PlanCustomerId , @IsActive , @IsDeleted , userid , @CreatedDate , userid
FROM userins
RETURNING id AS clientid, userid -- no ;
)
INSERT INTO public.client_subscription
("PlanId", "IsActive","StartDate", "EndDate", "IsDeleted", "CreatedBy", "CreatedDate", clientid , subscriptionid)
SELECT @PlanId , @IsActive , @StartDate, @EndDate , @IsDeleted , userid , @CreatedDate , clientid, @subscriptionid
FROM clientins
RETURNING clientid;
您还有一些语法错误。而且我统一拼写了clientid
等
每个 CTE(和外部 INSERT
)取决于前一个 CTE 的结果行。这样,如果第一个 INSERT
不插入(和 return)任何内容,则不会插入任何内容。
由于这是一个单 语句,它会自动在单个事务中运行。任何错误都会取消整个操作。
甚至可以在您过时的 Postgres 9.3 中使用。
参见:
- Insert data in 3 tables at a time using Postgres
您收到的答案很好,但是我会给出我的建议。由于您正在处理一个事务块,因此最好将其作为函数或存储过程的一部分包含在内。例如,我更喜欢在变量中分配 returning id,这样我就可以在整个执行过程中使用它。如果一切正常,我提交 return user_id 或者我也可以根据需要将 return 类型更改为布尔值。如果有异常,我catch了,return-1,说明没有成功,这里还可以进行其他的选择,比如在table中注册异常的原因。如果一个 table 失败,则全部回滚:
CREATE OR REPLACE FUNCTION test_function(p_name CHAR(50), p_other_params CHAR(50))
RETURNS INT AS
$$
DECLARE
new_user_id INT;
new_purchase_id INT;
BEGIN
INSERT INTO public.user(name)
VALUES(p_name)
RETURNING public.user.id INTO new_user_id;
INSERT INTO public.purchase(user_id)
VALUES(new_user_id)
RETURNING public.purchase.id INTO new_purchase_id;
INSERT INTO public.purchase_detail(purchase_id)
VALUES(new_purchase_id);
RAISE EXCEPTION 'Exception when inserting user: %', p_name; --remove to test when there is no issue
--commited if reaches this point
RETURN new_user_id;
EXCEPTION WHEN OTHERS THEN
--ROLLED back if exception
RETURN -1; -- this value indicates the app it failed
END;
$$
LANGUAGE plpgsql;
select * from test_function('SomeName', 'OtherParameter');
select id, name from public.user;
select id, user_id from public.purchase;
select id, purchase_id from public.purchase_detail;
这是架构:
CREATE TABLE public.user( name CHAR(50));
CREATE TABLE public.purchase( user_id int);
CREATE TABLE public.purchase_detail( purchase_id INT);
ALTER TABLE public.user ADD COLUMN id SERIAL PRIMARY KEY;
ALTER TABLE public.purchase ADD COLUMN id SERIAL PRIMARY KEY;
ALTER TABLE public.purchase_detail ADD COLUMN id SERIAL PRIMARY KEY;
勾选this fiddle in action,,没有异常时记得去掉RAISE Exception行测试。
注意:由于某些原因,在 fiddle 中 $$ 应该用引号代替,因此里面的单引号是双引号。
更多关于 exception handling(40.6.6. 陷阱错误)。
如果您能说明为什么要在一个事务中插入多个表的总体意图,将会有所帮助。
我不想听起来很无聊,但要充分利用某些东西并有效地使用它,您应该首先对正在运行的组件有一个合理到高级的理解。可悲的是,在当今世界,很多人只是离开 'doing stuff' 然后不得不寻求帮助,因为他们使用的技术没有按照他们的意愿行事,但没有对其进行配置。我不能也不会判断这里是否是这种情况。
总之。如果您想确保数据更改是否应用于所有相关表,对我来说第一个显而易见的事情就是查看 dapper transactions。这是否执行原子事务(因此应用所有更改或 none)?我不知道,但愿如此。
你可以像其他发帖人建议的那样,将 DML(数据操作语言)语句包装在 'transaction block' 中,如果发生错误,它将回滚事务块中的所有更改,或者将其包装在一个函数中,自己处理错误。
我想一次性将数据插入到多个相关表中 - 在单个事务中。我正在使用 Postgres 和 Dapper ORM。这是我的查询:
WITH userins AS (
INSERT INTO public."user"
(
"FirstName",
"LastName",
"Email",
"PasswordHash",
"Address",
"City",
"State",
"Zip",
"Phone",
"IsEnabled",
"IsVerified",
"IsDeleted",
"CreatedDate",
)
VALUES (@FirstName, @LastName, @Email, @PasswordHash, @Address, @City, @State, @Zip, @Phone, @IsEnabled, ?IsVerified, ?IsDeleted, @CreatedDate);
RETURNING id AS user_id
)
, clientins AS (
INSERT INTO public.client( "Description", "PlanCustomerId", "IsActive", "IsDeleted", "CreatedBy", "CreatedDate", userid)
VALUES(@Description, @PlanCustomerId, @IsActive,@IsDeleted, user_id, @CreatedDate,user_id) RETURNING id as client_id;
)
, clientsubins
(
INSERT INTO public.client_subscription(
"PlanId",
"IsActive",
"StartDate",
"EndDate",
"IsDeleted",
"CreatedBy",
"CreatedDate",
clientid,
subscriptionid)
VALUES (@PlanId, @IsActive, @StartDate, @EndDate, @IsDeleted, user_id, @CreatedDate, client_id, @subscriptionid);
)
RETURNING client_id
此查询是否有效或我需要更改哪些内容?
postgreSql 对此有不同的语法。几天前我也被困在这个问题上想知道如何处理它。您可以使用 BEGIN 块将数据插入到多个表中,请按照此查询完成您的工作。
BEGIN;
WITH userins AS (
INSERT INTO public.user(
""FirstName"", ""LastName"", ""Email"", ""PasswordHash"",""VerificationCode"", ""Phone"", ""IsEnabled"", ""IsVerified"", ""IsDeleted"", ""CreatedDate"")VALUES(@FirstName, @LastName, @Email, @PasswordHash, @VerificationCode, @Phone, @IsEnabled, @IsVerified, @IsDeleted, @CreatedDate)
RETURNING id AS user_id)
,clientins AS(
INSERT INTO public.client(""Description"", ""PlanCustomerId"", ""IsActive"", ""IsDeleted"", ""CreatedBy"", ""CreatedDate"", UserId)
VALUES(@Description, @PlanCustomerId, @IsActive, @IsDeleted, (SELECT user_id from userins), @CreatedDate, (SELECT user_id from userins)) RETURNING id as client_id)
,clientsubins AS
(
INSERT INTO public.client_subscription(""PlanId"", ""IsActive"",""StartDate"", ""EndDate"", ""IsDeleted"", ""CreatedBy"", ""CreatedDate"", ClientId, SubscriptionId)
VALUES(@PaymentMethodPlanId, @IsActive, @StartDate, @EndDate, @IsDeleted, (SELECT user_id from userins), @CreatedDate, (SELECT client_id from clientins), @PlanId)
)
,clientpurchaseins AS
(
INSERT INTO public.client_purchase_history(""PlanId"", ""InvoiceId"",""StartDate"", ""EndDate"", ""IsDeleted"", ""CreatedBy"", ""CreatedDate"", ClientId)
VALUES(@PlanId, @InvoiceId, @StartDate, @EndDate, @IsDeleted, (SELECT user_id from userins), @CreatedDate, (SELECT client_id FROM clientins))
)
SELECT client_id from clientins;
COMMIT;
这是开始块将处理您的事务,如果发生任何错误,它将自动回滚。
最重要的是,您需要从早期的 CTE SELECT
获得 RETURNING
子句产生的值:
WITH userins AS (
INSERT INTO public."user"
("FirstName", "LastName", "Email", "PasswordHash", "Address", "City", "State", "Zip", "Phone", "IsEnabled", "IsVerified", "IsDeleted", "CreatedDate") -- no dangling ,
VALUES (@FirstName , @LastName , @Email , @PasswordHash , @Address , @City , @State , @Zip , @Phone , @IsEnabled , @IsVerified , @IsDeleted , @CreatedDate) -- no ;
RETURNING id AS userid
)
, clientins AS (
INSERT INTO public.client
("Description", "PlanCustomerId", "IsActive", "IsDeleted", "CreatedBy", "CreatedDate", userid)
SELECT @Description , @PlanCustomerId , @IsActive , @IsDeleted , userid , @CreatedDate , userid
FROM userins
RETURNING id AS clientid, userid -- no ;
)
INSERT INTO public.client_subscription
("PlanId", "IsActive","StartDate", "EndDate", "IsDeleted", "CreatedBy", "CreatedDate", clientid , subscriptionid)
SELECT @PlanId , @IsActive , @StartDate, @EndDate , @IsDeleted , userid , @CreatedDate , clientid, @subscriptionid
FROM clientins
RETURNING clientid;
您还有一些语法错误。而且我统一拼写了clientid
等
每个 CTE(和外部 INSERT
)取决于前一个 CTE 的结果行。这样,如果第一个 INSERT
不插入(和 return)任何内容,则不会插入任何内容。
由于这是一个单 语句,它会自动在单个事务中运行。任何错误都会取消整个操作。
甚至可以在您过时的 Postgres 9.3 中使用。
参见:
- Insert data in 3 tables at a time using Postgres
您收到的答案很好,但是我会给出我的建议。由于您正在处理一个事务块,因此最好将其作为函数或存储过程的一部分包含在内。例如,我更喜欢在变量中分配 returning id,这样我就可以在整个执行过程中使用它。如果一切正常,我提交 return user_id 或者我也可以根据需要将 return 类型更改为布尔值。如果有异常,我catch了,return-1,说明没有成功,这里还可以进行其他的选择,比如在table中注册异常的原因。如果一个 table 失败,则全部回滚:
CREATE OR REPLACE FUNCTION test_function(p_name CHAR(50), p_other_params CHAR(50))
RETURNS INT AS
$$
DECLARE
new_user_id INT;
new_purchase_id INT;
BEGIN
INSERT INTO public.user(name)
VALUES(p_name)
RETURNING public.user.id INTO new_user_id;
INSERT INTO public.purchase(user_id)
VALUES(new_user_id)
RETURNING public.purchase.id INTO new_purchase_id;
INSERT INTO public.purchase_detail(purchase_id)
VALUES(new_purchase_id);
RAISE EXCEPTION 'Exception when inserting user: %', p_name; --remove to test when there is no issue
--commited if reaches this point
RETURN new_user_id;
EXCEPTION WHEN OTHERS THEN
--ROLLED back if exception
RETURN -1; -- this value indicates the app it failed
END;
$$
LANGUAGE plpgsql;
select * from test_function('SomeName', 'OtherParameter');
select id, name from public.user;
select id, user_id from public.purchase;
select id, purchase_id from public.purchase_detail;
这是架构:
CREATE TABLE public.user( name CHAR(50));
CREATE TABLE public.purchase( user_id int);
CREATE TABLE public.purchase_detail( purchase_id INT);
ALTER TABLE public.user ADD COLUMN id SERIAL PRIMARY KEY;
ALTER TABLE public.purchase ADD COLUMN id SERIAL PRIMARY KEY;
ALTER TABLE public.purchase_detail ADD COLUMN id SERIAL PRIMARY KEY;
勾选this fiddle in action,,没有异常时记得去掉RAISE Exception行测试。 注意:由于某些原因,在 fiddle 中 $$ 应该用引号代替,因此里面的单引号是双引号。
更多关于 exception handling(40.6.6. 陷阱错误)。
如果您能说明为什么要在一个事务中插入多个表的总体意图,将会有所帮助。
我不想听起来很无聊,但要充分利用某些东西并有效地使用它,您应该首先对正在运行的组件有一个合理到高级的理解。可悲的是,在当今世界,很多人只是离开 'doing stuff' 然后不得不寻求帮助,因为他们使用的技术没有按照他们的意愿行事,但没有对其进行配置。我不能也不会判断这里是否是这种情况。
总之。如果您想确保数据更改是否应用于所有相关表,对我来说第一个显而易见的事情就是查看 dapper transactions。这是否执行原子事务(因此应用所有更改或 none)?我不知道,但愿如此。
你可以像其他发帖人建议的那样,将 DML(数据操作语言)语句包装在 'transaction block' 中,如果发生错误,它将回滚事务块中的所有更改,或者将其包装在一个函数中,自己处理错误。