Postgres 트리거

오늘 이 블로그에서는 Postgres 트리거에 대해 이야기하고 싶습니다. 나는 또한 그것을 사용하는 방법을 배우기 위해 예제 사용 사례를 살펴볼 것입니다.

시나리오



책을 판매하는 전자 상거래 앱이 있고 데이터베이스에서 장바구니 기능을 구현하고 있습니다. 따라서 사용자당 고유한 카트 항목을 저장하는 카트 테이블과 책 테이블이 있습니다. 장바구니 항목의 경우 carts_books 다른 테이블이 있습니다. cartscarts_books는 다대다 관계를 사용하고 있습니다. 따라서 카트에는 많은 책이 있을 수 있고 책은 많은 카트에서 사용할 수 있습니다.
carts_books 테이블에 행이 삽입되거나 업데이트될 때마다 사용한 카트의 총 가격을 계산하고 싶습니다. multiplier 테이블의 carts_books 열도 고려하고 싶습니다. 이 multiplier 열은 단일 책의 수량입니다. 즉, 고객이 책을 여러 부 구매하도록 허용하고 있습니다.

Postgres 트리거를 사용하여 이 문제를 해결해 보겠습니다. 그 전에 아래는 지금까지 논의된 모든 테이블의 열 목록입니다.

스키마




                               Table "public.carts"
   Column    |           Type           | Collation | Nullable |      Default
-------------+--------------------------+-----------+----------+--------------------
 created_at  | timestamp with time zone |           | not null | now()
 updated_at  | timestamp with time zone |           | not null | now()
 archived_at | timestamp with time zone |           |          |
 amount      | numeric                  |           | not null |
 status      | text                     |           | not null | 'ENQUEUED'::text
 user_id     | text                     |           | not null |
 id          | uuid                     |           | not null | gen_random_uuid()


                                     Table "public.books"
   Column    |           Type           | Collation | Nullable |              Default
-------------+--------------------------+-----------+----------+-----------------------------------
 created_at  | timestamp with time zone |           | not null | now()
 updated_at  | timestamp with time zone |           | not null | now()
 archived_at | timestamp with time zone |           |          |
 name        | text                     |           | not null |
 price       | numeric                  |           | not null |
 user_id     | bigint                   |           | not null |
 id          | bigint                   |           | not null | nextval('books_id_seq'::regclass)
 description | text                     |           | not null |

              Table "public.carts_books"
   Column   |  Type   | Collation | Nullable | Default
------------+---------+-----------+----------+---------
 book_id    | bigint  |           | not null |
 multiplier | integer |           | not null |
 user_id    | text    |           | not null |
 cart_id    | uuid    |           | not null |


해결책



테이블carts_books의 행이 INSERTED 또는 UPDATED된 후 프로시저(함수라고도 함)를 실행하려고 합니다.

이를 위해 두 개의 Postgres 트리거를 생성해 보겠습니다. 하나는 UPDATE용이고 다른 하나는 INSERT용입니다.

CREATE TRIGGER update_cart_price
  AFTER UPDATE ON public.carts_books
  FOR EACH ROW
  EXECUTE PROCEDURE public.update_cart_price ();

COMMENT ON TRIGGER update_cart_price ON public.carts_books IS 
'update the price of the related cart based on the updated cart item(s) in relation carts_books';

CREATE TRIGGER calculate_cart_price
  AFTER INSERT ON public.carts_books
  FOR EACH ROW
  EXECUTE PROCEDURE public.update_cart_price ();

COMMENT ON TRIGGER calculate_cart_price ON public.carts_books IS 
'update the price of the related cart based on the updated cart item(s) in relation carts_books';


위의 코드는 두 개의 Postgres 트리거를 생성하는 코드입니다. 구문과 위에서 사용한 각 절이 의미하는 바를 이해합시다.

Postgres 트리거를 생성하기 위해 Postgres에 다음 사항을 알리고 싶습니다.
  • 트리거의 이름입니다. 이 경우 이름update_cart_pricecalculate_cart_price .
  • 언제 코드를 실행하시겠습니까? 삽입된 값을 알고 싶습니다.
    이를 위해 AFTER INSERTAFTER UPDATE 절을 사용해야 합니다.
  • 이 트리거를 어떻게 실행하시겠습니까? 두 가지 옵션이 있습니다. 하나는 영향을 받는 각 행에 대해 이 트리거를 실행하는 것입니다. 또 다른 옵션은 명령문마다 트리거를 실행하는 것입니다.
  • 무엇을 실행하고 싶습니까? update_cart_price() 라는 프로시저를 실행하는 이러한 트리거가 있습니다.

  • You must create the function before using it in a function.
    A function must return TRIGGER in order to be used in a trigger.



    이 트리거에서 실행하려는 함수는 다음과 같이 정의됩니다.
    `sql

    CREATE OR REPLACE FUNCTION public.update_cart_price ()
    RETURNS TRIGGER
    AS $$
    DECLARE
    item record;
    new_amount numeric := 0;
    BEGIN
    FOR item IN
    SELECT
    price,
    multiplier
    FROM
    carts_books
    JOIN books ON book_id = books.id
    WHERE
    cart_id = NEW.cart_id LOOP
    new_amount := new_amount + (item.price * item.multiplier);
    END LOOP;
    UPDATE
    carts
    SET
    amount = new_amount
    WHERE
    id = NEW.cart_id;
    RETURN new;
    END;
    $$
    LANGUAGE plpgsql;

    `

    This function selects the price and multiplier of all the books used inside the cart. It then updates the cart's amount column to reflect the updated amount.

    The data of the changed row is made available through the new 기록.

    또한 함수는 일반SQL 언어를 사용할 수 없으므로 절차적
    언어.

    결론



    Postgres 트리거를 사용하면 데이터 변경에 대해 소규모 비즈니스 로직을 쉽게 실행할 수 있습니다.

    또한 다른 서비스(예: nodejs 앱)에서 동일한 논리를 실행할 경우 직면하게 되는 네트워크 대기 시간에 대해 걱정할 필요가 없습니다.

    굿리즈



    이 게시물 외에도 Postgres에 대해 자세히 알아보려면 이 게시물을 읽는 것이 좋습니다.
    방아쇠.
  • Official Docs
  • Data visibility in trigger functions
  • 좋은 웹페이지 즐겨찾기