Ответы пользователя по тегу PostgreSQL
  • Где может быть ошибка в SQL запросе?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Причина очень проста. Формально comma-style join - это алиас CROSS JOIN. Но есть подвох - приоритет comma-style join ниже приоритета explicit join. И выражение источника данных, если расставить скобки в соответствии с приоритетом, получится такое:

    from
    table1 , ( table2
               left join table1 h on table1.id = h.id
               left join table2 s on table2.id = s.id )

    Вот теперь прекрасно видно, что внутри скобки о существовании где-то там снаружи table1 ничего не известно.

    Правильно - забыть НАВСЕГДА о возможности использовать запятую. И писать вот так:
    FROM table1 
    CROSS JOIN table2
    LEFT JOIN table1 h ON table1.id = h.id
    LEFT JOIN table2 s ON table2.id = s.id


    PS. Если всё же без comma-style жизнь не мила, можно поступить так, как обычно делают для явного задания приоритета. То есть добавить задающие приоритет скобки:
    FROM (table1 , table2)
    LEFT JOIN table1 h ON table1.id = h.id
    LEFT JOIN table2 s ON table2.id = s.id

    Теперь всё в порядке - сначала выполнится запятая, а потом к результату будут LEFT JOIN остальные две таблицы.
    Ответ написан
    1 комментарий
  • Как в PosgreSQL, в JSON добавить key:value?

    @Akina
    Сетевой и системный админ, SQL-программист.
    https://www.postgresql.org/docs/current/functions-...

    Оператор конкатенации JSONB - ||

    В более сложных случаях - jsonb_set(), jsonb_insert()... в общем, посмотри сам.
    Ответ написан
    Комментировать
  • Как выбрать дату последней транзакции (чтоб эта транзакция была 7 дней назад)?

    @Akina
    Сетевой и системный админ, SQL-программист.
    SELECT id AS servie_id,
           name AS servie_name,
           MAX(TO_TIMESTAMP(created_at))::DATE AS last_trans_date
    FROM services
    GROUP BY 1,2
    HAVING last_trans_date <= CURRENT_DATE - INTERVAL '7 day'
    Ответ написан
    Комментировать
  • Где ошибка в EXECUTE FORMAT?

    @Akina
    Сетевой и системный админ, SQL-программист.
    CREATE OR REPLACE FUNCTION _Foo2(st TIMESTAMP, fin TIMESTAMP)
    RETURNS TABLE (out_key INTEGER, out_tst timestamptz, out_val FLOAT) AS $$
    DECLARE  
      
    BEGIN
    
      RETURN QUERY EXECUTE FORMAT('
      SELECT %I, %I, %I
      FROM %I 
      WHERE %I BETWEEN ''%s'' AND ''%s'';
      ', 
      'in_key', 'in_tst', 'in_val', 'in_table', 'in_tst', st, fin);
    
    END;
    
    $$ LANGUAGE plpgsql;


    DEMO fiddle

    Ошибки найдёте самостоятельно...
    Ответ написан
    1 комментарий
  • Как заставить работать COUNT с GROUP BY?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Надо подсчитать количество строк в ответе запроса вида

    Вариантов несколько.

    Первый, наиболее правильный - получить это значение отдельным запросом.
    SELECT COUNT(*)  AS cnt
    FROM (
        SELECT 1
        FROM "order" AS o
             LEFT JOIN product p ON (p.order_id = o.id)
        GROUP BY o.id
        HAVING sum(p.price) >= 10
    ) x


    Второй - используя средства языка программирования или библиотеки доступа, получить количество записей в наборе записей. Обычно подобное свойство у рекордсета присутствует.

    Третий - добавить соотв. поле в каждую запись выходного набора, используя оконную функцию, и взять значение из любой записи:
    SELECT o.id, COUNT(o.id) OVER () AS cnt
    FROM "order" AS o
             LEFT JOIN product p ON (p.order_id = o.id)
    GROUP BY o.id
    HAVING sum(p.price) >= 10
    Ответ написан
  • PostgreSQL как выбрать всех у кого день рожденья 16 февраля?

    @Akina
    Сетевой и системный админ, SQL-программист.
    SELECT *
    FROM table
    WHERE to_char(dob, 'MMDD') = '0216'
    Ответ написан
  • Как получить 100 записей, после записи с определённым uuid?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Использовать 2 копии таблицы. Одну для получения даты по заданному UUID, вторую для выборки записей.
    SELECT t1.*
    FROM table t1
    JOIN table t2 ON t2.uuid = @uuid1 
                 AND t1.createdAt > t2.createdAt
    ORDER BY createdAt ASC LIMIT 100
    Ответ написан
    Комментировать
  • Как задать валидатор для поля json/jsonb?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Конвертируешь в строку (jsonb -> text of json) и проверяешь регуляркой.
    select jsonb_value,
           jsonb_value::text ~ '^{"en": "[^"]+", "ru": "[^"]+"}$' 
    from test

    https://dbfiddle.uk/?rdbms=postgres_12&fiddle=afc3...

    Если поле не JSONB, а JSON, то дополнительно конвертируешь: https://dbfiddle.uk/?rdbms=postgres_12&fiddle=2bd6...
    Ответ написан
    5 комментариев
  • Как защититься от двойного списания в многопоточном приложении?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Трата состоит из трех этапов. Чтение баланса пользователя, проверка хватает ли средств и собственно запись нового баланса.

    Странный подход. Достаточно на таблицу наложить ограничение неотрицательности баланса и безусловно выполнять операцию. Если средств недостаточно, ограничение сработает, сервер не выполнит изменения данных и вернёт ошибку. Один запрос, и никаких проблем с параллельным исполнением при правильно выбранном уровне изоляции по умолчанию.

    баланс юзера расчитывается на лету и не хранится в юзере. Расчитывсется на основе истории его пополнений/расходов.

    В этом случае - триггер, который посчитает актуальный баланс, проверит условие, и при недостатке средств сгенерирует ошибку. Хотя я бы всё же задумался о хранении актуального баланса, хотя бы на некий момент времени (скажем, на полночь, чтобы доподсчитывать только по операциям текущего дня, а не по всей истории). Редкие ошибки в нём, даже если их не удастся избежать, обойдутся дешевле, чем система, способная каждый раз считать баланс на лету и разводить конфликты.

    Если в многопоточном приложении произойдет так что два потока прочитают баланс, проверят хватает ли средств на покупку и этот этап пройдет успешно для обоих, то далее последуют две записи, которые загонят юзера в минус. Какие есть варианты защиты от двойной траты?

    Как раз уровень изоляции. При правильно выбранном уровне хрен чего второй прочитает, пока первый не завершит свою транзакцию и не отпустит ресурсы.
    Ответ написан
    1 комментарий
  • Как вывести значение поля до выполнения UPDATE, вместе со значением после UPDATE?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Добавь в источник данных копию изменяемой таблицы, связанную по первичному ключу или иному уникальному полю/выражению, и оттуда возьми исходное значение.

    DEMO
    Ответ написан
    Комментировать
  • Postgre Sql DISTINCT массив с ORDER BY?

    @Akina
    Сетевой и системный админ, SQL-программист.
    DISTINCT - это фактически частная форма GROUP BY. А потому к ней относятся все ограничения для GROUP BY в части использования полей источника данных в оконных функциях, выражениях пост-отбора и сортировки.

    Посему используйте поле выходного набора для сортировки.
    SELECT DISTINCT (regexp_split_to_array(commend, E'\\s+'))[:3] AS first_3_words
    FROM orders 
    ORDER BY first_3_words[2];
    Ответ написан
  • Как спроектировать базу данных слов?

    @Akina
    Сетевой и системный админ, SQL-программист.
    В голове идея создать varchar ячейку и в неё через запятую всё записывать.

    С точки зрения работы с СУБД хранение массива данных в виде CSV-значения - крайне неудачный вариант. И весьма проблемный в обработке. Впрочем, если речь идёт о достаточно статичном массиве данных в сотню записей - вариант допустимый.

    Нормализованная структура, удобная в обработке, может выглядеть, например, так (само собой, это только кусок для хранения слов и фраз):
    CREATE TABLE words (
        word_id INT AUTO_INCREMENT PRIMARY KEY,
        word VARCHAR(255) UNIQUE NOT NULL
    );
    
    CREATE TABLE phrase (
        phrase_id INT NOT NULL,
        FOREIGN KEY (phrase_id) REFERENCES phrases (phrase_id),
        word_id INT NOT NULL,
        FOREIGN KEY (word_id) REFERENCES words (word_id),
        word_position INT,
        PRIMARY KEY (phrase_id, word_position)
    );
    Ответ написан
  • Как перемешать символы в сроке?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Ну так перемешай...
    SELECT string_agg(concat(substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ', ceil(random() * 26)::integer, 1),
                             substring('abcdefghijklmnopqrstuvwxyz', ceil(random() * 26)::integer, 1),
                             substring('0123456789', ceil(random() * 10)::integer, 1),
                             substring('!#$%&()*+,-./:;<=>?@[]^', ceil(random() * 23)::integer, 1)
                             ),
                      '' ORDER BY RANDOM()
                      ) 
    FROM generate_series(1,8);
    Ответ написан
  • Postgresql, как получить строки таблицы, попадющие под интервалы записанные в таблице с интервалами?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Полное ощущение, что нужно нечто типа
    SELECT [DISTINCT] t.*
    FROM t
    JOIN t_intervals ti ON t.sequence BETWEEN ti.start AND ti.end
    WHERE ti.id in (2,10,30, ..., n)

    Если интервалы в t_intervals гарантированно не пересекаются (хотя я лично фиг знает как это организовать в виде констрейнтов, а триггерная логика - ниачём), то DISTINCT не требуется.
    Ответ написан
    Комментировать
  • Как задать переменную а потом использовать ее в запросе в Postgres?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Используйте CTE - туда слона запихнуть можно, и, если надо, то и не одного, хоть скалярно, хоть набор записей...

    Типа (игнорируя синтаксис, только принцип):
    WITH
    some_data AS ( SELECT ARRAY(1,2,3) AS some_value )
    SELECT * 
    FROM some_table
    CROSS JOIN some_data
    WHERE some_table.some_field NOT IN some_data.some_value


    К тому же такое решение вполне себе переносимое.
    Ответ написан
    1 комментарий
  • Почему сломались отчеты после миграции 1С с mssql на postgresql?

    @Akina
    Сетевой и системный админ, SQL-программист.
    SQL Server и PostgreSQL - две разные СУБД. И соответственно два разных диалекта (да, много общего, но и различий хватает). Запросы, работающие на одной СУБД, запросто могут поломаться при выполнении в другой СУБД - как совсем, до неисполнения и ошибки, так и частично, давая неверные результаты. Причём что-то легко адаптируется, а что-то не адаптируется вообще никак, требуя полного переписывания с нуля.
    Ответ написан
  • Какой формат для pg_dump порекомендуете?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Чем более "непохожи" серверы, тем проще должен быть формат дампа.

    Но начинать надо в любом случае просто с проверки совместимости. Сделайте тест-БД, пара таблиц с FK, одна функция, одна процедура, один триггер... потом забэкапьте и в скриптовый, и в архивный форматы, и проверьте, как на конкретно Вашем хосте пройдёт восстановление.
    Ответ написан
    1 комментарий
  • Позволяют ли партишены делать такое?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Можно ли создать партишен ... куда бы попадали данные только за последние 365 дней

    Нет, динамические границы раздела - такое в принципе невозможно.

    В документации показаны примеры. Когда создается базовая таблица table_base и дальше от нее создаются партишены
    Не лезьте в высокие материи, не зная основ. Начните с самого обычного RANGE PARTITIONING. Одна таблица - куча разделов, и никаких надтаблиц.
    Ответ написан
  • PostgreSQL: как дождаться ввода пароля при запуске скрипта?

    @Akina
    Сетевой и системный админ, SQL-программист.
    Сначала запросите пароль в переменную, а потом запускайте, передавая пароль из переменной.

    @echo off
    cls
    SET /P psqlpassword="Введите пароль (Enter для завершения): "
    if "%psqlpassword%"=="" exit /b
    "C:\Program Files\PostgreSQL\13\bin\psql.exe" -U postgres -W %psqlpassword% -f  D:/script.sql -a 
    pause


    Если пароль введён неверно, будет отображено сообщение об ошибке, и выполнение пойдёт дальше.

    Если надо заново запросить пароль, следует использовать проверку на ошибку:

    @echo off
    :execute_psql
    cls
    SET /P psqlpassword="Введите пароль (Enter для завершения): "
    if "%psqlpassword%"=="" exit /b
    "C:\Program Files\PostgreSQL\13\bin\psql.exe" -U postgres -W %psqlpassword% -f  D:/script.sql -a 
    if errorlevel 1 goto :execute_psql
    pause


    Правда, нужно убедиться, что psql.exe её возвращает, что не факт. Также следует убедиться, что он не возвращает ошибки в иных ситуациях, когда повторный запрос пароля и запуск не требуются, или ввести проверку на код ошибки.
    Ответ написан