Arrow
ArrowJuly 31, 2018, 4:57 a.m.

Working with trigger functions in PostgreSQL

In the process of developing one application for accounting for time norms, it became necessary to create a pivot table with a selection of data from the existing one.

PostgreSQL was used as the database.

The source table base_table is simplified as follows:

Table fields have the following types:

  • id - integer, auto inc;
  • date - date;
  • quantity - integer;
  • hours_count - time

The rest of the fields are simply assigned the type text .

Simplifications are introduced so as not to focus on the structure of the table.


The following data must be selected from it into the "new_table" pivot table: order, equipment, product, SUM(quantity), SUM(hours_count) , the following fields must also be present in the pivot table data (integer ) and state (boolean) . In the new table, it is necessary to enter not just values from the order, equipment, product columns, but grouped ones.

For ease of understanding, looking ahead a little, I will give the proposed request code that satisfies the task:

INSERT INTO new _table ( order , equip ment , p roduct , o quantity , time_sum )
SELECT order_number , equipment , p roduct , SUM ( quantity ), SUM ( hours_count )
FROM base _table GROUP BY order , equipment , p roduct
HAVING NOT EXISTS ( SELECT order , equip ment , p roduct FROM new _table WHERE order = order AND equip ment = equipment AND p roduct = p roduct );

Initially, there were thoughts about creating a separate table “new_table” as a summary table and in manual mode in a program using a database using an SQL query to select the necessary data from the main table. But still, the question remained how to be when creating new records in “base_table” after the selection is made, since the data will be entered continuously and they must be constantly updated in “new_table” .

After discussing at the EVILEG website forum possible options for solving the task, its creator proposed a way out of this situation by responding to changes in the data in the main table.

Based on this, it was decided that it was necessary to synchronize the data in the tables so that when creating, editing or deleting records in the “base_table” , the corresponding changes would also occur in the pivot table “new_table” .

Table view “new_table” :

It became clear that for this it is necessary to use trigger functions with the processing of events for creating, editing, and deleting data from the base table.

Let's pass to consideration of process of creation of such function with its description.

The following is the declaration of the trigger function:

CREATE TRIGGER select_to_table
BEFORE INSERT OR UPDATE OR DELETE ON base _table
FOR ROW EXECUTE PROCEDURE process_select_to ();

The first line contains a function declaration, let's call it "select_to_table" .

The second line indicates when performing what actions on the base table the function should be called. The BEFORE keyword means that the function must be called before creating a new record ( INSERT ), editing ( UPDATE ) or deleting an existing record ( DELETE ) in the “base_table” table.

Instead of BEFORE, it is possible to specify AFTER, which means calling the function after the above events occur.

It's worth noting that the official PostgreSQL documentation recommends using BEFORE as the keyword.

The third line specifies FOR ROW , or FOR EACH ROW . The first entry means a function call for one row, and the second - one for each row of the table.

And the last parameter after the PROCEDURE keyword is the name of the called function itself. A function with this name must be declared before the declaration or already exist, otherwise it will raise an error.

Now let's move on to the general view of the function itself.

CREATE OR REPLACE FUNCTION «имя функции»
RETURNS TRIGGER AS
$select_to$
BEGIN
-- тело функции --



RETURN NULL ; -- тип возвращаемого значения
END ;
$select_to$ LANGUAGE plpgsql;

The function must have the above form with the obligatory indication of the return value. The return type is NULL because the trigger does not perform any actions on tables. All work on tables will take place in the body of the function.

The type of function should not raise any special questions. However, anyone interested in a more detailed description can refer to the official documentation.

Now let's move on to writing the body itself. Since we indicated earlier that we want to handle the INSERT , UPDATE , and DELETE events, we need to provide a response to each of them. To determine the type of operation in PostgreSQL, there is a variable TG_OP , which contains the name of the operation being performed.

Based on this, we will write a condition for checking the type of operation, which will have the following form:

IF ( TG_OP = 'DELETE' ) THEN

-- операторы --

ELSEIF ( TG_OP = 'INSERT' ) THEN

-- операторы --

ELSIF ( TG_OP = 'UPDATE' ) THEN

-- операторы --

END IF ;

Now let's write the code to delete the entry:

IF ( TG_OP = 'DELETE' ) THEN

-- Удаление записи из таблицы new _table , которая удовлетворяет условию --
DELETE FROM new _table WHERE order = OLD . order AND product = OLD . product AND equipment = OLD . equipment ;
IF NOT FOUND THEN

RETURN NULL ; -- если строки в new _table не существует --

END IF ;
RETURN OLD ;

I think this query does not need a detailed description, except for one thing - this is the OLD keyword, which makes it possible to find out the old values of records from the “base_table” table, that is, before they are changed. It should also be noted that the return value of the delete records function must always be RETURN OLD .

Let's move on to creating a new entry:

ELSEIF ( TG_OP = 'INSERT' ) THEN

-- Создание новой записи в таблице new _table - -
INSERT INTO new _table ( order , equipme nt , p roduct , quantity , time_sum VALUES ( NEW . order_number , NEW . equipment , NEW . part , NEW . otk , NEW . hours_count );

RETURN NEW ;

The request itself does not need a detailed description either. I will only say that using the word NEW it is possible to get new values of records from the “base_table” table, and also that as a return value when creating or editing records, it is necessary to specify NEW as a return value.

Now it's the turn to update the record:

ELSIF ( TG_OP = 'UPDATE' ) THEN

-- Обновление записи в new _table --
UPDATE new _table SET order = NEW . order , equip ment = NEW . equipment , p roduct = NEW . p roduct , quantity = NEW . quantity , time_sum = NEW . hours_count

WHERE order = OLD . order AND equip ment = OLD . equipment AND p roduct = OLD . p roduct AND quantity = OLD . quantity AND time_sum = OLD . hours_count ;
RETURN NEW ;

For this request, everything should be clear from the previous descriptions. Now it's time to look at the code in its entirety.

CREATE OR REPLACE FUNCTION «имя функции»
RETURNS TRIGGER AS
$select_to$
BEGIN
IF ( TG_OP = 'DELETE' ) THEN

-- Удаление записи из new _table , которая удовлетворяет условию
DELETE FROM new _table WHERE order = OLD . order AND product = OLD . product AND equipment = OLD . equipment ;
IF NOT FOUND THEN

RETURN NULL ; -- если строки в new _table не существует

END IF ;
RETURN OLD ;

ELSEIF ( TG_OP = 'INSERT' ) THEN

-- C оздание новой записи в new _table
INSERT INTO new _table ( order , equipme nt , p roduct , quantity , time_sum ) VALUES ( NEW . order_number , NEW . equipment ,

NEW . part , NEW . otk , NEW . hours_count );
RETURN NEW ;

ELSIF ( TG_OP = 'UPDATE' ) THEN

-- Обновление записи в new _table
UPDATE new _table SET order = NEW . order , equip ment = NEW . equipment , p roduct = NEW . p roduct ,

quantity = NEW . quantity , time_sum = NEW . hours_count
WHERE order = OLD . order AND equip ment = OLD . equipment AND p roduct = OLD . p roduct AND

quantity = OLD . quantity AND time_sum = OLD . hours_count ;
RETURN NEW ;

RETURN NULL ;
END ;
$select_to$ LANGUAGE plpgsql;



CREATE TRIGGER select_to_table
BEFORE INSERT OR UPDATE OR DELETE ON base _table
FOR ROW EXECUTE PROCEDURE process_select_to ();

After executing this code, a new trigger function will be registered in the PostgreSQL database to handle the creation, modification or deletion of records in “base_table”. Similar operations will be performed synchronously with the corresponding records in “new_table” .

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.

Do you like it? Share on social networks!

A
  • Aug. 10, 2018, 9:40 a.m.

Приветствую!

Если вы создаете новую таблицу, почему бы просто не сделать вьюху ? Просто от одного названия "триггер" как-то не хочется его использовать, а уж кода сколько писать...

Comments

Only authorized users can post comments.
Please, Log in or Sign up
e
  • ehot
  • March 31, 2024, 2:29 p.m.

C++ - Тест 003. Условия и циклы

  • Result:78points,
  • Rating points2
B

C++ - Test 002. Constants

  • Result:16points,
  • Rating points-10
B

C++ - Test 001. The first program and data types

  • Result:46points,
  • Rating points-6
Last comments
k
kmssrFeb. 8, 2024, 6:43 p.m.
Qt Linux - Lesson 001. Autorun Qt application under Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lesson 007. Working with ICMP Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVADec. 25, 2023, 10:30 a.m.
Boost - static linking in CMake project under Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJoDec. 25, 2023, 8:38 a.m.
Boost - static linking in CMake project under Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
GvozdikDec. 18, 2023, 9:01 p.m.
Qt/C++ - Lesson 056. Connecting the Boost library in Qt for MinGW and MSVC compilers Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Now discuss on the forum
a
a_vlasovApril 14, 2024, 6:41 a.m.
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
Павел Дорофеев
Павел ДорофеевApril 14, 2024, 2:35 a.m.
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
f
fastrexApril 4, 2024, 4:47 a.m.
Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…
AC
Alexandru CodreanuJan. 19, 2024, 11:57 a.m.
QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…

Follow us in social networks