PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Прозоречна функция на PostgreSQL:дял чрез сравнение

Използвайки няколко различни функции на прозореца и две подзаявки, това трябва да работи прилично бързо:

WITH events(id, event, ts) AS (
  VALUES
   (1, 12, '2014-03-19 08:00:00'::timestamp)
  ,(2, 12, '2014-03-19 08:30:00')
  ,(3, 13, '2014-03-19 09:00:00')
  ,(4, 13, '2014-03-19 09:30:00')
  ,(5, 12, '2014-03-19 10:00:00')
   )
SELECT first_value(pre_id)  OVER (PARTITION BY grp ORDER BY ts)      AS pre_id
     , id, ts
     , first_value(post_id) OVER (PARTITION BY grp ORDER BY ts DESC) AS post_id
FROM  (
   SELECT *, count(step) OVER w AS grp
   FROM  (
      SELECT id, ts
           , NULLIF(lag(event) OVER w, event) AS step
           , lag(id)  OVER w AS pre_id
           , lead(id) OVER w AS post_id
      FROM   events
      WINDOW w AS (ORDER BY ts)
      ) sub1
   WINDOW w AS (ORDER BY ts)
   ) sub2
ORDER  BY ts;

Използване на ts като име за колоната с времеви печат.
Приемаме ts да бъде уникален – ииндексиран (уникално ограничение прави това автоматично).

В тест с реална таблица с 50 000 реда се нуждаеше само от единично сканиране на индекс . Така че, трябва да бъде прилично бързо дори с големи маси. За сравнение, вашата заявка с join / different не завърши след минута (както се очакваше).
Дори оптимизирана версия, която се занимава с едно кръстосано присъединяване в даден момент (лявото присъединяване с почти ограничено условие е ефективно ограничено кръстосано присъединяване) не завърши след минута.

За най-добра производителност с голяма маса, настройте настройките на паметта си, по-специално за work_mem (за операции с голям сорт). Помислете да го зададете (много) по-високо за вашата сесия временно, ако можете да пощадите RAM. Прочетете повече тук и тук.

Как?

  1. В подзаявка sub1 погледнете събитието от предишния ред и го запазете само ако е променено, като по този начин маркирате първия елемент от нова група. В същото време вземете id на предишния и следващия ред (pre_id , post_id ).

  2. В подзаявка sub2 , count() отчита само стойности, различни от нула. Полученият grp маркира партньори в блокове от последователни същите събития.

  3. В крайния SELECT , вземете първия pre_id и последният post_id на група за всеки ред, за да стигнете до желания резултат.
    Всъщност това трябва да е още по-бързо във външния SELECT :

     last_value(post_id) OVER (PARTITION BY grp ORDER BY ts
                               RANGE BETWEEN UNBOUNDED PRECEDING
                                     AND     UNBOUNDED FOLLOWING) AS post_id
    

    ... тъй като реда на сортиране на прозореца е съгласен с прозореца за pre_id , така че е необходим само един сорт. Бърз тест изглежда го потвърждава. Повече за тази дефиниция на рамката.

SQL Fiddle.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Съхраняването на json, jsonb, hstore, xml, enum, ipaddr и т.н. е неуспешно с колона x е от тип json, но изразът е от тип символ, различен

  2. Търсене в индекси на изрази

  3. Какво прави ::в PostgreSQL?

  4. Как да наблюдавате производителността на PostgreSQL 12 с OmniDB – част 2

  5. Прословутото java.sql.SQLException:Не е намерен подходящ драйвер