PostgresSQL - Как вставить в основную таблицу и таблицу подробностей в одной хранимой процедуре? - PullRequest
0 голосов
/ 12 октября 2018

, поэтому я работаю над проектом, который требует, чтобы мой запрос был вставлен в одну основную таблицу и его таблицу подробностей (которая будет отправлена ​​в БД в виде списка) в одной транзакции, чтобы он откатывался, если одиниз функций вставки не удалось.

Допустим, у меня есть эти таблицы:

CREATE TABLE transaction(
  id             BIGSERIAL PRIMARY KEY NOT NULL,
  user_id        BIGINT    FOREIGN KEY NOT NULL,
  total_item     INT                   NOT NULL DEFAULT 0,
  total_purchase BIGINT                NOT NULL DEFAULT 0
)

CREATE TABLE transaction_detail(
  id              BIGSERIAL PRIMARY KEY NOT NULL,
  transaction_id  BIGINT    FOREIGN KEY NOT NULL,
  product_id      BIGINT    FOREIGN KEY NOT NULL,
  product_price   INT                   NOT NULL DEFAULT 0,
  purchase_amount INT                   NOT NULL DEFAULT 0
)

И у меня есть эта функция:

CREATE OR REPLACE FUNCTION create_transaction(order JSONB, product_list JSONB)

Параметры функции:

  • order : An object which will be inserted into the transaction table
  • product_list : List of Product object which will be inserted into the transaction_detail table

Мой текущий запрос выглядит примерно так:

CREATE OR REPLACE FUNCTION insert_order(tx JSONB, product_list JSONB)
    RETURNS BIGINT
AS $$
WITH result AS (
    INSERT INTO transaction(
        user_id,
        total_item,
        total_purchase,
    ) VALUES (
        (tx ->> 'user_id') :: BIGINT,
        (tx ->> 'total_item') :: INT,
        (tx ->> 'total_purchase') :: INT,
    )
    RETURNING id AS transaction_id 
)
FOR row IN product_list LOOP
    INSERT INTO transaction_detail(
        transaction_id,
        product_id,
        product_price,
        purchase_amount,
    ) VALUES (
        transaction_id,
        (row ->> 'product_id') :: BIGINT,
        (row ->> 'product_price') :: INT,
        (row ->> 'purchase_amount') :: INT,
    )
END LOOP;
$$ LANGUAGE SQL SECURITY DEFINER;

Файлы JSON:

  • tx.json

[ "user_id" : "1", "total_item" : "2", "total_purchase" : "2000", ]

  • product_list.json

[ { "product_id" : "1", "product_price" : "500", "purchase_amount" : "2" }, { "product_id" : "2", "product_price" : "1000", "purchase_amount" : "1" } ]

Я знаю, что с моим запросом что-то не так, хотя я не могу понять, что с ним.Любой указатель высоко ценится.

1 Ответ

0 голосов
/ 12 октября 2018

Предполагая, что данные, переданные как product_list, являются массивом, вы можете сделать что-то вроде этого:

CREATE OR REPLACE FUNCTION insert_order(p_order JSONB, p_product_list JSONB)
    RETURNS BIGINT
AS $$
  WITH result AS (
      INSERT INTO "transaction"(
          user_id,
          total_item,
          total_purchase
      ) VALUES (
          (p_order ->> 'user_id') :: BIGINT,
          (p_order ->> 'total_item') :: INT,
          (p_order ->> 'total_purchase') :: INT
      )
      RETURNING id AS transaction_id 
  ), details as (
    INSERT INTO transaction_detail(
        transaction_id,
        product_id,
        product_price,
        purchase_amount
    ) 
    select r.transaction_id, 
           (pl.data ->> 'product_id')::bigint,
           (pl.data ->> 'product_price')::int,
           (pl.data ->> 'purchase_amount')::int
    from result r,
      jsonb_array_elements(p_product_list) as pl(data)
  )
  select transaction_id 
  from result;
$$ 
LANGUAGE SQL SECURITY DEFINER;

Я переименовал параметры, чтобы избежать конфликтов имен с зарезервированными ключевыми словами.Приставляя имена параметров, вы также избегаете конфликтов имен с именами столбцов или таблиц.order является зарезервированным ключевым словом и может использоваться только в кавычках, например, "order".transaction является ключевым словом, однако оно не зарезервировано, но, тем не менее, его лучше заключить в кавычки.

Для вставки в детали транзакции необходимо выбрать INSERT...SELECT из result, чтобы получить сгенерированный идентификатор транзакции.и путем удаления элементов массива в списке значений JSON.

Окончательный выбор CTE затем возвращает сгенерированный идентификатор транзакции.

Вы можете вызвать функцию следующим образом:

select insert_order('{"user_id": 42, "total_item": 1, "total_purchase": 100}'::jsonb, 
                    '[ {"product_id": 1, "product_price": 10, "purchase_amount": 1}, 
                       {"product_id": 2, "product_price": 20, "purchase_amount": 2}, 
                       {"product_id": 3, "product_price": 30, "purchase_amount": 3} ]'::jsonb);
...