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

Правилен начин за достъп до последния ред за всеки отделен идентификатор?

Ето кратко сравнение на ефективността за заявките, споменати в тази публикация.

Текуща настройка:

Таблицата core_message има 10 904 283 реда и има 60 740 реда в test_boats (или 60 740 отделни mmsi в core_message ).

И аз използвам PostgreSQL 11.5

Запитване чрез сканиране само за индекс :

1) използвайки DISTINCT ON :

SELECT DISTINCT ON (mmsi) mmsi 
FROM core_message;

2) използване на RECURSIVE с LATERAL :

WITH RECURSIVE cte AS (
   (
   SELECT mmsi
   FROM   core_message
   ORDER  BY mmsi
   LIMIT  1
   )
   UNION ALL
   SELECT m.*
   FROM   cte c
   CROSS  JOIN LATERAL (
      SELECT mmsi
      FROM   core_message
      WHERE  mmsi > c.mmsi
      ORDER  BY mmsi
      LIMIT  1
      ) m
   )
TABLE cte;

3) Използване на допълнителна таблица с LATERAL :

SELECT a.mmsi
FROM test_boats a
CROSS JOIN LATERAL(
    SELECT b.time
    FROM core_message b
    WHERE a.mmsi = b.mmsi
    ORDER BY b.time DESC
    LIMIT 1
) b;

Заявката не използва сканиране само за индекс :

4) използване на DISTINCT ON с mmsi,time DESC INDEX :

SELECT DISTINCT ON (mmsi) * 
FROM core_message 
ORDER BY mmsi, time desc;

5) използване на DISTINCT ON с обратен mmsi,time UNIQUE CONSTRAINT :

SELECT DISTINCT ON (mmsi) * 
FROM core_message 
ORDER BY mmsi desc, time desc;

6) използване на RECURSIVE с LATERAL и mmsi,time DESC INDEX :

WITH RECURSIVE cte AS (
   (
   SELECT *
   FROM   core_message
   ORDER  BY mmsi , time DESC 
   LIMIT  1
   )
   UNION ALL
   SELECT m.*
   FROM   cte c
   CROSS  JOIN LATERAL (
      SELECT *
      FROM   core_message
      WHERE  mmsi > c.mmsi
      ORDER  BY mmsi , time DESC 
      LIMIT  1
      ) m
   )
TABLE cte;

7) използване на RECURSIVE с LATERAL и назад mmsi,time UNIQUE CONSTRAINT :

WITH RECURSIVE cte AS (

   (

   SELECT *
   FROM   core_message
   ORDER  BY mmsi DESC , time DESC 
   LIMIT  1
   )
   UNION ALL
   SELECT m.*
   FROM   cte c
   CROSS  JOIN LATERAL (
      SELECT *
      FROM   core_message
      WHERE  mmsi < c.mmsi
      ORDER  BY mmsi DESC , time DESC 
      LIMIT  1
      ) m
   )
TABLE cte;

8) Използване на допълнителна таблица с LATERAL :

SELECT b.*
FROM test_boats a
CROSS JOIN LATERAL(
    SELECT b.*
    FROM core_message b
    WHERE a.mmsi = b.mmsi
    ORDER BY b.time DESC
    LIMIT 1
) b;

Използване на специална таблица за последното съобщение:

9) Ето моето първоначално решение, използвайки отделна таблица само с последното съобщение. Тази таблица се попълва, когато пристигат нови съобщения, но може да се създаде и така:

CREATE TABLE core_shipinfos AS (
    WITH RECURSIVE cte AS (
       (
       SELECT *
       FROM   core_message
       ORDER  BY mmsi DESC , time DESC 
       LIMIT  1
       )
       UNION ALL
       SELECT m.*
       FROM   cte c
       CROSS  JOIN LATERAL (
          SELECT *
          FROM   core_message
          WHERE  mmsi < c.mmsi
          ORDER  BY mmsi DESC , time DESC 
          LIMIT  1
          ) m
       )
    TABLE cte);

Тогава заявката за получаване на последното съобщение е толкова проста:

SELECT * FROM core_shipinfos;

Резултати:

Средна стойност на множество заявки (около 5 за бързата):

1) 9146 ms
2) 728 ms
3) 498 ms

4) 51488 ms
5) 54764 ms
6) 729 ms
7) 778 ms
8) 516 ms

9) 15 ms

Заключение:

Няма да коментирам специалното решение за таблица и ще го запазя за края.

Допълнителната таблица (test_boats ) решението определено е победителят тук, но RECURSIVE решението също е доста ефективно.

Има огромна разлика в производителността за DISTINCT ON използвайки сканиране само за индекс и този, който не го използва, но печалбата в производителността е доста малка за другата ефективна заявка.

Това има смисъл, тъй като основното подобрение, което носят тези заявки, е фактът, че не е необходимо да преминават през целия core_message таблица, но само на подмножество от уникалния mmsi което е значително по-малко (60K+) в сравнение с core_message размер на таблицата (10M+)

Като допълнителна бележка, не изглежда да има значително подобрение в производителността за заявките, използващи UNIQUE CONSTRAINT ако пусна mmsi,time DESC INDEX . Но премахването на този индекс, разбира се, ще ми спести малко място (този индекс в момента заема 328MB)

Относно решението за специална маса:

Всяко съобщение, съхранено в core_message Таблицата съдържа както позиционна информация (позиция, скорост, курс и т.н.), И информация за кораба (име, позивна, размери и т.н.), както и идентификатор на кораба (mmsi).

За да дам малко повече информация за това, което всъщност се опитвам да направя:внедрявам бекенд за съхраняване на съобщения, излъчвани от кораби чрез AIS протокол .

Като такъв, всеки уникален mmsi, който получих, го получих чрез този протокол. Това не е предварително определен списък. Продължава да добавя нови MMSI, докато не накарам всички кораби в света да използват AIS.

В този контекст специална таблица с информация за кораба като последно получено съобщение има смисъл.

Мога да избегна използването на такава таблица, каквато видяхме с RECURSIVE решение, но... специална таблица все още е 50 пъти по-бърза от тази RECURSIVE решение.

Тази специална таблица всъщност е подобна на test_boat таблица, с повече информация от mmsi поле. Както е, имайки таблица с mmsi единствено поле или таблица с всяка последна информация от core_message таблица добави същата сложност към моето приложение.

В крайна сметка мисля да се спра на тази специална маса. Това ще ми даде ненадмината скорост и все още ще имам възможността да използвам LATERAL трик с core_message , което ще ми даде повече гъвкавост.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. PostgreSQL разстояние между 2 точки, съхранени в таблица

  2. Стойността за грешка не съществува - проблем с postgresql INSERT INTO

  3. Инсталиране на PostgreSQL Extension към всички схеми

  4. Доктрина - Добавяне на времеви печат по подразбиране към обект като NOW()

  5. Rails каква е разликата в уникалния индекс и validates_uniqueness_of