Ответы пользователя по тегу PostgreSQL
  • Как сделать сортировку рекурсивного запроса в postgresql?

    Melkij
    @Melkij
    PostgreSQL DBA
    Так сравниваются массивы - поэлементно по количеству элементов в меньшем из массивов.
    Если элементы идентичны - сравниваем длины массивов. Поэтому массивы в таком виде вам подходят слабо.

    Может лучше ваш список смежности вовсе заменить на штатное материализованное представление ltree?
    Ну или во всяком случае на него можно заменить ваши массивы.
    Ответ написан
    1 комментарий
  • Как правильно сделать фильтрацию по числу JSONB в PostgreSQL используя индекс?

    Melkij
    @Melkij
    PostgreSQL DBA
    Индекс возможно повесить функциональный:
    create index on tablename using btree(((features ->> 'capacity'::text)::integer));

    Соответственно предикат по этому же самому выражению получит возможность использовать этот индекс.

    Больше возможностей внятно индексировать jsonb для запросов на числовые диапазоны мне как-то не вспоминается. Сортировку по полю-то только btree и умеет из всех актуальных access method.

    Т.е. индекс используется, хотя в данном случае index срабатывает на price столбце? Тогда почему запрос отрабатывает быстро?

    Да, индекс используется - индекс по price. Потому что вы по нему сортируете. И планировщик опираясь на свою статистику надеется, что сможет быстро найти 25 строк читая строки в порядке требуемой сортировки по индексу и по пути проверять выкидывать неподходящие.
    Ответ написан
    2 комментария
  • Как правильно составить запрос для взятия первого значения из группы?

    Melkij
    @Melkij
    PostgreSQL DBA
    Ну а когда станет скучно перебирать всю таблицу - обращайтесь :-)

    Postgresql 9.4 и выше, выбор по известному диапазону дат через index scan по timestamp полю:
    select day, data 
    from generate_series('2017-12-10', '2017-12-12', interval '1 day') as day, 
    lateral (
        select data from tablename 
        where "timestamp" between day and day + interval '1 day' 
        order by "timestamp" desc limit 1
    ) ljd;


    Все дни из таблицы по индексу:
    with recursive t as (
    (select "timestamp"::date as day, data from tablename order by "timestamp" desc limit 1)
    union all
    select bpt.* from t, lateral (
    select "timestamp"::date as day, data from tablename where "timestamp" < t.day order by "timestamp" desc limit 1
    ) as bpt
    )
    select * from t;

    Используя loose index scan
    Ответ написан
    7 комментариев
  • Как в PostgreSQL выбрать значения значения которые есть в массиве, но нет в БД?

    Melkij
    @Melkij
    PostgreSQL DBA
    Всё просто: надо развернуть массив в набор строк и проверить на not exists
    select i from unnest(array[1,2,3,4]) as i 
    where not exists (select 1 from tablename where id = i);
    Ответ написан
    9 комментариев
  • Как подсчитать кол-во вставленных записей в БД?

    Melkij
    @Melkij
    PostgreSQL DBA
    Мне неизвестны штатные средства для этого.

    В качестве обходных фокусов можно поизвращаться вот так:
    with 
    data (new_user_id, new_name) as (values(%(user_id)s, %(name)s)),
    wr as (
        INSERT INTO users(user_id, name)
                    select new_user_id, new_name from data
                    ON CONFLICT (user_id) DO NOTHING returning user_id
    ), upd as (
        update users set name = new_name from data where users.user_id = data.new_user_id and data.new_user_id not in (
        select user_id from wr
        )
    )
    select count(*) from wr;


    Что работает согласно задаче из-за того, что do nothing не осуществляет insert и потому строка не появляется в returning.
    Ответ написан
    Комментировать
  • Стоит ли хранить файлы транзакций отдельно от баз?

    Melkij
    @Melkij
    PostgreSQL DBA
    Имеются в виду pg_xlog (pg_wal в 10) или pg_clog (pg_xact)? Файлы (метаданных) транзакций - это вторые и пишутся они не очень интенсивно. Первый - write ahead log - журнал записи, где отражается вся пишущая активность базы и обычно синхронной записью.

    Производительность дисков для записи WAL - это ключевой фактор для времени commit. Но WAL пишется последовательно и за счёт этого можно держать вполне хорошую нагрузку размещая их монопольно на отдельных HDD, а не SSD. Сэкономив соответственно немного денег на требуемой под базу ёмкости SSD.
    Ну или если у вас записи достаточно много при нормально настроенных checkpointer и bgwriter, чтобы была необходимость выделить отдельные несколько SSD под WAL. Впрочем, у вас в тегах NTFS упомянут. На таком нормальный production всё равно не делают, значит не ваш случай.

    Массивы - RAID10 или RAID1 смотря сколько дисков есть. Для тестовых машин смотрите сами.
    Ответ написан
  • Вставка записи в главную таблицу и подчиненные?

    Melkij
    @Melkij
    PostgreSQL DBA
    Добавить ещё один CTE.
    WITH new_tbl_main( id ) AS (
              INSERT INTO tbl_main ( institution_id ) VALUES ( 38)
                RETURNING id 
    ), chi2 as (
    INSERT INTO tbl_child1 ( tbl_main_id ) 
                SELECT new_tbl_main.id  FROM new_tbl_main
    )
    INSERT INTO tbl_child1 ( tbl_main_id ) 
                SELECT new_tbl_main.id  FROM new_tbl_main
                RETURNING tbl_main_id

    Если нужен returning от всех - то добавить ещё cte и в финале склеить запросы через union all или join или как понравится.
    Ответ написан
    Комментировать
  • Как правильно создать функцию возвращающую булевый результат выполнения селекта?

    Melkij
    @Melkij
    PostgreSQL DBA
    Вероятно при упрощении примера вы выкинули собственно саму причину синтаксической ошибки.
    Потому что приведённая в вопросе хранимка синтаксически корректна, что, впрочем, не изменяет некорректности семантической.
    ERROR: cannot use RETURN QUERY in a non-SETOF function

    Вы не можете использовать return query для возврата одного значений. return query только для функций возвращающих множество. Булево выражение можно посчитать непосредственно в return, как в большинстве других языков:
    CREATE OR REPLACE FUNCTION checkPermission(text, text) RETURNS BOOLEAN AS
    $$
    BEGIN
      RETURN $1 && $2;
    END;
    $$ LANGUAGE plpgsql


    Ну и мелочь лучше записывать на language sql, что позволяет оптимизаторы делать интересные фокусы в целях повышения производительности запросов.
    CREATE OR REPLACE FUNCTION checkPermission(text, text) RETURNS BOOLEAN AS
    $$
      SELECT $1 && $2;
    $$ LANGUAGE sql


    Разумеется, это будет работать только если у вас определён оператор && для пары операндов типа text.
    Ответ написан
    Комментировать
  • Как правильно найти пересечение двух jsonb массивов в postgres?

    Melkij
    @Melkij
    PostgreSQL DBA
    Есть у постгреса оператор &&

    "есть оператор" - это сказано немного сильно преждевременно. Например, на моей тестовой 9.6 таких операторов 8 штук.
    Потому что нет отдельно операторов. Операторы определены только неразрывно с типами данных своих операндов (двух или одного для префиксных и постфиксных операторов)

    select oprkind, l.typname, oprname, r.typname from pg_operator join pg_type l on oprleft = l.oid join pg_type r on oprright = r.oid where oprname = '&&';
     oprkind |  typname  | oprname |  typname  
    ---------+-----------+---------+-----------
     b       | box       | &&      | box
     b       | polygon   | &&      | polygon
     b       | tinterval | &&      | tinterval
     b       | circle    | &&      | circle
     b       | inet      | &&      | inet
     b       | tsquery   | &&      | tsquery
     b       | anyarray  | &&      | anyarray
     b       | anyrange  | &&      | anyrange

    И jsonb здесь явно отсутствует.

    Значит берём CREATE OPERATOR в руки, пара хранимок - и вот у вас новый оператор готов. Это PostgreSQL, он так умеет и позволяет делать.

    Плюс посмотреть в релевантный jsonb раздел документации: https://www.postgresql.org/docs/current/static/fun...
    Но с операциями на массивами в json там немного печально и неудобно.
    Ответ написан
    Комментировать
  • Как использовать huge pages для Postgres запущенного в docker контейнере?

    Melkij
    @Melkij
    PostgreSQL DBA
    Не извращаться и вытащить production базу из докера на нативное железо. Что базе в этом докере делать, если и диск и сеть надо использовать хоста?

    Насколько я помню, ни cgroups ни namespaces не мешают выделенным huge pages. Попробуйте вкрутить vm.nr_hugepages на хост системе и стартовать базу с huge_pages=on
    Ответ написан
    Комментировать
  • Можно ли поставить разные таймзоны в типе datetime with timezone postgresql?

    Melkij
    @Melkij
    PostgreSQL DBA
    timestamp with time zone приводит и хранит данные только в UTC и переводит их в местное время при чтении согласно сессионной настройке timezone.

    https://www.postgresql.org/docs/9.6/static/datatyp...
    For timestamp with time zone, the internally stored value is always in UTC (Universal Coordinated Time, traditionally known as Greenwich Mean Time, GMT). An input value that has an explicit time zone specified is converted to UTC using the appropriate offset for that time zone. If no time zone is stated in the input string, then it is assumed to be in the time zone indicated by the system's TimeZone parameter, and is converted to UTC using the offset for the timezone zone.


    timestamp without time zone вовсе игнорирует указание в литерале временной зоны.

    а других timestamp и нет. Поэтому нет, хранить таймзону (при необходимости в этом) необходимо отдельно. Зато за счёт этого timestamp занимает всего константные 8 байт.
    Ответ написан
    Комментировать
  • Выбрать первую и последнюю запись группы?

    Melkij
    @Melkij
    PostgreSQL DBA
    В смысле вот так?
    select ticket_number , min(some_id), max(some_id) from tablename group by ticket_number
    Ответ написан
  • Pgpool2 multy-master mode где искать мануалы по настройке? Как вы масштабируете базу(под запись)?

    Melkij
    @Melkij
    PostgreSQL DBA
    Как вы масштабируете базу(под запись)?

    В основном никак. У нас нет баз на столько десятков террабайт данных чтобы внятно настроенный единственный мастер с парой обычных потоковых ro-реплик не справлялся.

    Если у вас столько данных пишется что одного мастера действительно мало - то репликация вам не подходит по своему определению, она не про масштабирование записи, а только чтения и high availability (репликация = копия. Копию нельзя сделать, если писать не все данные).

    А те базы что у нас распиливались - распиливались они не потому что в мастер упёрлись:
    Что-то пилится на горячее-архивное: отдельные одна или несколько машин на более дешёвых (медленных) дисках, куда переносятся данные старше какого-то времени, к которым обращаются редко.
    Что-то пилится на части функционально. Например, сайт - один кластер, данные для окружающих некритичных сервисов - другой кластер. Или данные ru-сайта - один кластер, данные нескольких других стран - отдельные базы на другом кластере.

    Ещё можно пилить по каким-нибудь другим динамическим критериям. Например, данные всех пользователей от 1 до 1000 - на один кластер, от 1000 до 2000 - на другой. Плюс пара машин с данными координатора какой id на какой машине и авторизации. Это уже эталонное горизонтальное масштабирование. Крайне редко кому действительно нужно и собирается типично вручную с пониманием что и зачем делается.

    Сильно ли нагружает сервер процес синхронной репликация?

    Железо - не очень. Со стороны приложения кажется что очень. Потому что латентность коммита локально и согласовать коммит с даже хотя бы одной синхронной репликой - вещи очень сильно расходящиеся по латентности.

    чаще всего используеться репликация на уровне приложения

    Репликация чаще всего используется штатная потоковая.
    Запросы от приложения удобнее направлять именно из приложения не из-за сырости прокси, а потому что именно приложению лучше знать, нужно ли запрос отправлять на реплику (и если реплику - то какую, боевую? отдельную для медленных запросов аналитики?) или его вообще можно выполнять только на мастере.

    какие есть угрозы при такой балансировке базы?

    В смысле при мультимастере? Кучу головной боли вам доставит CAP теорема, из-за которой внятного мультимастера нет.
    Самое весёлое - split brain, когда у вас образовались противоречащие друг другу данные на двух мастерах.
    Или триггерная репликация внезапно встала колом и надо разбираться, из-за чего.
    Отправлять пишущие запросы синхронно на несколько хостов и предполагать, что там будут сходиться данные в итоге - это надо быть или большим оптимистом или очень внимательно проверять каждый запрос на предмет что именно он будет делать и как себя поведёт при конкурентном доступе.
    Ответ написан
    4 комментария
  • Postgresql как ограничить общий размер WAL-файлов?

    Melkij
    @Melkij
    PostgreSQL DBA
    Один сегмент WAL обычно имеет фиксированный размер 16мб (опция компиляции postgresql with-wal-segsize, если вы её указывали - вы должны об этом знать, плюс она требует initdb делать заново). Поэтому их количество пересчитывается в объём банальным умножением.

    Ограничить максимальный занимаемый объём wal сверху - нельзя. Такой единой гайки нет, даже если вас устраивает переход базы в readonly по достижении этого лимита. И есть довольно много гаек, которые при своём использовании могут попросить базу не вычищать старые wal ещё какое-то время.

    Гайки, на которые надо обращать внимание:
    wal_keep_segments - база оставляет на диске не меньше этого числа wal, что позволяет реплике штатно на некоторое время терять мастера и затем догонять
    replication slots - если вы сделаете слот репликации и его никто не будет читать - база будет сохранять wal пока не закончится диск. Если вы используете слоты репликации, то у вас в мониторинге обязана быть отдельная проверка что слот вычитывается
    max_wal_size - несмотря на название - объём wal, по накоплению которого происходит checkpoint. (либо по таймеру checkpoint_timeout смотря что случится раньше). Реально объём wal может быть выше, т.к. автоматический checkpoint намеренно не выполняется моментально, а размазан во времени.
    min_wal_size - этот объём wal всегда будет на диске занят и будет переписываться по кругу. Как гарантия того, что на диске есть место под столько wal
    archive_command - если включен и команда возвращает ненулевой статус возврата - то будет накапливать wal без ограничений. wal будут удалены (если только ещё не нужны какой из других настроек) когда archive_command получает от команды 0 код возврата.

    Если не хочется много денег тратить на резерв свободного места на хороших SSD - разместите на SSD саму базу данных, а WAL перенесите (симлинком директории pg_xlog (до 10.0) или pg_wal (10.0 и выше)) на отдельные HDD. WAL пишутся строго последовательно, вполне возможно жить с WAL на механических дисках и держать хорошую нагрузку.
    Плюс если работаете не с деньгами, можно сделать synchronous_commit = off. Что увеличит производительность всех пишущих транзакций. Но в случае фатального краха железа вы потеряете последние 3*wal_writer_delay транзакции (т.е. до 3*200мс = 600мс).
    Ответ написан
    2 комментария
  • Как хранить базу postgres 8.4 в памяти?

    Melkij
    @Melkij
    PostgreSQL DBA
    Закупаете железку с 256гб ram и выше. Ставите shared_buffers в 200гб. Всё. Со временем все данные к каким обращались будут подтянуты в память. А поскольку буферов больше чем база - база их не будет вытеснять из памяти.

    Можно было бы воспользоваться pg_prewarm для более удобного подтягивания данных в память после старта СУБД - да у вас какая-то ископаемая, давно уже не поддерживаемая версия. pg_prewarm для такой древности нет. (если, конечно, вы не опечатались в 9.4)
    Для такого объёма shared_buffers желательно huge pages сделать по объёму на пару процентов больше чем shared_buffers. А вот как - может для 8.4 и никак, я не настолько старый DBA, не знаю.
    Ответ написан
    4 комментария
  • Как сделать правильно check constraint?

    Melkij
    @Melkij
    PostgreSQL DBA
    next_month := date_part( 'year', NEW.dtime)::text ||'-'|| date_part( 'month', NEW.dtime )::text ||'-31';

    Какой удивительно-непроверенный способ выстрелить себе в ногу. Половина года просто не нужна? Ни февраль ни ноябрь?

    При этом же
    table_part := table_master
    || '' || to_char(NOW(), 'YYYY')::text
    || 'm' || to_char(NOW(), 'MM')::text;

    INSERT INTO ' || table_part || '
    Какой дважды отважный способ отстрелить ногу. Дебажить что у вас получается не пробовали в принципе?
    И с чего же это действительно violates check constraint? Вообще не понятно. И вас ничего на насторожило даже в попытке вставить августовские данные в m10 раздел? Ну нельзя же так, в самом деле.

    Про race condition не упоминаю даже, он хотя бы действительно не для всех применений имеет значение.
    Ответ написан
  • Как работать с Ltree?

    Melkij
    @Melkij
    PostgreSQL DBA
    Это реализация материализованного пути. Ветка удалена если в ней нет элементов. Перенос - перенести каждый элемент. subpath функция в помощь например
    update tablename set tree = concat('newbranch.', subpath(tree, 1))::ltree where tree <@ 'origbranch';
    Ответ написан
    Комментировать
  • Как ограничить поиск в партиции таблицы?

    Melkij
    @Melkij
    PostgreSQL DBA
    Перепроверил догадку
    melkij=> create table events2016m11 (check (dtime >= '2016-11-01'::date AND dtime < '2016-12-01'::date)) inherits(events);
    melkij=> insert into events2016m11 values ('2016-11-20');
    INSERT 0 1
    melkij=> explain (analyze) select * from events where dtime > '2016-12-05';
                                                       QUERY PLAN                                                   
    ----------------------------------------------------------------------------------------------------------------
     Append  (cost=0.00..38.25 rows=754 width=8) (actual time=0.013..0.013 rows=0 loops=1)
       ->  Seq Scan on events  (cost=0.00..0.00 rows=1 width=8) (actual time=0.004..0.004 rows=0 loops=1)
             Filter: (dtime > '2016-12-05 00:00:00+03'::timestamp with time zone)
       ->  Seq Scan on events2016m11  (cost=0.00..38.25 rows=753 width=8) (actual time=0.009..0.009 rows=0 loops=1)
             Filter: (dtime > '2016-12-05 00:00:00+03'::timestamp with time zone)
             Rows Removed by Filter: 1
     Planning time: 0.127 ms
     Execution time: 0.032 ms
    (8 строк)
    melkij=> drop table events2016m11 ;
    DROP TABLE
    melkij=> create table events2016m11 (check (dtime >= '2016-11-01'::timestamptz AND dtime < '2016-12-01'::timestamptz)) inherits(events);
    CREATE TABLE
    melkij=> insert into events2016m11 values ('2016-11-20');INSERT 0 1
    melkij=> explain (analyze) select * from events where dtime > '2016-12-05';                                              QUERY PLAN                                              
    ------------------------------------------------------------------------------------------------------
     Append  (cost=0.00..0.00 rows=1 width=8) (actual time=0.004..0.004 rows=0 loops=1)
       ->  Seq Scan on events  (cost=0.00..0.00 rows=1 width=8) (actual time=0.004..0.004 rows=0 loops=1)
             Filter: (dtime > '2016-12-05 00:00:00+03'::timestamp with time zone)
     Planning time: 0.301 ms
     Execution time: 0.024 ms
    (5 строк)
    
    melkij=> show constraint_exclusion ;
     constraint_exclusion 
    ----------------------
     partition

    Внимательнее с явным приведением типов. У планировщика достаточно работы и не всё эквивалентное он считает идентичным. К тому же timestamp with timezone и date (без времени вовсе) надо сравнивать аккуратно.
    Ответ написан
    Комментировать
  • Как посмотреть существующие триггеры событий (Event Trigger, postgresql)?

    Melkij
    @Melkij
    PostgreSQL DBA
    Они прописываются в системной view pg_event_trigger
    https://www.postgresql.org/docs/9.6/static/catalog...

    В psql есть сокращение \dy выполняющее вот такой запрос:
    SELECT evtname as "Name", evtevent as "Event", pg_catalog.pg_get_userbyid(e.evtowner) as "Owner",
     case evtenabled when 'O' then 'enabled'  when 'R' then 'replica'  when 'A' then 'always'  when 'D' then 'disabled' end as "Enabled",
     e.evtfoid::pg_catalog.regproc as "Procedure", pg_catalog.array_to_string(array(select x from pg_catalog.unnest(evttags) as t(x)), ', ') as "Tags"
    FROM pg_catalog.pg_event_trigger e ORDER BY 1
    Ответ написан
    Комментировать
  • Как правильно написать двойное обновление?

    Melkij
    @Melkij
    PostgreSQL DBA
    Вы сильно заблуждаетесь, если думаете, что ваш cte выполняется последовательно.
    The sub-statements in WITH are executed concurrently with each other and with the main query. Therefore, when using data-modifying statements in WITH, the order in which the specified updates actually happen is unpredictable. All the statements are executed with the same snapshot (see Chapter 13), so they cannot "see" one another's effects on the target tables.

    https://www.postgresql.org/docs/9.6/static/queries...

    Вы выполняете на одном слепке данных одних и тех строк различающиеся действия. Не надо так. Я понятий не имею, какой эффект от этого будет.
    К тому же вы элементарно переписываетесь в один просто запрос
    UPDATE "TABLE1"
    SET
      "Value2" = (NOT EXISTS(
        SELECT NULL
        FROM "TABLE2"
        WHERE "что-то" = "кое-что"
    )
          AND NOT EXISTS(
        SELECT NULL
        FROM "TABLE3"
        WHERE "что-то" = "кое-что"
    ))
    WHERE "кое-что" = ANY ($1 :: INT [])
    Ответ написан
    Комментировать