Ответы пользователя по тегу PostgreSQL
  • Как создать функцию с параметрами в DBeaver (в postgres)?

    @galaxy
    Добавить параметры в существующую функцию с тем же именем нельзя - в PG полиморфизм функций: функции с одним именем, но разными параметрами - разные функции.
    Удаляйте эту, создавайте новую (через SQL: CREATE FUNCTION). Как именно в dbeaver параметры редактировать, не скажу.
    Ответ написан
    Комментировать
  • Как написать рекурсивный запрос?

    @galaxy
    Ну что-то типа такого (выводит для каждой строки с parent_id = null):
    with recursive cat as (
      select id, id top from landcategory where parent_id is null
      union
      select lc.id, top from landcategory lc join cat on (cat.id = lc.parent_id)
    )
    select lc.*, childs from landcategory lc join (
      select top, array_agg(id) childs from cat
       group by 1
    ) t on (t.top = lc.id)

    https://www.db-fiddle.com/f/9gQNjVwLn3W11pnCcr8NEs/0
    Ответ написан
    Комментировать
  • Как вставить байт код в PostgreSQL?

    @galaxy
    Ответ, вообще говоря, зависит от ЯП/клиента, который вы используете.
    Ибо вам нужно сначала считать содержимое файла в переменную, а затем вставить ее в запрос тем или иным способом.
    Например, для PHP можно использовать pg_escape_bytea + pg_query_params
    Ответ написан
    Комментировать
  • Блокирует ли процесс построения индексов таблицу?

    @galaxy
    Блокирует ли процесс построения индексов таблицу?

    и тут же
    Перед UPDATE я индекс по нему сделал

    Ну и как сам думаешь?

    Мне потребовалось добавить одно поле
    добавление поля без DEFAULT, допускающего NULL значение, пройдет быстро. Если его надо заполнять (есть DEFAULT, или как вы руками), это уже апдейт на 100 млн строк.

    1. Правильно ли я понимаю что hash индексы не могут использовать сразу все ядра
    1.1 а btree могут?

    Вообще ни при чем ни hash, ни btree индексы.
    В postgres есть только parallel queries - параллельные запросы, которые могут выполняться несколькими потоками. Параллельных апдейтов нет.

    3. Почему UPDATE не связанный с этим полем так тормозит?

    Основной апдейт грузит машину + если другие апдейты пытаются изменить те же строки в таблице, они у вас, скорее всего, не тормозят, а просто отваливаются по таймауту. Ибо строки заблокированы основной бесконечной транзакцией.
    Какое поле изменяется (то же самое или нет), значения не имеет.

    2. Как быть в текущей ситуации. Я понимаю что индекс нужно было после UPDATE делать, но как понять через сколько процесс закончится? Может подождать стоит?

    Прибейте этот апдейт для начала. Ничего страшного не произойдет. Никто вам тут не скажет, сколько еще он будет висеть. Может, он сам ждет какой-то блокировки.
    Потом VACUUM ANALYZE на таблицу.

    Что касается заполнения поля, из вопроса непонятны условия. Вы во всей таблице (или большей ее части) хотите это сделать? Или вот это: where field и LIKE '%pattern%' - выборка все же малого числа строк (сколько примерно?)?
    * вообще-то стоило бы до UPDATE дернуть SELECT с тем же условием и посмотреть, сколько времени уйдет на него*
    Простой индекс не сработает с запросами вида ilike '%pattern%'. Поставьте расширение pg_trgm и сделайте GIN индекс. Опять же, если надо заполнить полтаблицы, индекс не спасет, надо будет разбивать UPDATE на куски.
    Ответ написан
  • Как хранить категории для юзера в postgres?

    @galaxy
    Стандартное отношение many-to-many через таблицу связи
    users
    __________
    id
    name
    ...
    
    categories
    __________
    id
    name
    ...
    
    user_categories
    __________
    user_id
    category_id


    В принципе, если категории просто текстовые теги, можно хранить в поле таблицы users в виде JSON, например.
    Ответ написан
  • Что произойдет, если не сделать коммит транзакции?

    @galaxy
    Откатится в конце сессии (т.е. когда соединение с клиентом будет потеряно) или при перезагрузке сервера.
    Ответ написан
    Комментировать
  • Как работает timezone в php и postgres?

    @galaxy
    Ну для начала пользователь из Владивостока должен как-то сообщить серверу о своей зоне (например, выставив ее в настройках своего профиля).
    Далее, PHP должно распарсить строку с датой (например, с помощью DateTime::createFromFormat), причем использовать зону, настроенную пользователем. Полученную дату (объект DateTime) можно вывести пользователю дату/время в любой временной зоне, сделав DateTime::setTimezone() и DateTime::format().

    БД работает похожим образом: если используется тип TIMESTAMP WITH TIMEZONE, то физически в колонке хранится время в UTC. Клиент (в данном случае приложение PHP) при подключении к БД задает желаемую зону (или не задает, тогда используется зона по умолчанию из настроек сервера БД). В этой зоне клиенту выдаются даты. При желании поле с меткой времени можно перевести в другую зону через констукцию datetime_value AT TIME ZONE zone.

    По идее, БД и PHP у вас используют одну, общую для сервера, зону, и менять это обычно смысла нет. Ваша задача - правильно распарсить даты, присылаемые пользователем (подставив его предпочитаемую зону), а также выводить даты пользователю в его зоне. Проще всего один раз выставить эту зону через date_default_timezone_set()
    Ответ написан
    1 комментарий
  • Почему SQL запрос выполняется с разной скоростью?

    @galaxy
    Когда выполняю этот запрос в SQL то 10 раз он может выполняться по 90 секунд, а на 11 может выполниться за 5 и потом продолжить выполняться за 5.
    ...
    Там какие-то оптимизаторы есть в БД или другая хитрая логика? Просто по прошествии часа история повторяется и запрос опять начинает медленно выполняться.

    ну эта картина неудивительная. Почти любой кеш работает по принципу вытеснения LRU (least recently used) данных. Вы повторяете запрос - ваши данные вытесняют то, что было в кеше. Вы прекращаете - другие запросы вытесняют ваши данные.

    Далее, судя по плану, вам стоит сделать на таблицы ANALYZE (и, может быть, увеличить статистику - ALTER TABLE SET STATISTICS), т.к. реальное число строк и оценка расходятся весьма сильно.

    И, наконец, self-join'а тут можно не делать. Нужен по сути последний по дате документ, так что, с поправкой на составление запросов в уме:
    SELECT * FROM (
         SELECT row_number() OVER (PARTITION BY id ORDER BY mydate DESC) as rn,
              t1.* FROM xml_files t1
             AND t1.insert_status IS NULL
            AND t1.section_name='users' AND t1.region IN ('Moscow') AND t1.xml_date >= to_date('2016', 'YYYY') AND t1.parsing_status IS NULL
         ) t
     WHERE rn = 1
    Ответ написан
  • Как вывести разность двух полей?

    @galaxy
    Ну так и пишите сразу разность:
    select
                    region_code, ...
                    sum(case when status_id = 2 then 1 else 0 end) - sum(case when consul_need_id = 3 then 1 else 0 end) as diff, ...


    Или оберните все в еще один select, а дальше уж делайте, что хотите:
    select t.*,
          "Всего детей",
          "Всего детей" - "18.1",
          "18.1" * "До года жизни" / ("7.1" + "8.1")
          ...
      from (
          select
                    region_code, sum(case when age_group_id < 13 then 1 else 0 end) as "До года жизни",
          ....
      ) t


    Единственное, не понял назначения конструкций (sum(case when ... end),0) - зачем оно вам?
    Ответ написан
    2 комментария
  • Как восстановить базу данных PostgreSQL?

    @galaxy
    Утилита pg_restore предназначена для восстановления базы данных Postgres Pro из архива, созданного командой pg_dump в любом из не текстовых форматов

    https://postgrespro.ru/docs/postgrespro/10/app-pgr...

    У вас дамп в SQL (текстовый формат).
    Нет под рукой pgAdmin4 (да покарает Аллах его создателя), но там где-то должна быть кнопка типа "запустить SQL скрипт", либо "открыть SQL консоль" - оттуда надо восстанавливать.
    Ну или запустите консоль и в ней команду, которую пишет pgAdmin с заменой pg_restore.exe на psql.exe, и перед путем к файлу поставьте <. Примерно:
    C:\Program Files\...\psql.exe --host ... --dbname "demo" < "C:\\Users\\...\\....SQL"
    Ответ написан
    1 комментарий
  • Какой SQL запрос написать?

    @galaxy
    SELECT Company, SUM(CASE WHEN IsDiscounted THEN Price ELSE -Price END) ...
    Ответ написан
    1 комментарий
  • Недостатки PostgreSQL?

    @galaxy
    Самая большая проблема - это архитектура MVCC, которая вызывает распухание таблиц, требует наличие vacuum'инга, и в многих случаях не способствует производительности.

    Но советую все же самому погуглить (для затравки - https://habr.com/ru/company/southbridge/blog/322624/). Будет гораздо проще и полезнее, если вы нагуглите какие-то особенности, а специалисты тут подскажут, насколько они актуальны и критичны.
    Ответ написан
    2 комментария
  • Как организовать структуру хранения атрибутов?

    @galaxy
    Вообще, рассуждать в отрыве от данных достаточно бессмысленно. Надо смотреть конкретные планы запросов и время выполнения.
    Например, для статистики, которую вы приводили тут где-то (похоже, ответ уже потерли), у меня получаются вполне приемлемые результаты (на мой взгляд, не факт, что вас устроит):
    Схема и данные

    create table products (
    	id serial primary key,
    	name text not null
    );
    
    create table products_attributes (
    	id serial primary key,
    	attribute_id int not null,
    	product_id int not null references products (id),
    	value double precision
    );
    
    
    -- 10 млн продуктов
    insert into products (name)
    select 'p' || n from generate_series(1, 10000000) n;
    
    -- в среднем по 10 аттрибутов на продукт
    -- всего 20 разных аттрибутов
    -- значения - случайные из 1-1000
    insert into products_attributes(attribute_id, product_id, value)
    select a,
           p.id,
           trunc(random() * 1000)
      from products p, generate_series(1, 20) a
     where random() < 0.5;
    
    
    create index ix_attr_attribute_id on products_attributes(attribute_id);
    create index ix_attr_product_id on products_attributes(product_id);
    create unique index uk_attr_attr_product on products_attributes(product_id, attribute_id);
    create index ix_attr_value on products_attributes(value);
    
    -- немного статистики
    select attribute_id, count(*) from products_attributes group by 1 order by 1;
    
     attribute_id |  count
    --------------+---------
                1 | 5001345
                2 | 5001937
                3 | 4998754
                4 | 4998706
                5 | 4999357
                6 | 5004465
                7 | 4999215
    ...
    
    select product_id, count(*) from products_attributes group by 1 order by 2 desc limit 20;
     product_id | count
    ------------+-------
        4769292 |    20
        5366802 |    20
        7241348 |    20
        3019891 |    20
        7789046 |    20
        1688646 |    19
        1585970 |    19
    ...
    
    SELECT count(*) FROM products_attributes WHERE attribute_id = 1 AND value BETWEEN 1 AND 400;
      count
    ---------
     1999212
    (1 row)
    
    SELECT count(*) FROM products_attributes WHERE attribute_id = 2 AND value BETWEEN 1 AND 400;
      count
    ---------
     1999385
    (1 row)
    
    SELECT count(*) FROM products_attributes WHERE attribute_id = 3 AND value BETWEEN 20 AND 30;
     count
    -------
     55318
    (1 row)



    explain analyze
    select * from products
     where id in (
       select product_id from products_attributes
        where attribute_id = 1 AND value BETWEEN 1 AND 400
     )
     and id in (
       select product_id from products_attributes
        where attribute_id = 2 AND value BETWEEN 1 AND 400
     )
     and id in (
       select product_id from products_attributes
        where attribute_id = 3 AND value BETWEEN 20 AND 30
     );

    Без лимита, 2189 записей - 3.8 сек:
    План

    QUERY PLAN                          
    -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
     Gather  (cost=164603.86..347381.12 rows=2047 width=12) (actual time=680.688..3753.927 rows=2189 loops=1)
       Workers Planned: 2
       Workers Launched: 2
       ->  Nested Loop  (cost=163603.86..346176.42 rows=853 width=12) (actual time=652.714..3713.617 rows=730 loops=3)
             ->  Nested Loop  (cost=163603.43..341334.66 rows=762 width=12) (actual time=652.439..3294.369 rows=730 loops=3)
                   ->  Parallel Hash Join  (cost=163602.86..313081.39 rows=4167 width=8) (actual time=649.437..1742.210 rows=3689 loops=3)
                         Hash Cond: (products_attributes_1.product_id = products_attributes_2.product_id)
                         ->  Parallel Index Scan using ix_attr_attribute_id on products_attributes products_attributes_1  (cost=0.57..147444.27 rows=775173 width=4) (actual time=0.577..880.079 rows=666462 loops=3)
                               Index Cond: (attribute_id = 2)
                               Filter: ((value >= '1'::double precision) AND (value <= '400'::double precision))
                               Rows Removed by Filter: 1000851
                         ->  Parallel Hash  (cost=163306.17..163306.17 rows=23690 width=4) (actual time=647.483..647.483 rows=18439 loops=3)
                               Buckets: 65536  Batches: 1  Memory Usage: 2752kB
                               ->  Parallel Index Scan using ix_attr_attribute_id on products_attributes products_attributes_2  (cost=0.57..163306.17 rows=23690 width=4) (actual time=18.296..639.541 rows=18439 loops=3)
                                     Index Cond: (attribute_id = 3)
                                     Filter: ((value >= '20'::double precision) AND (value <= '30'::double precision))
                                     Rows Removed by Filter: 1647812
                   ->  Index Scan using uk_attr_attr_product on products_attributes  (cost=0.57..6.78 rows=1 width=4) (actual time=0.420..0.420 rows=0 loops=11066)
                         Index Cond: ((product_id = products_attributes_1.product_id) AND (attribute_id = 1))
                         Filter: ((value >= '1'::double precision) AND (value <= '400'::double precision))
                         Rows Removed by Filter: 0
             ->  Index Scan using products_pkey on products  (cost=0.43..6.35 rows=1 width=12) (actual time=0.572..0.572 rows=1 loops=2189)
                   Index Cond: (id = products_attributes.product_id)
     Planning Time: 4.481 ms
     JIT:
       Functions: 93
       Options: Inlining false, Optimization false, Expressions true, Deforming true
       Timing: Generation 14.220 ms, Inlining 0.000 ms, Optimization 2.852 ms, Emission 50.889 ms, Total 67.961 ms
     Execution Time: 3762.035 ms
    (29 rows)



    То же, но с лимитом (LIMIT 20):
    Запрос
    explain analyze
    select * from products
     where id in (
       select product_id from products_attributes
        where attribute_id = 1 AND value BETWEEN 1 AND 400
     )
     and id in (
       select product_id from products_attributes
        where attribute_id = 2 AND value BETWEEN 1 AND 400
     )
     and id in (
       select product_id from products_attributes
        where attribute_id = 3 AND value BETWEEN 20 AND 30
     ) limit 20;


    20 записей - 48 мсек:
    spoiler
    QUERY PLAN                                   
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
     Limit  (cost=2.14..7292.14 rows=20 width=12) (actual time=3.743..47.724 rows=20 loops=1)
       ->  Nested Loop  (cost=2.14..746133.39 rows=2047 width=12) (actual time=3.741..47.714 rows=20 loops=1)
             ->  Nested Loop  (cost=1.70..734511.89 rows=1829 width=12) (actual time=3.313..44.710 rows=20 loops=1)
                   ->  Nested Loop  (cost=1.14..666709.45 rows=10000 width=8) (actual time=2.377..42.276 rows=95 loops=1)
                         ->  Index Scan using ix_attr_attribute_id on products_attributes products_attributes_2  (cost=0.57..209246.27 rows=56856 width=4) (actual time=1.250..14.969 rows=511 loops=1)
                               Index Cond: (attribute_id = 3)
                               Filter: ((value >= '20'::double precision) AND (value <= '30'::double precision))
                               Rows Removed by Filter: 46859
                         ->  Index Scan using uk_attr_attr_product on products_attributes products_attributes_1  (cost=0.57..8.05 rows=1 width=4) (actual time=0.053..0.053 rows=0 loops=511)
                               Index Cond: ((product_id = products_attributes_2.product_id) AND (attribute_id = 2))
                               Filter: ((value >= '1'::double precision) AND (value <= '400'::double precision))
                               Rows Removed by Filter: 0
                   ->  Index Scan using uk_attr_attr_product on products_attributes  (cost=0.57..6.78 rows=1 width=4) (actual time=0.025..0.025 rows=0 loops=95)
                         Index Cond: ((product_id = products_attributes_1.product_id) AND (attribute_id = 1))
                         Filter: ((value >= '1'::double precision) AND (value <= '400'::double precision))
                         Rows Removed by Filter: 0
             ->  Index Scan using products_pkey on products  (cost=0.43..6.35 rows=1 width=12) (actual time=0.149..0.149 rows=1 loops=20)
                   Index Cond: (id = products_attributes.product_id)
     Planning Time: 9.959 ms
     Execution Time: 47.796 ms
    (20 rows)


    Как видите, оценки планировщика по селективности адекватные. Начинает он с самого высоко-селективного условия. Лимит проносится внутрь самых глубоких джойнов (видно по количеству записей).
    Ответ написан
    3 комментария
  • Из-за чего ошибка nodejs client password must be a string?

    @galaxy
    pasword:'password',

    точно ничего не замечаете?
    Ответ написан
    Комментировать
  • Как парсить json?

    @galaxy
    Есть тип jsonb, из него извлечь можно так:
    create table t (f jsonb);
    insert into t values ('{"t_in":[0],"t_out":[1]}');
    
    # table t;
                  f
    -----------------------------
     {"t_in": [0], "t_out": [1]}
    
    # select f->'t_in' from t;
     ?column?
    ----------
     [0]
    (1 row)


    Если поле имеет строковый тип:
    select '{"t_in":[0],"t_out":[1]}'::jsonb->'t_in';
    Ответ написан
    1 комментарий
  • Как сформировать произвольные строки данных при выполнении запроса?

    @galaxy
    Не знаю, что именно вам нужно. Следующая функция возвращает произвольную строку длины len из символов алфавита chars:
    create or replace function rand_str(len int, chars text default '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz') returns text language sql as $$
      SELECT string_agg (substr(chars, ceil (random() * length(chars))::integer, 1), '')
      FROM generate_series(1, len)
    $$;


    postgres=# select rand_str(10);
      rand_str
    ------------
     wmIn59AeiS
    (1 row)
    
    postgres=# select rand_str(50, 'абвгдежзиклмнопрстуфхцчшщъыьэюя');
                          rand_str
    ----------------------------------------------------
     дломчяочэатьщсдмллянблнцммюгььжзетшбзвшлежлэщдечют
    (1 row)
    Ответ написан
    Комментировать
  • Как сделать конструкцию IN %(param_name)s необязательным параметром в строке sql если в качестве значения параметра был предоставлен пустой массив?

    @galaxy
    Если не хотите руками собирать запросы (или с помощью модуля sql), можно использовать списки:
    v = {'_id': 10, 'param_name': None}
    c.mogrify("SELECT * FROM table_name WHERE %(param_name)s IS NULL OR id = ANY(%(param_name)s)", v)
    # b'SELECT * FROM table_name WHERE NULL IS NULL OR id = ANY(NULL)'
    
    v = {"_id": 10, "param_name": [1, 2, 3]}
    # b'SELECT * FROM table_name WHERE ARRAY[1,2,3] IS NULL OR id = ANY(ARRAY[1,2,3])'


    Вместо None можно и под пустой список подогнать (немного больше возни):
    v = {"_id": 10, "param_name": []}
    c.mogrify("SELECT * FROM table_name WHERE cardinality(%(param_name)s::int[]) = 0 OR id = ANY(%(param_name)s)", v)
    # b"SELECT * FROM table_name WHERE cardinality('{}'::int[]) = 0 OR id = ANY('{}')"
    Ответ написан
    3 комментария
  • Как задать переменную а потом использовать ее в запросе в Postgres?

    @galaxy
    Специального механизма, как в некоторых других СУБД, для этого нет. Можно, в зависимости от конкретной задачи, использовать:

    1. Анонимные блоки plpgsql
    do $$
    declare
      list int[] := array[2619,1247,4159];
      r text;
    begin
      select string_agg(relname, ',') into r from pg_class where oid = any(list);
      raise notice '%', r;
    end;
    $$;
    
    # NOTICE:  pg_statistic,pg_type,pg_toast_2600

    (вернуть результат запроса из такого блока нельзя, поэтому там raise notice)

    2. Psql переменные
    (разумеется, только если работаете в программе-клиенте psql)
    > \set list 2619,1247,4159
    > select * from pg_class where oid in (:list);


    3. Временная (или даже не временная) таблица. Тут, думаю, примеры не требуются.
    Ответ написан
    Комментировать
  • Как инициировать pg_dump из интерфейса psql?

    @galaxy
    Никак*. Pg_dump выполняет множество разных SQL запросов, чтобы сравнить схему с template0, разрулить зависимости и выгрузить данные.

    * - не считая, конечно, костыля в виде postgres=# \! pg_dump
    Ответ написан
    Комментировать
  • Какой формат для pg_dump порекомендуете?

    @galaxy
    Неважно. Какой удобнее, такой и используйте.
    Customs и directory форматы позволяют восстанавливать объекты выборочно, поддерживают параллельное восстановление и по умолчанию сжаты. Архитектура машины и ОС не имеют значения.
    Проблемы могут быть только при восстановлении на сервере с более старой (мажорной) версией PG, тогда лучше использовать plain (даже в этом случае могут быть трудности). При заливке на новую версию проблем быть не должно.
    Ответ написан
    Комментировать