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

SQL:Когато става въпрос за НЕ В и НЕ РАВНО НА, кое е по-ефективно и защо?

В PostgreSQL обикновено има доста малка разлика при разумни дължини на списъка, въпреки че IN е много по-чист концептуално. Много дълго AND ... <> ... списъци и много дълги NOT IN И двата списъка се представят ужасно, с AND много по-лошо от NOT IN .

И в двата случая, ако са достатъчно дълги, за да зададете въпроса, вместо това трябва да правите тест за изключване срещу присъединяване или подзаявка върху списък със стойности.

WITH excluded(item) AS (
    VALUES('item1'), ('item2'), ('item3'), ('item4'),('item5')
)
SELECT * 
FROM thetable t
WHERE NOT EXISTS(SELECT 1 FROM excluded e WHERE t.item = e.item);

или:

WITH excluded(item) AS (
    VALUES('item1'), ('item2'), ('item3'), ('item4'),('item5')
)
SELECT * 
FROM thetable t
LEFT OUTER JOIN excluded e ON (t.item = e.item)
WHERE e.item IS NULL;

(В съвременните версии на Pg и двете ще произвеждат един и същ план за заявка.

Ако списъкът със стойности е достатъчно дълъг (много десетки хиляди артикули), тогава анализът на заявката може да започне да има значителни разходи. На този етап трябва да помислите за създаване на TEMPORARY таблица, COPY въвеждане на данните за изключване в него, евентуално създаване на индекс върху тях, след което използване на един от горните подходи за временната таблица вместо CTE.

Демо:

CREATE UNLOGGED TABLE exclude_test(id integer primary key);
INSERT INTO exclude_test(id) SELECT generate_series(1,50000);
CREATE TABLE exclude AS SELECT x AS item FROM generate_series(1,40000,4) x;

където exclude е списъкът със стойности за пропускане.

След това сравнявам следните подходи на едни и същи данни с всички резултати в милисекунди:

  • NOT IN списък:3424.596
  • AND ... списък:80173.823
  • VALUES базиран на JOIN изключване:20.727
  • VALUES базирано изключване на подзаявка:20.495
  • Базирано на таблица JOIN , без индекс в бившия списък:25.183
  • Въз основа на таблица на подзаявки, без индекс в предишния списък:23,985

... което прави базирания на CTE подход над три хиляди пъти по-бърз от AND списък и 130 пъти по-бързо от NOT IN списък.

Код тук:https://gist.github.com/ringerc/5755247 (закрилете очите си, вие, които следвате тази връзка).

За този размер на набора от данни добавянето на индекс в списъка за изключване не направи разлика.

Бележки:

  • IN списък, генериран с SELECT 'IN (' || string_agg(item::text, ',' ORDER BY item) || ')' from exclude;
  • AND списък, генериран с SELECT string_agg(item::text, ' AND item <> ') from exclude; )
  • Изключването на таблици, базирано на подзаявка и присъединяване, беше почти едно и също при многократно изпълнение.
  • Разглеждането на плана показва, че Pg превежда NOT IN до <> ALL

Така че... можете да видите, че има наистина огромен разлика между двата IN и AND списъци срещу правене на правилно присъединяване. Това, което ме изненада, беше колко бързо го направих с CTE с помощта на VALUES списъкът беше ... синтактичен анализ на VALUES списъкът не отне почти никакво време, изпълнявайки същото или малко по-бързо от подходът на таблицата в повечето тестове.

Би било хубаво, ако PostgreSQL може автоматично да разпознае нелепо дълъг IN клауза или верига от подобни AND условия и преминете към по-интелигентен подход като извършване на хеширано присъединяване или имплицитно превръщането му в CTE възел. В момента то не знае как да направи това.

Вижте също:

  • тази удобна публикация в блога Магнус Хагандер написа по темата


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

  2. Как да вмъкна няколко стойности в таблица на postgres наведнъж?

  3. Естествен сорт, поддържащ големи числа

  4. Кой е най-елегантният начин за съхраняване на времеви печат с nanosec в postgresql?

  5. SQLAlchemy декларативно:дефиниране на тригери и индекси (Postgres 9)