# Postgres

# Как использовать процедуры и функции на Pl/SQL, какие есть еще языки для работы с Postgres?

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%92-postgresql-%D0%B4%D0%BB%D1%8F-%D0%BD%D0%B0%D0%BF" inline-copy-host=""><div>В PostgreSQL для написания процедур и функций используется процедурный язык **PL/pgSQL** (Procedural Language/PostgreSQL Structured Query Language).</div>  
</div>#### Функции (Functions)

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%A4%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8-%D0%B2-postgresql" inline-copy-host=""><div>Функции в PostgreSQL могут возвращать значение (или набор строк) и могут использоваться в SQL-запросах (в секциях `SELECT`, `WHERE`, `JOIN`).</div>  
<div>Синтаксис создания простой функции:</div>  
</div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-209 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="3" id="bkmrk-sql" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8xMjE0OWMzNzAzMGE4ZmIxIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zNTcyOGM5NjEzZTk4ZWU5IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-209"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-209"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-209 ng-star-inserted"><span class="ng-tns-c2344738136-209">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-209 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
CREATE OR REPLACE FUNCTION calculate_bonus(emp_id INT, bonus_percentage NUMERIC)
RETURNS NUMERIC AS $$
DECLARE
    current_salary NUMERIC;
    calculated_bonus NUMERIC;
BEGIN
    -- Получаем текущую зарплату сотрудника
    SELECT salary INTO current_salary 
    FROM employees 
    WHERE id = emp_id;
    
    -- Если сотрудник не найден, возвращаем 0
    IF current_salary IS NULL THEN
        RETURN 0;
    END IF;

    calculated_bonus := current_salary * (bonus_percentage / 100);
    RETURN calculated_bonus;
END;
$$ LANGUAGE plpgsql;

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-209 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="3" id="bkmrk-" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8xMjE0OWMzNzAzMGE4ZmIxIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zNTcyOGM5NjEzZTk4ZWU5IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-209"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-209"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%92%D1%8B%D0%B7%D0%BE%D0%B2-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8%3A" inline-copy-host=""><div>Вызов функции:</div>  
</div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-210 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="6" id="bkmrk-sql-1" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8xMjE0OWMzNzAzMGE4ZmIxIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zNTcyOGM5NjEzZTk4ZWU5IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-210"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-210"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-210 ng-star-inserted"><span class="ng-tns-c2344738136-210">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-210 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
SELECT calculate_bonus(5, 10.5);

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-210 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="6" id="bkmrk--1" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8xMjE0OWMzNzAzMGE4ZmIxIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zNTcyOGM5NjEzZTk4ZWU5IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-210"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-210"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--2" inline-copy-host=""></div>#### Процедуры (Procedures)

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%9F%D1%80%D0%BE%D1%86%D0%B5%D0%B4%D1%83%D1%80%D1%8B-%D0%BF%D0%BE%D1%8F%D0%B2%D0%B8%D0%BB%D0%B8%D1%81%D1%8C-" inline-copy-host=""><div>Процедуры появились начиная с PostgreSQL 11. В отличие от функций, они **не возвращают значение** через `RETURN` (но могут возвращать данные через параметры `INOUT` или `OUT`), однако они **поддерживают транзакции** (`COMMIT`, `ROLLBACK`), в то время как функции — нет.</div>  
<div>Синтаксис создания процедуры:</div>  
</div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-211 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="9" id="bkmrk-sql-2" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8xMjE0OWMzNzAzMGE4ZmIxIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zNTcyOGM5NjEzZTk4ZWU5IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-211"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-211"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-211 ng-star-inserted"><span class="ng-tns-c2344738136-211">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-211 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
CREATE OR REPLACE PROCEDURE transfer_funds(
    sender_account INT, 
    receiver_account INT, 
    amount NUMERIC
)
LANGUAGE plpgsql
AS $$
BEGIN
    -- Снимаем средства с одного счета
    UPDATE accounts 
    SET balance = balance - amount 
    WHERE id = sender_account;

    -- Зачисляем средства на другой счет
    UPDATE accounts 
    SET balance = balance + amount 
    WHERE id = receiver_account;

    -- Фиксируем транзакцию
    COMMIT;
END;
$$;

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-211 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="9" id="bkmrk--3" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8xMjE0OWMzNzAzMGE4ZmIxIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zNTcyOGM5NjEzZTk4ZWU5IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-211"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-211"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%92%D1%8B%D0%B7%D0%BE%D0%B2-%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D0%B4%D1%83%D1%80%D1%8B-%28%D0%B8%D1%81%D0%BF" inline-copy-host=""><div>Вызов процедуры (используется команда `CALL`):</div>  
</div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-212 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="12" id="bkmrk-sql-3" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8xMjE0OWMzNzAzMGE4ZmIxIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zNTcyOGM5NjEzZTk4ZWU5IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-212"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-212"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-212 ng-star-inserted"><span class="ng-tns-c2344738136-212">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-212 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
CALL transfer_funds(1, 2, 500.00);

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-212 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="12" id="bkmrk--4" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8xMjE0OWMzNzAzMGE4ZmIxIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zNTcyOGM5NjEzZTk4ZWU5IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-212"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-212"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--5" inline-copy-host=""></div>#### Основные управляющие конструкции PL/pgSQL

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%A3%D1%81%D0%BB%D0%BE%D0%B2%D0%B8%D1%8F-%28if-%2F-then-%2F" inline-copy-host="">- <div>**Условия (IF / THEN / ELSE):**</div>  
    <div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-213 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="15" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8xMjE0OWMzNzAzMGE4ZmIxIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zNTcyOGM5NjEzZTk4ZWU5IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-213"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-213"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-213 ng-star-inserted"><span class="ng-tns-c2344738136-213">SQL</span></div></div></div></div>```
    IF v_count > 10 THEN
        -- код
    ELSIF v_count > 0 THEN
        -- код
    ELSE
        -- код
     END IF;
    
    ```
- <div>**Циклы (LOOP, WHILE, FOR):**</div>  
    <div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-214 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="18" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8xMjE0OWMzNzAzMGE4ZmIxIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zNTcyOGM5NjEzZTk4ZWU5IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-214"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-214"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-214 ng-star-inserted"><span class="ng-tns-c2344738136-214">SQL</span></div></div></div></div>```
    -- Цикл с условием
    WHILE v_counter < 10 LOOP
        v_counter := v_counter + 1;
    END LOOP;
    
    -- Обход результатов запроса
    FOR r_row IN SELECT * FROM employees LOOP
        RAISE NOTICE 'Employee: %', r_row.name;
    END LOOP;
    
    ```
- <div>**Обработка ошибок (EXCEPTION):**</div>  
    <div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-215 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="21" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8xMjE0OWMzNzAzMGE4ZmIxIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zNTcyOGM5NjEzZTk4ZWU5IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-215"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-215"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-215 ng-star-inserted"><span class="ng-tns-c2344738136-215">SQL</span></div></div></div></div>```
    BEGIN
        -- рискованный код
    EXCEPTION
        WHEN division_by_zero THEN
            RAISE NOTICE 'Деление на ноль!';
        WHEN OTHERS THEN
            RAISE NOTICE 'Произошла непредвиденная ошибка: %', SQLERRM;
    END;
    
    ```

<div>Главное отличие при выборе между ними: если вам нужно делать выборку данных внутри `SELECT` — пишите **функцию**. Если вам нужно выполнить набор команд модификации данных с управлением транзакциями — используйте **процедуру**.</div>  
<div>Какой именно сценарий или задачу вы хотите реализовать с помощью PL/pgSQL? Могу написать конкретный пример под ваши требования.</div></div>### Чем различаются языки PL/SQL у Oracle и Postgres?

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%A1%D1%80%D0%B0%D0%B2%D0%BD%D0%B5%D0%BD%D0%B8%D0%B5-%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D0%B4%D1%83%D1%80%D0%BD%D1%8B" inline-copy-host=""><div>Сравнение процедурных расширений Oracle (**PL/SQL**) и PostgreSQL (**PL/pgSQL**) сводится к сопоставлению зрелого коммерческого стандарта и мощного открытого аналога. Несмотря на внешнюю схожесть (оба языка вдохновлены Ada и используют блоки `DECLARE`, `BEGIN`, `EXCEPTION`), между ними есть глубокие архитектурные и синтаксические различия.</div>  
</div>#### Основные различия

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%A5%D0%B0%D1%80%D0%B0%D0%BA%D1%82%D0%B5%D1%80%D0%B8%D1%81%D1%82%D0%B8%D0%BA%D0%B0-oracl" inline-copy-host=""><table data-path-to-node="2"><thead><tr><td>**Характеристика**</td><td>**Oracle (PL/SQL)**</td><td>**PostgreSQL (PL/pgSQL)**</td></tr></thead><tbody><tr><td><span data-path-to-node="2,1,0,0">**Интеграция с СУБД**</span></td><td><span data-path-to-node="2,1,1,0">Язык является **родным и доминирующим** для СУБД. На нем написаны даже многие внутренние системные пакеты.</span></td><td><span data-path-to-node="2,1,2,0">Язык — один из множества поддерживаемых (`plpython`, `plperl`, `pltcl`), хотя и самый популярный для написания логики в БД.</span></td></tr><tr><td><span data-path-to-node="2,2,0,0">**Пакеты (Packages)**</span></td><td><span data-path-to-node="2,2,1,0">**Есть.** Позволяют группировать типы, функции и процедуры, а также имеют состояние сессии (общие переменные внутри пакета).</span></td><td><span data-path-to-node="2,2,2,0">**Нет** в чистом виде (хотя можно эмулировать через схемы). Переменные уровня сессии требуют сторонних расширений или таблиц.</span></td></tr><tr><td><span data-path-to-node="2,3,0,0">**Типы данных и коллекции**</span></td><td><span data-path-to-node="2,3,1,0">Огромный выбор встроенных сложных типов: `TABLE` (ассоциативные массивы), `VARRAY`, `NESTED TABLE`, продвинутая работа с записями (`ROWTYPE`).</span></td><td><span data-path-to-node="2,3,2,0">Коллекции ограничены базовыми массивами и таблицами типов (строки таблиц, `RECORD`), работа с ними менее гибкая.</span></td></tr><tr><td><span data-path-to-node="2,4,0,0">**Обработка SQL (курсоры)**</span></td><td><span data-path-to-node="2,4,1,0">Поддерживает неявные курсоры с мощными атрибутами (`%FOUND`, `%NOTFOUND`, `%ROWCOUNT`), а также продвинутые конструкции `BULK COLLECT` и `FORALL` для массовой обработки (оптимизация контекст-переключений).</span></td><td><span data-path-to-node="2,4,2,0">Курсоры есть (`FOR` циклы по выборкам), но массовые операции (BULK) реализуются иначе, часто через конструкции вроде `ANY` или временные таблицы.</span></td></tr><tr><td><span data-path-to-node="2,5,0,0">**Управление транзакциями**</span></td><td><span data-path-to-node="2,5,1,0">Поддерживает **автономные транзакции** (`PRAGMA AUTONOMOUS_TRANSACTION`), позволяя фиксировать изменения в логах независимо от основной транзакции.</span></td><td><span data-path-to-node="2,5,2,0">Обычные функции не могут управлять транзакциями. Процедуры могут выполнять `COMMIT` или `ROLLBACK`, но автономных транзакций «из коробки» до сих пор нет (требуются костыли через внешние вызовы или dblink).</span></td></tr><tr><td><span data-path-to-node="2,6,0,0">**Обработка ошибок**</span></td><td><span data-path-to-node="2,6,1,0">Большой встроенный набор предопределенных исключений (`NO_DATA_FOUND`, `TOO_MANY_ROWS` и др.).</span></td><td><span data-path-to-node="2,6,2,0">Исключения обрабатываются через блок `EXCEPTION`, но кодов ошибок и имен меньше, часто приходится проверять коды ошибок через `SQLSTATE`.</span></td></tr><tr><td><span data-path-to-node="2,7,0,0">**Объектно-ориентированные возможности**</span></td><td><span data-path-to-node="2,7,1,0">Поддерживает ООП-концепции: типы объектов с методами, наследование объектов в базе данных.</span></td><td><span data-path-to-node="2,7,2,0">Отсутствуют ООП-возможности на уровне языка процедур.</span></td></tr></tbody></table>

</div>#### Примеры синтаксических отличий

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-1.-%D0%9E%D0%B1%D1%8A%D1%8F%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5" inline-copy-host=""><div>**1. Объявление переменных и присваивание:**</div>  
- <div>**Oracle:** Часто используется оператор присваивания `:=`, но также активно применяется `DEFAULT` или прямая инициализация. Широко развита работа с атрибутами `%TYPE` и `%ROWTYPE`.</div>
- <div>**PostgreSQL:** Синтаксис очень похож (`:=`), но синтаксис функций требует оборачивания тела в текстовые кавычки (`$$ ... $$`) и явного указания языка `LANGUAGE plpgsql`.</div>

<div>**2. Функция выборки одной строки (когда строка не найдена):**</div>  
- <div>**Oracle:** Если запрос в блоке PL/SQL возвращает 0 строк, автоматически возбуждается исключение `NO_DATA_FOUND`.</div>
- <div>**PostgreSQL:** При использовании `SELECT ... INTO` в PL/pgSQL переменные просто становятся `NULL`, исключение не генерируется (если не настроено строгое поведение).</div>

<div>**3. Создание пакетов (отсутствие в Postgres):**</div>  
- <div>**Oracle:**</div>  
    <div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-175 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="5" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9kOTllY2MxMzExY2UwOTZlIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19iOTFlZjA5MGIwM2Q4ZTZkIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-175"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-175"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-175 ng-star-inserted"><span class="ng-tns-c2344738136-175">SQL</span></div></div></div></div>```
    CREATE OR REPLACE PACKAGE my_pack AS
        procedure do_something;
    END my_pack;
    
    ```
- <div>**PostgreSQL:** Пакеты создать нельзя. Функции и процедуры создаются по отдельности, а для изоляции и структурирования кода используются разные **схемы** (schemas).</div>

<div>Какой конкретный паттерн из Oracle вы пытаетесь перенести в PostgreSQL? Возможно, для него есть стандартный и элегантный обходной путь.</div></div>### Какие еще языки могут использоваться с postgres?

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-postgresql-%D0%BE%D0%B1%D0%BB%D0%B0%D0%B4%D0%B0%D0%B5%D1%82-" inline-copy-host=""><div>PostgreSQL обладает уникальной архитектурой расширяемости (благодаря системе обработчиков языков — *Language Handlers*), которая позволяет писать функции, триггеры и процедуры не только на PL/pgSQL, но и на множестве других языков программирования.</div>  
<div>Все языки делятся на две категории: входящие в стандартный комплект и сторонние расширения.</div>  
</div>#### 1. Языки из стандартного дистрибутива

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%AD%D1%82%D0%B8-%D1%8F%D0%B7%D1%8B%D0%BA%D0%B8-%D0%BF%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D1%8F%D1%8E%D1%82" inline-copy-host=""><div>Эти языки поставляются вместе с базовым сервером PostgreSQL, их нужно только активировать в конкретной базе данных командой `CREATE EXTENSION extension_name;` (например, `CREATE EXTENSION plpython3u;`).</div>  
- <div>**PL/Python (`plpython3u`)**</div><div>Один из самых популярных альтернативных языков. Позволяет использовать всю мощь Python для работы с данными, математикой, парсинга или вызова внешних API прямо из базы.</div>  
    <div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-176 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="3" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9hNzYyYTBjN2RhNmE3NjYyIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19jMWU4NzkzYWJhMzk3Y2IwIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-176"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-176"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-176 ng-star-inserted"><span class="ng-tns-c2344738136-176">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-176 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
    CREATE FUNCTION get_max(a int, b int) RETURNS int AS $$
        return max(a, b)
    $$ LANGUAGE plpython3u;
    
    ```
    
    <div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-176 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="3" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9hNzYyYTBjN2RhNmE3NjYyIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19jMWU4NzkzYWJhMzk3Y2IwIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-176"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-176"></div></div></div>
- <div>**<span class="citation-145">PL/Perl (</span>`<span class="citation-145">plperl</span>`<span class="citation-145">)</span>**<span class="citation-145 citation-end-145"> <sup class="superscript" data-turn-source-index="1"></sup></span>Исторически один из первых языков в Postgres. Удобен для сложных манипуляций со строками, регулярных выражений и задач системного администрирования. </div>
- <div>**<span class="citation-144">PL/Tcl (</span>`<span class="citation-144">pltcl</span>`<span class="citation-144">)</span>**<span class="citation-144 citation-end-144"> <sup class="superscript" data-turn-source-index="2"></sup></span>Язык Tcl (Tool Command Language), исторически встроенный в экосистему PostgreSQL. Реже используется в новых проектах, но поддерживается «из коробки». </div>

</div>#### 2. Сторонние и популярные языки (расширения)

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%AD%D1%82%D0%B8-%D1%8F%D0%B7%D1%8B%D0%BA%D0%B8-%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%B0%D1%82%D1%8B%D0%B2" inline-copy-host=""><div>Эти языки разрабатываются сторонним сообществом, устанавливаются отдельно, но глубоко интегрируются в СУБД:</div>  
- <div>**<span class="citation-143 citation-end-143">PL/v8 (JavaScript / V8)<sup class="superscript" data-turn-source-index="3"></sup></span>**</div><div>Позволяет писать функции на **JavaScript**, используя движок V8 от Google. Идеально подходит, если нужно обрабатывать JSON-документы (которых в Postgres множество) с помощью привычного JS-кода или переиспользовать бизнес-логику из Node.js/фронтенда на стороне базы данных. </div>
- <div>**<span class="citation-142 citation-end-142">PL/Java<sup class="superscript" data-turn-source-index="4"></sup></span>**</div><div><span class="citation-141">Дает возможность писать код на </span>**<span class="citation-141">Java</span>**<span class="citation-141 citation-end-141">, используя стандартные интерфейсы JDBC.<sup class="superscript" data-turn-source-index="5"></sup></span> Востребовано в энтерпрайз-среде, где вся логика монолита завязана на Java. </div>
- <div>**<span class="citation-140 citation-end-140">PL/R<sup class="superscript" data-turn-source-index="6"></sup></span>**</div><div>Интеграция со статистическим языком **R**. Позволяет выполнять сложные математические вычисления, статистический анализ и строить модели машинного обучения прямо внутри SQL-запросов. </div>
- <div>**<span class="citation-139 citation-end-139">PL/Lua<sup class="superscript" data-turn-source-index="7"></sup></span>**</div><div><span class="citation-138">Сверхбыстрый и легкий скриптовый язык </span>**<span class="citation-138">Lua</span>**<span class="citation-138 citation-end-138">, встроенный в PostgreSQL.<sup class="superscript" data-turn-source-index="8"></sup></span> <span class="citation-137 citation-end-137">Часто применяется там, где критична максимальная производительность скриптов в базе.<sup class="superscript" data-turn-source-index="9"></sup></span> </div>
- <div>**<span class="citation-136 citation-end-136">PL/sh<sup class="superscript" data-turn-source-index="10"></sup></span>**</div><div>Позволяет выполнять **системные shell-скрипты** (Bash и др.) прямо из базы данных (например, запускать утилиты ОС по триггеру). Это крайне опасный с точки зрения безопасности язык, поэтому он используется только в изолированных контурах. </div>

</div>#### Когда и что использовать?

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%94%D0%BB%D1%8F-%D1%81%D1%82%D0%B0%D0%BD%D0%B4%D0%B0%D1%80%D1%82%D0%BD%D0%BE%D0%B9-%D0%B1%D0%B8%D0%B7%D0%BD" inline-copy-host="">- <div>Для стандартной бизнес-логики, CRUD-операций и высокой скорости работы без лишних зависимоств выбирайте **PL/pgSQL**.</div>
- <div>Для работы с нейросетями, сложной аналитики, парсинга или интеграции с внешними библиотеками экосистемы Python незаменим **PL/Python**.</div>
- <div>Для интенсивной работы с JSON и быстрой обработки данных через знакомый синтаксис — **PL/v8 (JS)**.</div>

</div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--6" inline-copy-host=""></div>

# Как активировать PL/Python, примеры функции и процедуры

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%A7%D1%82%D0%BE%D0%B1%D1%8B-%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D1%82%D1%8C-p" inline-copy-host=""><div>Чтобы использовать **PL/Python**, необходимо, чтобы модуль Python был установлен в вашей операционной системе для вашей версии PostgreSQL (например, пакет `postgresql-plpython3` в Linux), после чего его можно включить в нужной базе данных.</div>  
</div>#### 1. Активация PL/Python

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%90%D0%BA%D1%82%D0%B8%D0%B2%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C-%D1%80%D0%B0%D1%81%D1%88%D0%B8%D1%80%D0%B5" inline-copy-host=""><div>Активировать расширение нужно в целевой базе данных от имени суперпользователя (`postgres`):</div>  
</div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-177 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="3" id="bkmrk-sql" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9lNmVjNTljZDRkZTFlMGJhIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19hOWNkYzgyNmY4NTNhYjE0IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-177"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-177"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-177 ng-star-inserted"><span class="ng-tns-c2344738136-177">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-177 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
CREATE EXTENSION IF NOT EXISTS plpython3u;

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-177 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="3" id="bkmrk-" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9lNmVjNTljZDRkZTFlMGJhIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19hOWNkYzgyNmY4NTNhYjE0IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-177"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-177"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--1" inline-copy-host=""></div>> <div>**Важная деталь:** Буква `u` на конце расширения означает *untrusted* (недоверенный). Это значит, что функции на PL/Python могут обращаться к файловой системе сервера, сети и операционной системе, поэтому создавать функции на PL/Python может только суперпользователь СУБД.</div>

#### 2. Пример функции (Function)

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%A4%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8-%D0%B2-postgresql" inline-copy-host=""><div>Функции в PostgreSQL возвращают значение. В PL/Python вы можете импортировать любые сторонние библиотеки Python (например, `json`, `math`, `requests`, `pandas` и др.).</div>  
<div>Пример функции для парсинга и валидации JSON-строки с расчетом итоговой стоимости:</div>  
</div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-178 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="6" id="bkmrk-sql-1" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9lNmVjNTljZDRkZTFlMGJhIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19hOWNkYzgyNmY4NTNhYjE0IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-178"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-178"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-178 ng-star-inserted"><span class="ng-tns-c2344738136-178">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-178 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
CREATE OR REPLACE FUNCTION calculate_cart_total(cart_json text)
RETURNS numeric AS $$
    import json

    try:
        data = json.loads(cart_json)
        items = data.get("items", [])
        
        total = 0.0
        for item in items:
            price = item.get("price", 0)
            quantity = item.get("quantity", 1)
            total += price * quantity
            
        return round(total, 2)
    except Exception as e:
        # raise_notice или log позволяют отправлять сообщения в клиент/лог базы
        plpy.notice(f"Ошибка при обработке JSON: {e}")
        return 0.0
$$ LANGUAGE plpython3u;

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-178 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="6" id="bkmrk--2" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9lNmVjNTljZDRkZTFlMGJhIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19hOWNkYzgyNmY4NTNhYjE0IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-178"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-178"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%92%D1%8B%D0%B7%D0%BE%D0%B2-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8%3A" inline-copy-host=""><div>**Вызов функции:**</div>  
</div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-179 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="9" id="bkmrk-sql-2" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9lNmVjNTljZDRkZTFlMGJhIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19hOWNkYzgyNmY4NTNhYjE0IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-179"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-179"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-179 ng-star-inserted"><span class="ng-tns-c2344738136-179">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-179 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
SELECT calculate_cart_total('{"items": [{"price": 100.5, "quantity": 2}, {"price": 50.0, "quantity": 1}]}');
-- Вернет: 251.00

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-179 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="9" id="bkmrk--3" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9lNmVjNTljZDRkZTFlMGJhIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19hOWNkYzgyNmY4NTNhYjE0IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-179"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-179"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--4" inline-copy-host=""></div>#### 3. Пример процедуры (Procedure)

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%9F%D1%80%D0%BE%D1%86%D0%B5%D0%B4%D1%83%D1%80%D1%8B-%D0%B2-postgres" inline-copy-host=""><div>Процедуры в PostgreSQL не возвращают результат и поддерживают управление транзакциями (`plpy.commit()`, `plpy.rollback()`).</div>  
<div>Специальный модуль `plpy` внутри PL/Python позволяет выполнять SQL-запросы внутри базы.</div>  
<div>Пример процедуры, которая находит устаревшие записи, отправляет их во внешнюю систему (имитация) и удаляет их:</div>  
</div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-180 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="12" id="bkmrk-sql-3" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9lNmVjNTljZDRkZTFlMGJhIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19hOWNkYzgyNmY4NTNhYjE0IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-180"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-180"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-180 ng-star-inserted"><span class="ng-tns-c2344738136-180">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-180 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
CREATE OR REPLACE PROCEDURE cleanup_old_logs(days_old int)
AS $$
    import time

    # Выполняем SQL-запрос к БД с помощью plpy.execute
    plan = plpy.prepare(
        "SELECT id, message FROM system_logs WHERE created_at < NOW() - ($1 || ' days')::INTERVAL",
        ["int"]
    )
    result = plpy.execute(plan, [days_old])

    plpy.info(f"Найдено записей для удаления: {len(result)}")

    if len(result) > 0:
        # Логика обработки записей на Python
        for row in result:
            log_id = row["id"]
            # Здесь можно вызвать внешний API, сохранить резервную копию и т.д.

        # Удаление записей
        delete_plan = plpy.prepare(
            "DELETE FROM system_logs WHERE created_at < NOW() - ($1 || ' days')::INTERVAL",
            ["int"]
        )
        plpy.execute(delete_plan, [days_old])
        
        # Фиксация изменений
        plpy.commit()
$$ LANGUAGE plpython3u;

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-180 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="12" id="bkmrk--5" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9lNmVjNTljZDRkZTFlMGJhIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19hOWNkYzgyNmY4NTNhYjE0IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-180"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-180"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%92%D1%8B%D0%B7%D0%BE%D0%B2-%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D0%B4%D1%83%D1%80%D1%8B%3A" inline-copy-host=""><div>**Вызов процедуры:**</div>  
</div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-181 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="15" id="bkmrk-sql-4" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9lNmVjNTljZDRkZTFlMGJhIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19hOWNkYzgyNmY4NTNhYjE0IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-181"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-181"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-181 ng-star-inserted"><span class="ng-tns-c2344738136-181">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-181 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
CALL cleanup_old_logs(30);

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-181 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="15" id="bkmrk--6" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9lNmVjNTljZDRkZTFlMGJhIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19hOWNkYzgyNmY4NTNhYjE0IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-181"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-181"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--7" inline-copy-host=""></div>#### Дополнительные возможности `plpy`

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%92%D0%BD%D1%83%D1%82%D1%80%D0%B8-pl%2Fpython-%D0%B2%D0%B0%D0%BC" inline-copy-host=""><div>Внутри PL/Python вам автоматически доступен встроенный объект `plpy`:</div>  
- <div>`plpy.execute("SELECT ...")` — выполнение произвольного SQL-запроса.</div>
- <div>`plpy.prepare(query, [types])` — подготовка SQL-запроса для многократного выполнения.</div>
- <div>`plpy.notice(msg)`, `plpy.info(msg)`, `plpy.warning(msg)` — вывод сообщений пользователю.</div>
- <div>`plpy.error(msg)` — генерация исключения, отменяющего транзакцию.</div>

</div>### Возвращают ли функции PL/Python наборы строк (recordsets) которые можно использовать в SQL-запросах?

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%94%D0%B0%2C-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8-pl%2Fpytho" inline-copy-host=""><div>**Да, функции PL/Python могут возвращать наборы строк.** Их можно использовать непосредственно в блоке `FROM` SQL-запроса, как обычную таблицу или представление (View).</div>  
<div>Возвращать наборы данных из PL/Python можно двумя основными способами:</div>  
1. <div>**Список словарей / списков / кортежей** — если функция формирует и отдает все данные сразу.</div>
2. <div>**Итератор / Генератор (`yield`)** — если данных много и их нужно отдавать по одной строке (ленивые вычисления), чтобы не перегружать оперативную память.</div>

</div>#### Вариант 1. Возврат всех строк через список (List of Dicts)

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%94%D0%BB%D1%8F-%D0%B2%D0%BE%D0%B7%D0%B2%D1%80%D0%B0%D1%82%D0%B0-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D1%8B" inline-copy-host=""><div>Для возврата таблицы используется конструкция `RETURNS TABLE(...)` или `RETURNS SETOF record_type`. Внутри Python-кода вы возвращаете обычный список Python-словарей, где ключи соответствуют именам колонок.</div>  
</div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-182 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="3" id="bkmrk-sql-5" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9lMDdhYTgwOGQ2YjI1MGY0IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zODg0OGUwODdhMzQwZDFkIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-182"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-182"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-182 ng-star-inserted"><span class="ng-tns-c2344738136-182">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-182 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
CREATE OR REPLACE FUNCTION generate_user_report(min_age int)
RETURNS TABLE(user_id int, full_name text, status text) AS $$
    # В реальности данные могут запрашиваться из внешнего API или рассчитываться
    users = [
        {"user_id": 1, "full_name": "Алексей Иванов", "age": 25},
        {"user_id": 2, "full_name": "Мария Сидорова", "age": 19},
        {"user_id": 3, "full_name": "Иван Петров", "age": 34}
    ]
    
    result = []
    for user in users:
        if user["age"] >= min_age:
            result.append({
                "user_id": user["user_id"],
                "full_name": user["full_name"],
                "status": "Adult" if user["age"] >= 21 else "Young"
            })
            
    return result
$$ LANGUAGE plpython3u;

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-182 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="3" id="bkmrk--8" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9lMDdhYTgwOGQ2YjI1MGY0IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zODg0OGUwODdhMzQwZDFkIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-182"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-182"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--9" inline-copy-host=""></div>##### Использование функции в SQL:

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--10" inline-copy-host=""></div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-183 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="6" id="bkmrk-sql-6" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9lMDdhYTgwOGQ2YjI1MGY0IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zODg0OGUwODdhMzQwZDFkIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-183"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-183"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-183 ng-star-inserted"><span class="ng-tns-c2344738136-183">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-183 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
SELECT * 
FROM generate_user_report(20)
WHERE status = 'Adult';

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-183 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="6" id="bkmrk--11" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9lMDdhYTgwOGQ2YjI1MGY0IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zODg0OGUwODdhMzQwZDFkIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-183"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-183"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--12" inline-copy-host=""></div>#### Вариант 2. Построчная отдача через генератор (`yield`)

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%95%D1%81%D0%BB%D0%B8-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F-%D0%B4%D0%BE%D0%BB%D0%B6%D0%BD%D0%B0-" inline-copy-host=""><div>Если функция должна выгрузить 100 000+ строк из внешнего ресурса, сохранение их всех в список в памяти вызовет высокую нагрузку на RAM. В PL/Python можно использовать встроенный оператор Python `yield`.</div>  
</div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-184 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="9" id="bkmrk-sql-7" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9lMDdhYTgwOGQ2YjI1MGY0IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zODg0OGUwODdhMzQwZDFkIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-184"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-184"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-184 ng-star-inserted"><span class="ng-tns-c2344738136-184">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-184 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
CREATE OR REPLACE FUNCTION stream_numbers(limit_num int)
RETURNS TABLE(num int, square int) AS $$
    for i in range(1, limit_num + 1):
        # Отдаем по одной строке за раз
        yield {"num": i, "square": i * i}
$$ LANGUAGE plpython3u;

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-184 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="9" id="bkmrk--13" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9lMDdhYTgwOGQ2YjI1MGY0IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zODg0OGUwODdhMzQwZDFkIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-184"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-184"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--14" inline-copy-host=""></div>##### Использование в SQL-запросе:

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--15" inline-copy-host=""></div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-185 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="12" id="bkmrk-sql-8" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9lMDdhYTgwOGQ2YjI1MGY0IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zODg0OGUwODdhMzQwZDFkIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-185"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-185"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-185 ng-star-inserted"><span class="ng-tns-c2344738136-185">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-185 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
SELECT num, square 
FROM stream_numbers(5) 
WHERE square > 10;

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-185 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="12" id="bkmrk--16" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9lMDdhYTgwOGQ2YjI1MGY0IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zODg0OGUwODdhMzQwZDFkIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-185"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-185"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%A0%D0%B5%D0%B7%D1%83%D0%BB%D1%8C%D1%82%D0%B0%D1%82%3A-num-squar" inline-copy-host=""><div>**Результат:**</div>  
<table data-path-to-node="16"><thead><tr><td>**num**</td><td>**square**</td></tr></thead><tbody><tr><td><span data-path-to-node="16,1,0,0">4</span></td><td><span data-path-to-node="16,1,1,0">16</span></td></tr><tr><td><span data-path-to-node="16,2,0,0">5</span></td><td><span data-path-to-node="16,2,1,0">25</span></td></tr></tbody></table>

</div>#### Вариант 3. Возврат результата встроенного SQL-запроса (`plpy.execute`)

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%95%D1%81%D0%BB%D0%B8-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F-%D0%B2%D1%8B%D0%BF%D0%BE%D0%BB%D0%BD%D1%8F" inline-copy-host=""><div>Если функция выполняет внутренний SQL-запрос с помощью `plpy.execute()`, возвращаемый объект `PLPyResult` уже является итерируемым набором строк, поэтому его можно вернуть напрямую:</div></div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-186 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="17" id="bkmrk-sql-9" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9lMDdhYTgwOGQ2YjI1MGY0IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zODg0OGUwODdhMzQwZDFkIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-186"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-186"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-186 ng-star-inserted"><span class="ng-tns-c2344738136-186">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-186 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
CREATE OR REPLACE FUNCTION get_active_users()
RETURNS TABLE(id int, email text) AS $$
    # plpy.execute возвращает результат, который совместим с SETOF/TABLE
    res = plpy.execute("SELECT id, email FROM users WHERE is_active = true")
    return res
$$ LANGUAGE plpython3u;

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-186 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="17" id="bkmrk--17" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9lMDdhYTgwOGQ2YjI1MGY0IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zODg0OGUwODdhMzQwZDFkIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-186"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-186"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--28" inline-copy-host=""><div class="attachment-container unknown"></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--29" inline-copy-host=""><div class="attachment-container unknown"></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--30" inline-copy-host=""><div class="attachment-container unknown"></div></div>

# Использование Postgres в C#

# Как вызываются процедуры и функции на PL/Python из C#?

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%92%D1%8B%D0%B7%D0%BE%D0%B2-%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D0%B4%D1%83%D1%80-%D0%B8-%D1%84%D1%83%D0%BD" inline-copy-host=""><div>Вызов процедур и функций PostgreSQL из C# (начиная с C# 7 и выше) выполняется через стандартную библиотеку **Npgsql** (официальный ADO.NET провайдер для PostgreSQL).</div>  
<div>С точки зрения приложения C# **абсолютно не имеет значения, на чем написана функция/процедура** (на PL/pgSQL, PL/Python, PL/v8 или C). СУБД предоставляет единый интерфейс вызова.</div>  
</div>#### Вызов функции, возвращающей значение или набор строк

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%94%D0%BB%D1%8F-%D0%B2%D1%8B%D0%B7%D0%BE%D0%B2%D0%B0-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B9-%D1%80" inline-copy-host=""><div>Для вызова функций рекомендуется использовать обычные SQL-запросы `SELECT * FROM function_name(@param)`. Это универсальный подход, работающий как для скалярных значений, так и для табличных результатов (`RETURNS TABLE`).</div>  
</div>##### Пример 1: Вызов функции, возвращающей набор строк (`RETURNS TABLE`)

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%9F%D1%80%D0%B5%D0%B4%D0%BF%D0%BE%D0%BB%D0%BE%D0%B6%D0%B8%D0%BC%2C-%D1%83-%D0%BD%D0%B0%D1%81-%D0%B5" inline-copy-host=""><div>Предположим, у нас есть функция PL/Python из предыдущего примера:</div>  
</div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-187 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="3" id="bkmrk-sql-10" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8yMTE2MDcwMTQyNTUzODQ3IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY185NzQwMGZhZGMzNWYxYTcxIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-187"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-187"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-187 ng-star-inserted">**<span class="ng-tns-c2344738136-187">SQL</span>**<div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-187 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
CREATE OR REPLACE FUNCTION generate_user_report(min_age int)
RETURNS TABLE(user_id int, full_name text, status text) AS $$
    # PL/Python логика
$$ LANGUAGE plpython3u;

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-187 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="3" id="bkmrk--18" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8yMTE2MDcwMTQyNTUzODQ3IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY185NzQwMGZhZGMzNWYxYTcxIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-187"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-187"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%9A%D0%BE%D0%B4-c%23-%D0%B4%D0%BB%D1%8F-%D0%B2%D1%8B%D0%B7%D0%BE%D0%B2%D0%B0-%D1%84%D1%83" inline-copy-host=""><div>**Код C# для вызова функции и чтения данных:**</div>  
</div>```
using System;
using System.Threading.Tasks;
using Npgsql;

public class PostgresExample
{
    private const string ConnectionString = "Host=localhost;Database=mydb;Username=postgres;Password=secret";

    public async Task ReadUserReportAsync(int minAge)
    {
        await using var conn = new NpgsqlConnection(ConnectionString);
        await conn.OpenAsync();

        // Обращаемся к функции как к обычной таблице
        const string sql = "SELECT user_id, full_name, status FROM generate_user_report(@minAge)";

        await using var cmd = new NpgsqlCommand(sql, conn);
        cmd.Parameters.AddWithValue("minAge", minAge);

        await using var reader = await cmd.ExecuteReaderAsync();
        
        while (await reader.ReadAsync())
        {
            // Используем кортежи (Tuples), доступные начиная с C# 7
            var (userId, fullName, status) = (
                reader.GetInt32(0),
                reader.GetString(1),
                reader.GetString(2)
            );

            Console.WriteLine($"ID: {userId}, Имя: {fullName}, Статус: {status}");
        }
    }
}

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-188 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="6" id="bkmrk--19" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8yMTE2MDcwMTQyNTUzODQ3IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY185NzQwMGZhZGMzNWYxYTcxIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-188"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-188"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--20" inline-copy-host=""></div>##### Пример 2: Вызов скалярной функции (возвращает одно значение)

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--21" inline-copy-host=""></div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-189 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="9" id="bkmrk-c%23-1" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8yMTE2MDcwMTQyNTUzODQ3IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY185NzQwMGZhZGMzNWYxYTcxIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-189"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-189"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-189 ng-star-inserted"><span class="ng-tns-c2344738136-189">C#</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-189 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
public async Task<decimal> GetCartTotalAsync(string cartJson)
{
    await using var conn = new NpgsqlConnection(ConnectionString);
    await conn.OpenAsync();

    const string sql = "SELECT calculate_cart_total(@cartJson)";

    await using var cmd = new NpgsqlCommand(sql, conn);
    cmd.Parameters.AddWithValue("cartJson", cartJson);

    // ExecuteScalarAsync возвращает первое значение первой строки
    var result = await cmd.ExecuteScalarAsync();
    return Convert.ToDecimal(result);
}

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-189 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="9" id="bkmrk--22" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8yMTE2MDcwMTQyNTUzODQ3IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY185NzQwMGZhZGMzNWYxYTcxIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-189"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-189"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--23" inline-copy-host=""></div>#### Вызов процедуры (Procedure)

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%9F%D1%80%D0%BE%D1%86%D0%B5%D0%B4%D1%83%D1%80%D1%8B-%D0%B2-postgres-1" inline-copy-host=""><div>Процедуры в PostgreSQL вызывают с помощью команды `CALL`. В Npgsql это можно сделать двумя способами: через явный `CALL` или через `CommandType.StoredProcedure`.</div>  
</div>##### Пример вызова процедуры через `CALL`

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%9F%D1%80%D0%B5%D0%B4%D0%BF%D0%BE%D0%BB%D0%BE%D0%B6%D0%B8%D0%BC%2C-%D1%83-%D0%BD%D0%B0%D1%81-%D0%B5-1" inline-copy-host=""><div>Предположим, у нас есть процедура PL/Python:</div>  
</div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-190 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="12" id="bkmrk-sql-11" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8yMTE2MDcwMTQyNTUzODQ3IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY185NzQwMGZhZGMzNWYxYTcxIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-190"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-190"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-190 ng-star-inserted">**<span class="ng-tns-c2344738136-190">SQL</span>**<div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-190 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
CREATE OR REPLACE PROCEDURE cleanup_old_logs(days_old int) AS $$
    # PL/Python логика
$$ LANGUAGE plpython3u;

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-190 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="12" id="bkmrk--24" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8yMTE2MDcwMTQyNTUzODQ3IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY185NzQwMGZhZGMzNWYxYTcxIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-190"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-190"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%9A%D0%BE%D0%B4-c%23%3A" inline-copy-host=""><div>**Код C#:**</div>  
</div>```
public async Task CleanupLogsAsync(int daysOld)
{
    await using var conn = new NpgsqlConnection(ConnectionString);
    await conn.OpenAsync();

    // Явный вызов процедуры
    const string sql = "CALL cleanup_old_logs(@daysOld)";

    await using var cmd = new NpgsqlCommand(sql, conn);
    cmd.Parameters.AddWithValue("daysOld", daysOld);

    // Используем ExecuteNonQueryAsync, так как процедура не возвращает табличный результат
    await cmd.ExecuteNonQueryAsync();
}

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-191 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="15" id="bkmrk--25" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8yMTE2MDcwMTQyNTUzODQ3IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY185NzQwMGZhZGMzNWYxYTcxIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-191"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-191"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--26" inline-copy-host=""></div>#### Особенности работы с массивами (ARRAY) из C# 7

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%95%D1%81%D0%BB%D0%B8-%D0%B2%D0%B0%D1%88%D0%B0-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F-pl" inline-copy-host=""><div>Если ваша функция PL/Python принимает или возвращает массив PostgreSQL (например, `integer[]` или `text[]`), Npgsql автоматически сопоставляет их с массивами C# (`int[]`, `string[]`).</div>  
<div>**Пример:**</div></div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-192 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="18" id="bkmrk-c%23-3" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8yMTE2MDcwMTQyNTUzODQ3IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY185NzQwMGZhZGMzNWYxYTcxIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-192"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-192"></div></div></div>```
public async Task ProcessIdsAsync(int[] ids)
{
    await using var conn = new NpgsqlConnection(ConnectionString);
    await conn.OpenAsync();

    const string sql = "SELECT process_array_function(@ids)";

    await using var cmd = new NpgsqlCommand(sql, conn);
    // Npgsql автоматически приведёт int[] C# к integer[] в Postgres
    cmd.Parameters.AddWithValue("ids", ids);

    await cmd.ExecuteNonQueryAsync();
}

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-192 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="18" id="bkmrk--27" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8yMTE2MDcwMTQyNTUzODQ3IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY185NzQwMGZhZGMzNWYxYTcxIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-192"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-192"></div></div></div>

# Как передавать и парсить JSON/JSONB из C# в PL/Python функцию через Npgsql?

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%9F%D0%B5%D1%80%D0%B5%D0%B4%D0%B0%D1%87%D0%B0-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85-%D0%B2-%D1%84%D0%BE" inline-copy-host=""><div>Передача данных в формате **JSON / JSONB** из C# в PL/Python — один из наиболее эффективных подходов: C# сериализует объект в JSON, Npgsql передает его в PostgreSQL как native-тип `jsonb`, а PL/Python автоматически или с помощью стандартного модуля `json` превращает его в родной словарь (`dict`) или список (`list`).</div>  
</div>#### 1. Подготовка функции в PostgreSQL (PL/Python)

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%A4%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F-%D0%BF%D1%80%D0%B8%D0%BD%D0%B8%D0%BC%D0%B0%D0%B5%D1%82-%D1%82%D0%B8" inline-copy-host=""><div>Функция принимает тип `jsonb` (или `json`). В PL/Python аргументы типа `json/jsonb` передаются в виде обычных **строк**, поэтому внутри их нужно распарсить с помощью стандартного модуля `json`.</div>  
</div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-193 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="3" id="bkmrk-sql" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9mYmVjZDQzODhkZjEzYjYzIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY185M2Q4YWZjMDMxMmNmMTJjIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-193"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-193"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-193 ng-star-inserted"><span class="ng-tns-c2344738136-193">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-193 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
CREATE OR REPLACE FUNCTION process_user_data(payload jsonb)
RETURNS TABLE(user_id int, status text, is_valid boolean) AS $$
    import json

    # PL/Python получает json/jsonb как строку
    data = json.loads(payload)

    users = data.get("users", [])
    result = []

    for user in users:
        u_id = user.get("id")
        email = user.get("email", "")
        
        # Простая проверка/обработка
        is_valid = "@" in email
        status = "Active" if user.get("age", 0) >= 18 else "Pending"
        
        result.append({
            "user_id": u_id,
            "status": status,
            "is_valid": is_valid
        })

    return result
$$ LANGUAGE plpython3u;

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-193 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="3" id="bkmrk-" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9mYmVjZDQzODhkZjEzYjYzIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY185M2Q4YWZjMDMxMmNmMTJjIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-193"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-193"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--1" inline-copy-host=""></div>#### 2. Вызов из C# с помощью Npgsql и System.Text.Json

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%92-%D1%81%D0%BE%D0%B2%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D1%8B%D1%85-%D0%B2%D0%B5%D1%80%D1%81%D0%B8%D1%8F" inline-copy-host=""><div>В современных версиях Npgsql передача JSON выполняется через встроенную поддержку `System.Text.Json` или явное указание типа `NpgsqlDbType.Jsonb`.</div>  
</div>##### Вариант 1: Использование `System.Text.Json` (рекомендуемый)

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--2" inline-copy-host=""></div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-194 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="6" id="bkmrk-c%23" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9mYmVjZDQzODhkZjEzYjYzIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY185M2Q4YWZjMDMxMmNmMTJjIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-194"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-194"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-194 ng-star-inserted"><span class="ng-tns-c2344738136-194">C#</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-194 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;
using Npgsql;
using NpgsqlTypes;

public class JsonPostgresExample
{
    private const string ConnectionString = "Host=localhost;Database=mydb;Username=postgres;Password=secret";

    public async Task ProcessDataAsync()
    {
        // 1. Формируем C# объект для передачи
        var requestData = new
        {
            users = new[]
            {
                new { id = 101, email = "alex@example.com", age = 25 },
                new { id = 102, email = "invalid_email", age = 16 }
            }
        };

        // 2. Сериализуем объект в строку JSON
        string jsonPayload = JsonSerializer.Serialize(requestData);

        await using var conn = new NpgsqlConnection(ConnectionString);
        await conn.OpenAsync();

        const string sql = "SELECT user_id, status, is_valid FROM process_user_data(@payload)";

        await using var cmd = new NpgsqlCommand(sql, conn);

        // 3. Добавляем параметр с явным указанием NpgsqlDbType.Jsonb
        cmd.Parameters.Add(new NpgsqlParameter("payload", NpgsqlDbType.Jsonb) 
        { 
            Value = jsonPayload 
        });

        // 4. Читаем результат
        await using var reader = await cmd.ExecuteReaderAsync();
        while (await reader.ReadAsync())
        {
            var userId = reader.GetInt32(0);
            var status = reader.GetString(1);
            var isValid = reader.GetBoolean(2);

            Console.WriteLine($"ID: {userId} | Статус: {status} | Валиден: {isValid}");
        }
    }
}

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-194 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="6" id="bkmrk--3" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9mYmVjZDQzODhkZjEzYjYzIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY185M2Q4YWZjMDMxMmNmMTJjIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-194"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-194"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--4" inline-copy-host=""></div>#### 3. Обратный сценарий: Возврат JSONB из PL/Python в C#

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%95%D1%81%D0%BB%D0%B8-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F-pl%2Fpyth" inline-copy-host=""><div>Если функция PL/Python должна вернуть сложный JSON-объект обратно в C#:</div>  
</div>##### Функция в PostgreSQL:

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--5" inline-copy-host=""></div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-195 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="9" id="bkmrk-sql-1" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9mYmVjZDQzODhkZjEzYjYzIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY185M2Q4YWZjMDMxMmNmMTJjIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-195"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-195"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-195 ng-star-inserted"><span class="ng-tns-c2344738136-195">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-195 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
CREATE OR REPLACE FUNCTION get_system_stats()
RETURNS jsonb AS $$
    import json

    stats = {
        "server_time": "2026-09-12",
        "metrics": {
            "cpu_usage": 42.5,
            "active_connections": 12
        },
        "tags": ["prod", "primary"]
    }

    # Возвращаем JSON-строку
    return json.dumps(stats)
$$ LANGUAGE plpython3u;

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-195 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="9" id="bkmrk--6" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9mYmVjZDQzODhkZjEzYjYzIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY185M2Q4YWZjMDMxMmNmMTJjIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-195"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-195"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--7" inline-copy-host=""></div>##### Код чтения в C#:

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--8" inline-copy-host=""></div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-196 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="12" id="bkmrk-c%23-1" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9mYmVjZDQzODhkZjEzYjYzIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY185M2Q4YWZjMDMxMmNmMTJjIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-196"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-196"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-196 ng-star-inserted"><span class="ng-tns-c2344738136-196">C#</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-196 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
public async Task GetStatsAsync()
{
    await using var conn = new NpgsqlConnection(ConnectionString);
    await conn.OpenAsync();

    const string sql = "SELECT get_system_stats()";
    await using var cmd = new NpgsqlCommand(sql, conn);

    var rawJson = (string)await cmd.ExecuteScalarAsync();

    // Десериализуем JSON в C# объект
    using var doc = JsonDocument.Parse(rawJson);
    var cpu = doc.RootElement
                 .GetProperty("metrics")
                 .GetProperty("cpu_usage")
                 .GetDouble();

    Console.WriteLine($"CPU Usage: {cpu}%");
}

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-196 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="12" id="bkmrk--9" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9mYmVjZDQzODhkZjEzYjYzIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY185M2Q4YWZjMDMxMmNmMTJjIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-196"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-196"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--10" inline-copy-host=""></div>#### Ключевые моменты при работе с JSON/JSONB:

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%A2%D0%B8%D0%BF-%D0%BF%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80%D0%B0-%D0%B2-c%23%3A-" inline-copy-host="">1. <div>**Тип параметра в C#:** При передаче строкового JSON всегда указывайте `NpgsqlDbType.Jsonb` (или `NpgsqlDbType.Json`), иначе Npgsql передаст строку как обычный `text`, и Postgres выдаст ошибку несоответствия типов.</div>
2. <div>**Сериализация внутри PL/Python:**</div>  
    
    - <div>При **приеме** `jsonb` в Python аргумент является строкой `str` — применяйте `json.loads(arg)`.</div>
    - <div>При **возврате** `jsonb` отправляйте строку — применяйте `json.dumps(obj)`.</div>

</div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--11" inline-copy-host=""></div>

# Какой тип даных поля в таблице Postgres лучше использовать для хранения пакетов json, но там могут быть и другие типы, например XML или изображения?

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%94%D0%BB%D1%8F-%D1%82%D0%B0%D0%BA%D0%BE%D0%B9-%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8-%D0%BD%D0%B5-" inline-copy-host=""><div>Для такой задачи **не подходит** чистый тип `JSON` или `JSONB`, так как PostgreSQL начнет проверять валидность JSON-структуры и выдаст ошибку при попытке сохранить XML или бинарные данные (изображение).</div>  
<div>Наилучшим решением будет использование одного из следующих подходов в зависимости от ваших приоритетов по размеру и производительности.</div>  
</div>#### Подход 1: `BYTEA` (Бинарные данные) — *Самый универсальный и производительный*

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%A2%D0%B8%D0%BF-bytea-%28byte-arra" inline-copy-host=""><div>Тип `BYTEA` (byte array) хранит данные «как есть» в виде сырого массива байт. Ему абсолютно неважно, что именно там находится — JSON, XML, PNG/JPEG или текстовый файл.</div>  
</div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-199 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="3" id="bkmrk-sql" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8yZDZmNDk3MTI4ZGI3YTRlIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19jNzg5MDkyYTE4Y2Y1ZGNjIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-199"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-199"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-199 ng-star-inserted"><span class="ng-tns-c2344738136-199">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-199 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
CREATE TABLE payload_store (
    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    content_type VARCHAR(50) NOT NULL, -- 'application/json', 'application/xml', 'image/png'
    payload BYTEA NOT NULL,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-199 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="3" id="bkmrk-" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8yZDZmNDk3MTI4ZGI3YTRlIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19jNzg5MDkyYTE4Y2Y1ZGNjIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-199"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-199"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%9F%D0%BB%D1%8E%D1%81%D1%8B%3A-%D0%90%D0%B1%D1%81%D0%BE%D0%BB%D1%8E%D1%82%D0%BD%D0%B0%D1%8F-%D1%83%D0%BD" inline-copy-host="">- <div>**Плюсы:**</div>  
    
    - <div>Абсолютная универсальность (принимает всё: от текстовых форматов до бинарных изображений).</div>
    - <div>Минимальные накладные расходы при записи/чтении (нет перекодировок в текст).</div>
    - <div>Корректно сохраняет изображения без раздувания размера.</div>
- <div>**Минусы:**</div>  
    
    - <div>Из C# нужно передавать данные как `byte[]` (для JSON/XML это будет байтовый массив строки UTF-8).</div>
    - <div>Внутри PostgreSQL нельзя делать прямое обращение к полям JSON (например, `payload->>'name'`).</div>

</div>#### Подход 2: `TEXT` — *Удобнее для отладки, но со штрафом для бинарных данных*

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%95%D1%81%D0%BB%D0%B8-%D0%B2%D1%8B-%D1%85%D0%BE%D1%82%D0%B8%D1%82%D0%B5-%D0%B2%D0%B8%D0%B4%D0%B5%D1%82" inline-copy-host=""><div>Если вы хотите видеть содержимое пакетов (JSON и XML) напрямую через консоль или клиенты (DBeaver, pgAdmin) без декодирования байтов, можно использовать `TEXT`.</div>  
</div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-200 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="6" id="bkmrk-sql-1" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8yZDZmNDk3MTI4ZGI3YTRlIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19jNzg5MDkyYTE4Y2Y1ZGNjIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-200"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-200"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-200 ng-star-inserted"><span class="ng-tns-c2344738136-200">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-200 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
CREATE TABLE payload_store (
    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    content_type VARCHAR(50) NOT NULL,
    payload TEXT NOT NULL
);

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-200 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="6" id="bkmrk--1" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8yZDZmNDk3MTI4ZGI3YTRlIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19jNzg5MDkyYTE4Y2Y1ZGNjIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-200"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-200"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%9F%D0%BB%D1%8E%D1%81%D1%8B%3A-%D0%A2%D0%B5%D0%BA%D1%81%D1%82%D0%BE%D0%B2%D1%8B%D0%B5-%D0%B4%D0%B0%D0%BD" inline-copy-host="">- <div>**Плюсы:**</div>  
    
    - <div>Текстовые данные (JSON, XML) хранятся в читаемом виде.</div>
    - <div>Текст можно при необходимости привести к типу `JSONB` налету: `SELECT (payload::jsonb)->>'key' WHERE content_type = 'application/json'`.</div>
- <div>**Минусы:**</div>  
    
    - <div>**Изображения придется кодировать в Base64** перед сохранением из C#.</div>
    - <div>Кодирование в Base64 увеличивает объем хранимого файла (изображения) примерно на **33%**.</div>

</div>#### Сравнение вариантов

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%9A%D1%80%D0%B8%D1%82%D0%B5%D1%80%D0%B8%D0%B9-bytea-text-" inline-copy-host=""><table data-path-to-node="14"><thead><tr><td>**Критерий**</td><td>**BYTEA**</td><td>**TEXT (с Base64 для фото)**</td></tr></thead><tbody><tr><td><span data-path-to-node="14,1,0,0">**Универсальность**</span></td><td><span data-path-to-node="14,1,1,0">100% (любые бинарники и текст)</span></td><td><span data-path-to-node="14,1,2,0">100% (но изображения нужно кодировать)</span></td></tr><tr><td><span data-path-to-node="14,2,0,0">**Размер изображений**</span></td><td><span data-path-to-node="14,2,1,0">Исходный (1:1)</span></td><td><span data-path-to-node="14,2,2,0">+33% к размеру файла из-за Base64</span></td></tr><tr><td><span data-path-to-node="14,3,0,0">**Удобство просмотра JSON/XML в СУБД**</span></td><td><span data-path-to-node="14,3,1,0">Требуется `encode(payload, 'escape')`</span></td><td><span data-path-to-node="14,3,2,0">Читается из «коробки»</span></td></tr><tr><td><span data-path-to-node="14,4,0,0">**Сложность в C# (Npgsql)**</span></td><td><span data-path-to-node="14,4,1,0">Работа через `byte[]`</span></td><td><span data-path-to-node="14,4,2,0">Работа через `string`</span></td></tr></tbody></table>

</div>#### Рекомендация

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%95%D1%81%D0%BB%D0%B8-%D0%B8%D0%B7%D0%BE%D0%B1%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9-%D0%BC%D0%BD%D0%BE" inline-copy-host="">1. <div>Если **изображений много или они большие** — однозначно используйте **`BYTEA`**.</div>
2. <div>Если хранение изображений — редкий случай, а **JSON/XML нужно часто просматривать напрямую в БД** — используйте **`TEXT`** (сохраняя картинки как Base64-строки).</div>
3. <div>**Обязательно добавьте колонку `content_type`** (MIME-тип) или `payload_kind` — без нее приложение на C# не сможет понять, как правильно интерпретировать вычитанные данные (как раскодировать JSON/XML или как отдать картинку).</div>

</div>##### Как работать с `BYTEA` из C# (Npgsql):

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--2" inline-copy-host=""></div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-201 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="11" id="bkmrk-c%23" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8yZDZmNDk3MTI4ZGI3YTRlIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19jNzg5MDkyYTE4Y2Y1ZGNjIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-201"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-201"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-201 ng-star-inserted"><span class="ng-tns-c2344738136-201">C#</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-201 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
// Для JSON/XML кодируем строку в UTF-8 байты:
byte[] jsonBytes = System.Text.Encoding.UTF8.GetBytes(jsonString);

// Для изображений берем байты файла напрямую:
byte[] imageBytes = await System.IO.File.ReadAllBytesAsync("photo.png");

// Передача в NpgsqlParameter:
cmd.Parameters.Add(new NpgsqlParameter("payload", NpgsqlTypes.NpgsqlDbType.Bytea) { Value = imageBytes });

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-201 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="11" id="bkmrk--3" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl8yZDZmNDk3MTI4ZGI3YTRlIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19jNzg5MDkyYTE4Y2Y1ZGNjIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-201"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-201"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--4" inline-copy-host=""></div>

# Запрос данных из пакетов JSON в поле таблицы

Для хранения пакетов используем такую таблицу:

[![upload_data.png](https://docs.consulting-plus.com/uploads/images/gallery/2026-09/scaled-1680-/upload-data.png)](https://docs.consulting-plus.com/uploads/images/gallery/2026-09/upload-data.png)

Пакеты хранятся в поле pack\_data, они одинаковой структуры для определенного типа записи (поле type\_id)

Пакет имеет такую структуру:

```json
[
  {"charsetinfo": 
    {"name": "utf-8"}
  },
  {"history": 
    [
      {"BOARDID": "TQBR", "TRADEDATE": "2026-09-21", "SHORTNAME": "РоссЮг ао", "SECID": "MRKY", "NUMTRADES": 275, "VALUE": 1541379, "OPEN": 0.07065, "LOW": 0.0695, "HIGH": 0.0716, "LEGALCLOSEPRICE": 0.07015, "WAPRICE": 0.0704, "CLOSE": 0.07055, "VOLUME": 21890000, "MARKETPRICE2": 0.0702, "MARKETPRICE3": 0.0702, "ADMITTEDQUOTE": null, "MP2VALTRD": 766668, "MARKETPRICE3TRADESVALUE": 766668, "ADMITTEDVALUE": null, "WAVAL": 0, "TRADINGSESSION": 3, "CURRENCYID": "SUR", "TRENDCLSPR": 0.21, "TRADE_SESSION_DATE": "2026-09-21"},{"BOARDID": "TQBR", "TRADEDATE": "2026-09-21", "SHORTNAME": "Мостотрест", "SECID": "MSTT", "NUMTRADES": 162, "VALUE": 1539552, "OPEN": 69.4, "LOW": 67.9, "HIGH": 69.4, "LEGALCLOSEPRICE": 67.9, "WAPRICE": 68.5, "CLOSE": 68.35, "VOLUME": 22470, "MARKETPRICE2": 68.45, "MARKETPRICE3": 68.45, "ADMITTEDQUOTE": null, "MP2VALTRD": 1292162.5, "MARKETPRICE3TRADESVALUE": 1292162.5, "ADMITTEDVALUE": null, "WAVAL": 0, "TRADINGSESSION": 3, "CURRENCYID": "SUR", "TRENDCLSPR": -1.51, "TRADE_SESSION_DATE": "2026-09-21"},
        ...
     ]
  }
]
```

Нам нужно соединить все пакеты и получить записи из поля "history".

Чтобы развернуть массив из массива `history` во всех JSON-пакетах одной SQL-выборкой, в PostgreSQL используются функции для работы с JSON/JSONB:

<div _ngcontent-ng-c4294887596="" class="container" id="bkmrk-%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5-%D1%82%D0%B8%D0%BF%D0%B0%3A-%D0%A2%D0%B0%D0%BA"><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" inline-copy-host="">1. **Приведение типа:** Так как поле `pack_data` имеет тип `text`, его нужно привести к `jsonb` (или `json`): `pack_data::jsonb`.
2. **`jsonb_array_elements`:** Позволяет «расплющить» (распаковать) элементы JSON-массива в отдельные строки.

</div></div>#### SQL-запрос

Поскольку yаш JSON представляет собой массив объектов, где массив `history` находится во **втором элементе** (`pack_data::jsonb -> 1 -> 'history'`), запрос будет выглядеть так:

<div _ngcontent-ng-c4294887596="" class="container" id="bkmrk--1"><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" inline-copy-host=""></div></div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-202 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="3" id="bkmrk-sql" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9iMjdmOTI2NjkzOTU5OGQyIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19hMDlmZDdlNjU5MGQzNzI5IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-202"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-202"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-202 ng-star-inserted"><span class="ng-tns-c2344738136-202">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-202 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```sql
SELECT u.upload_id,
       u.pack_name,
       u.upload_time,
       -- Извлекаем поля из объектов внутри history
       history_item ->> 'SECID' AS secid,
       history_item ->> 'SHORTNAME' AS shortname,
       history_item ->> 'TRADEDATE' AS tradedate,
       (history_item ->> 'OPEN')::numeric AS open_price,
       (history_item ->> 'CLOSE')::numeric AS close_price,
       (history_item ->> 'VOLUME')::bigint AS volume,
       (history_item ->> 'VALUE')::numeric AS value,
       history_item ->> 'BOARDID' AS boardid
  FROM trading.upload_data u
 WHERE u.type_id = 1
 -- 1. Извлекаем массив history из второго элемента корня JSON и разворачиваем его по строкам
 CROSS JOIN LATERAL jsonb_array_elements(u.pack_data::jsonb -> 1 -> 'history') AS history_item;

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-202 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="3" id="bkmrk--2" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9iMjdmOTI2NjkzOTU5OGQyIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19hMDlmZDdlNjU5MGQzNzI5IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-202"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-202"></div></div></div><div _ngcontent-ng-c4294887596="" class="container" id="bkmrk--3"><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" inline-copy-host=""></div></div>#### Как это работает:

<div _ngcontent-ng-c4294887596="" class="container" id="bkmrk-u.pack_data%3A%3Ajsonb--"><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" inline-copy-host="">1. **`u.pack_data::jsonb -> 1`** — берет второй элемент корневого JSON-массива (индексация с 0, поэтому `1` указывает на объект `{"history": [...]}`).
2. **`-> 'history'`** — переходит непосредственно к массиву цен и сделок.
3. **`jsonb_array_elements(...)`** — превращает каждый элемент массива `history` в отдельную строку результата.
4. **`CROSS JOIN LATERAL`** — связывает каждую строку из таблицы `upload_data` с каждым распакованным элементами из массива `history`.
5. **`history_item ->> 'FIELD_NAME'`** — извлекает текстовое значение конкретного ключа из JSON-объекта.
6. **`::numeric`, `::bigint`** — сразу приводит текстовые значения JSON к правильным математическим типам PostgreSQL для последующей фильтрации или агрегации.

</div></div>#### Альтернатива: Если порядок элементов в корневом JSON может меняться

Если элемент `history` не всегда гарантированно идет вторым (с индексом `1`), безопаснее сначала распаковать корневой массив, а затем найти элемент с ключом `history`:

<div _ngcontent-ng-c4294887596="" class="container" id="bkmrk--4"><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" inline-copy-host=""></div></div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-203 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="6" id="bkmrk-sql-1" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9iMjdmOTI2NjkzOTU5OGQyIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19hMDlmZDdlNjU5MGQzNzI5IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-203"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-203"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-203 ng-star-inserted"><span class="ng-tns-c2344738136-203">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-203 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```sql
SELECT u.upload_id,
       history_item ->> 'SECID' AS secid,
       history_item ->> 'SHORTNAME' AS shortname,
       (history_item ->> 'CLOSE')::numeric AS close_price
  FROM trading.upload_data u
 WHERE u.type_id = 1
 -- Распаковываем корневой массив
 CROSS JOIN LATERAL jsonb_array_elements(u.pack_data::jsonb) AS root_elem
 -- Распаковываем массив history (игнорируя элементы без этого ключа, например charsetinfo)
 CROSS JOIN LATERAL jsonb_array_elements(root_elem -> 'history') AS history_item;

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-203 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="6" id="bkmrk--5" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9iMjdmOTI2NjkzOTU5OGQyIiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY19hMDlmZDdlNjU5MGQzNzI5IixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-203"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-203"></div></div></div><div _ngcontent-ng-c4294887596="" class="container" id="bkmrk--6"><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" inline-copy-host=""></div></div>Этот вариант гибче, так как найдет массив `history` в любом месте структуры пакета.

### Что делает оператор: -&gt;&gt;

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="polite" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%9E%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D0%BE%D1%80--%3E%3E-%D0%B2-postg" inline-copy-host=""><div>Оператор **`->>`** в PostgreSQL используется для **извлечения значения из JSON или JSONB в виде обычного текста (`text`)**.</div>  
<div>В PostgreSQL есть два похожих оператора для работы с JSON, и разница между ними принципиальна:</div>  
<table data-path-to-node="2"><thead><tr><td>**Оператор**</td><td>**Что делает**</td><td>**Возвращаемый тип**</td><td>**Назначение**</td></tr></thead><tbody><tr><td><span data-path-to-node="2,1,0,0">**`->`**</span></td><td><span data-path-to-node="2,1,1,0">Извлекает поле или элемент массива</span></td><td><span data-path-to-node="2,1,2,0">**`json`** или **`jsonb`**</span></td><td><span data-path-to-node="2,1,3,0">Для дальнейшего навигационного продвижения вглубь структуры JSON</span></td></tr><tr><td><span data-path-to-node="2,2,0,0">**`->>`**</span></td><td><span data-path-to-node="2,2,1,0">Извлекает поле или элемент массива</span></td><td><span data-path-to-node="2,2,2,0">**`text`**</span></td><td><span data-path-to-node="2,2,3,0">Для вывода конечных данных, фильтрации в `WHERE` или приведения типов</span></td></tr></tbody></table>

</div>#### Примеры применения

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="polite" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%9F%D1%80%D0%B5%D0%B4%D0%BF%D0%BE%D0%BB%D0%BE%D0%B6%D0%B8%D0%BC%2C-%D1%83-%D0%B2%D0%B0%D1%81-%D0%B5" inline-copy-host=""><div>Предположим, у вас есть переменная или колонка `data` с содержимым `{"user": {"name": "Иван", "age": 25}}`.</div>  
</div>##### 1. Извлечение поля по ключу

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="polite" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--7" inline-copy-host=""></div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-204 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="5" id="bkmrk-sql-2" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9iNzM4ODQ5NmQzZTE1NmQ3IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zMjZlOTc3NDFhYmYyNGQyIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-204"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-204"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-204 ng-star-inserted"><span class="ng-tns-c2344738136-204">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-204 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
-- Оператор -> возвращает JSON ("Иван" в кавычках)
SELECT data -> 'user' -> 'name'; 
-- Результат: "Иван" (тип jsonb)

-- Оператор ->> возвращает чистый текст (без кавычек)
SELECT data -> 'user' ->> 'name'; 
-- Результат: Иван (тип text)

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-204 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="5" id="bkmrk--8" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9iNzM4ODQ5NmQzZTE1NmQ3IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zMjZlOTc3NDFhYmYyNGQyIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-204"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-204"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="polite" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--9" inline-copy-host=""></div>##### 2. Извлечение элемента из JSON-массива по индексу

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="polite" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%95%D1%81%D0%BB%D0%B8-%D0%B2-%D0%BA%D0%BE%D0%BB%D0%BE%D0%BD%D0%BA%D0%B5-%D0%BD%D0%B0%D1%85%D0%BE%D0%B4" inline-copy-host=""><div>Если в колонке находится массив `["apple", "banana", "cherry"]`:</div>  
</div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-205 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="8" id="bkmrk-sql-3" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9iNzM4ODQ5NmQzZTE1NmQ3IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zMjZlOTc3NDFhYmYyNGQyIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-205"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-205"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-205 ng-star-inserted"><span class="ng-tns-c2344738136-205">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-205 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
SELECT data ->> 0; 
-- Результат: apple (тип text)

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-205 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="8" id="bkmrk--10" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9iNzM4ODQ5NmQzZTE1NmQ3IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zMjZlOTc3NDFhYmYyNGQyIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-205"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-205"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="polite" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--11" inline-copy-host=""></div>##### 3. Приведение типов для расчетов и фильтрации

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="polite" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%9F%D0%BE%D1%81%D0%BA%D0%BE%D0%BB%D1%8C%D0%BA%D1%83--%3E%3E-%D0%B2%D0%BE%D0%B7%D0%B2%D1%80%D0%B0" inline-copy-host=""><div>Поскольку `->>` возвращает обычный `text`, результат можно легко привести к числу, дате или логическому типу через `::type`:</div>  
</div><div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-206 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="11" id="bkmrk-sql-4" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9iNzM4ODQ5NmQzZTE1NmQ3IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zMjZlOTc3NDFhYmYyNGQyIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-206"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-206"><div _ngcontent-ng-c2344738136="" class="code-block-decoration header-formatted gds-emphasized-body-m ng-tns-c2344738136-206 ng-star-inserted"><span class="ng-tns-c2344738136-206">SQL</span><div _ngcontent-ng-c2344738136="" class="buttons ng-tns-c2344738136-206 ng-star-inserted"><button aria-label="Download code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button><button aria-label="Copy code" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-badge mat-unthemed _mat-animation-noopable mat-badge-overlap mat-badge-above mat-badge-after mat-badge-small mat-badge-hidden ng-star-inserted"></button></div></div></div></div></div>```
-- Извлекаем возраст как число для условия в WHERE
SELECT * 
FROM users_table 
WHERE (data ->> 'age')::int >= 18;

-- Извлекаем дату и делаем сортировку
SELECT data ->> 'TRADEDATE' AS trade_date
FROM trading.upload_data
ORDER BY (data ->> 'TRADEDATE')::date DESC;

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-206 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="11" id="bkmrk--12" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl9iNzM4ODQ5NmQzZTE1NmQ3IiwiY19lNDNjNmQwYjM1YTFjZTk1IixudWxsLCJyY18zMjZlOTc3NDFhYmYyNGQyIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-206"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-206"></div></div></div><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="polite" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--13" inline-copy-host=""></div>#### Чек-лист для запоминания

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="polite" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%95%D1%81%D0%BB%D0%B8-%D0%B2%D1%8B-%D1%85%D0%BE%D1%82%D0%B8%D1%82%D0%B5-%D0%BF%D1%80%D0%BE%D0%B4%D0%BE" inline-copy-host="">- <div>Если вы хотите **продолжить цепочку** погружения в JSON дальше — используйте одиночную стрелку: `data -> 'a' -> 'b'`.</div>
- <div>На **самом последнем шаге**, когда нужно получить конечное значение в виде текста для вывода, используйте двойную стрелку: `data -> 'a' ->> 'b'`.</div>

</div><div _ngcontent-ng-c4294887596="" class="container" id="bkmrk--14"><div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="off" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk--15" inline-copy-host=""></div></div>

# Работа с Postgres SQL

# Переключение текущей сессии в нужную схему.

Допустим вам нужно сделать схему **main** текущей.

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

```postgresql
SET search_path TO main, public;
```

> **Как это работает:** Любые команды `CREATE TABLE qrtz_...` без явного указания схемы теперь будут создавать таблицы строго внутри `main`.

#### Проверьте переключение

Чтобы убедиться, что контекст изменился на `main`, выполните:

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-634 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="0" data-ved="0CAAQhtANahgKEwjxq6-G55CXAxUAAAAAHQAAAAAQjgo" decode-data-ved="1" id="bkmrk-sql" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl85OGU2Mjg4ZGE3NzNiZGFlIiwiY19jZTI4YjI2MDZhYmQwMzk2IixudWxsLCJyY19kMGQzNjI1ZDgxMTdhYTFmIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"></div>```
SHOW search_path;
SELECT current_schema();

```

<div _ngcontent-ng-c2344738136="" class="code-block ng-tns-c2344738136-634 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" data-hveid="0" data-ved="0CAAQhtANahgKEwjxq6-G55CXAxUAAAAAHQAAAAAQjgo" decode-data-ved="1" id="bkmrk--1" jslog="223238;track:impression,attention;BardVeMetadataKey:W1sicl85OGU2Mjg4ZGE3NzNiZGFlIiwiY19jZTI4YjI2MDZhYmQwMzk2IixudWxsLCJyY19kMGQzNjI1ZDgxMTdhYTFmIixudWxsLG51bGwsInJ1IixudWxsLDEsbnVsbCxudWxsLDEsMF1d"><div _ngcontent-ng-c2344738136="" class="formatted-code-block-internal-container ng-tns-c2344738136-634"><div _ngcontent-ng-c2344738136="" class="animated-opacity ng-tns-c2344738136-634"></div></div></div>*В ответе должно быть `main`.*

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="polite" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%A3%D0%BA%D0%B0%D0%B7%D0%B0%D0%BD%D0%B8%D0%B5-%D0%B4%D0%B2%D1%83%D1%85-%D1%81%D1%85%D0%B5%D0%BC-%D0%BE" inline-copy-host=""><div>Указание двух схем определяет **порядок поиска объектов и резервный контекст**.</div>  
<div>Когда вы пишете `SET search_path TO main, public;`, вы задаете списку схем приоритет слева направо:</div>  
1. <div>**При создании объектов (`CREATE TABLE ...`)**</div>  
    <div>PostgreSQL всегда создает новый объект в **первой** схеме списка (в данном случае `main`), если вы не указали схему явно (например, `CREATE TABLE public.my_table`).</div>
2. <div>**При поиске и чтении данных (`SELECT * FROM my_table`)**</div>  
    <div>PostgreSQL ищет таблицу по очереди:</div>  
    
    - <div>Сначала проверяет, есть ли `my_table` в схеме `main`.</div>
    - <div>Если не находит — ищет её в схеме `public`.</div>
    - <div>Если её нет и там — выдает ошибку `relation "my_table" does not exist`.</div>
3. <div>**Доступ к системным функциям и расширениям**</div>  
    <div>В схеме `public` часто располагаются стандартные расширения (extensions), служебные функции или общие справочники базы данных. Указав `public` вторым номером, вы гарантируете, что PostgreSQL сможет найти эти системные функции, даже если вы работаете в `main`.</div>

</div>##### Что будет, если указать только `main`?

<div _ngcontent-ng-c2589024448="" aria-busy="false" aria-live="polite" class="markdown markdown-main-panel md-content enable-luminous-fast-follows enable-updated-hr-color" dir="ltr" id="bkmrk-%D0%95%D1%81%D0%BB%D0%B8-%D0%BD%D0%B0%D0%BF%D0%B8%D1%81%D0%B0%D1%82%D1%8C-set-se" inline-copy-host=""><div>Если написать `SET search_path TO main;`:</div>  
- <div>Все таблицы создадутся в `main` (как и нужно).</div>
- <div>Но если вашей программе или функции потребуется объект или расширение из `public`, PostgreSQL его не увидит и выдаст ошибку.</div>

<div>Поэтому запись `SET search_path TO main, public;` является стандартом: **новые объекты создаются в `main`, но старые и системные объекты из `public` остаются доступны для чтения**.</div></div>