微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用 postgres 通过一个查询将数据插入到多个表中

如何解决使用 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","CreatedBy",userid)
                           VALUES(@Description,@PlanCustomerId,@IsActive,@IsDeleted,user_id,@CreatedDate,user_id) RETURNING id as client_id;
   ),clientsubins
   (
    INSERT INTO public.client_subscription(
    "PlanId","StartDate","EndDate",clientid,subscriptionid)
    VALUES (@PlanId,@StartDate,@EndDate,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"",""CreatedBy"",""CreatedDate"",UserId) 
                        VALUES(@Description,@PlanCustomerId,@IsActive,(SELECT user_id from userins),@CreatedDate,(SELECT user_id from userins)) RETURNING id as client_id),clientsubins AS
                (
                   INSERT INTO public.client_subscription(""PlanId"",""StartDate"",""EndDate"",ClientId,SubscriptionId)
                   VALUES(@PaymentMethodPlanId,@StartDate,@EndDate,(SELECT client_id from clientins),@PlanId)
                ),clientpurchaseins AS
                (
                   INSERT INTO public.client_purchase_history(""PlanId"",""InvoiceId"",ClientId)
                   VALUES(@PlanId,@InvoiceId,(SELECT client_id FROM clientins))
                )
                SELECT client_id from clientins;
        COMMIT;

如果发生任何错误,这里是 Begin 块将处理您的事务,然后它会自动回滚。

,

最重要的是,您需要从早期的 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,@Address,@City,@State,@Zip,@CreatedDate)  --  no ;
   RETURNING id AS userid
   ),clientins AS (
   INSERT INTO public.client
         ("Description","PlanCustomerId","IsActive","CreatedBy","CreatedDate",userid)
   SELECT @Description,userid,userid
   FROM   userins
   RETURNING id AS clientid,userid  -- no ;
   )
INSERT INTO public.client_subscription
      ("PlanId","StartDate","EndDate",clientid,subscriptionid)
SELECT @PlanId,@subscriptionid
FROM   clientins
RETURNING clientid;

您还有几个语法错误。我统一了clientid等的拼写

每个 CTE(和外层 INSERT)都依赖于前一个 CTE 的结果行。这样,如果第一个 INSERT 没有插入(并返回)任何内容,则不会插入任何内容。

由于这是一个单个语句,它会自动在单个事务中运行。任何错误都会取消整个操作。

即使在过时的 Postgres 9.3 中也能工作。

见:

,

你收到的答案很好,但我会给出我的建议。由于您正在处理一个事务块,因此最好将其包含为函数或存储过程的一部分。例如,我更喜欢在变量中分配返回 id,这样我就可以在整个执行过程中使用它。如果一切正常,我提交并返回 user_id 或者如果需要我也可以将返回类型更改为布尔值。如果有异常,我捕获并返回-1,表示不成功,这里还可以执行其他选项,比如在表中注册异常的原因。如果一张表失败,则全部回滚:

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行,在没有异常时测试。 注意:由于某些原因,小提琴中的 $$ 应该用引号替换,因此里面的单引号是双引号。

有关 exception handling 的更多信息(40.6.6. 捕获错误)。

,

如果您能说明为什么要在一个事务中插入多个表的总体意图会有所帮助。

我不想听起来很无聊,但是为了充分利用某物并有效地使用它,您应该首先对所使用的组件有一个合理到高级的理解。可悲的是,在当今世界,很多人只是开始“做事”,然后不得不寻求帮助,因为他们使用的技术没有做他们想做的事,但没有对其进行配置。我不能也不会判断这里是否是这种情况。

无论如何。如果您想确保数据更改是否应用于所有相关表,对我来说第一件显而易见的事情就是查看 dapper transactions。这是否进行原子事务(因此应用所有更改或不应用)?我不知道,但愿如此。

您可以像其他发帖人所建议的那样,将 DML(数据操作语言)语句包装在“事务块”中,如果发生错误,它将回滚事务块中的所有更改,或者将其包装在一个函数中,并自行处理错误。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。