PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Wielokrotne używanie tej samej kolumny w klauzuli WHERE

To jest przypadek podziału relacyjnego. Dodałem tag.

Indeksy

Zakładając ograniczenie PK lub UNIQUE na USER_PROPERTY_MAP(property_value_id, user_id) - kolumny w tej kolejności, aby moje zapytania były szybkie. Powiązane:

  • Czy indeks złożony jest również odpowiedni dla zapytań w pierwszym polu?

Powinieneś także mieć indeks PROPERTY_VALUE(value, property_name_id, id) . Ponownie kolumny w tej kolejności. Dodaj ostatnią kolumnę id tylko wtedy, gdy zdobędziesz tylko skany indeksu.

Dla określonej liczby nieruchomości

Istnieje wiele sposobów na rozwiązanie tego problemu. To powinno być jednym z najprostszych i najszybszych dla dokładnie dwóch właściwości:

SELECT u.*
FROM   users             u
JOIN   user_property_map up1 ON up1.user_id = u.id
JOIN   user_property_map up2 USING (user_id)
WHERE  up1.property_value_id =
      (SELECT id FROM property_value WHERE property_name_id = 1 AND value = '101')
AND    up2.property_value_id =
      (SELECT id FROM property_value WHERE property_name_id = 2 AND value = '102')
-- AND    u.user_name = 'user1'  -- more filters?
-- AND    u.city = 'city1'

Nie odwiedzasz tabeli PROPERTY_NAME , ponieważ wygląda na to, że masz już rozwiązane nazwy właściwości na identyfikatory, zgodnie z przykładowym zapytaniem. W przeciwnym razie możesz dodać dołączenie do PROPERTY_NAME w każdym podzapytaniu.

W ramach tego pytania zebraliśmy cały arsenał technik:

  • Jak filtrować wyniki SQL w relacji ma wiele przejść

Dla nieznanej liczby nieruchomości

@Mike i @Valera mają w swoich odpowiedziach bardzo przydatne zapytania. Aby uczynić to jeszcze bardziej dynamicznym :

WITH input(property_name_id, value) AS (
      VALUES  -- provide n rows with input parameters here
        (1, '101')
      , (2, '102')
      -- more?
      ) 
SELECT *
FROM   users u
JOIN  (
   SELECT up.user_id AS id
   FROM   input
   JOIN   property_value    pv USING (property_name_id, value)
   JOIN   user_property_map up ON up.property_value_id = pv.id
   GROUP  BY 1
   HAVING count(*) = (SELECT count(*) FROM input)
   ) sub USING (id);

Dodawaj/usuwaj tylko wiersze z VALUES wyrażenie. Lub usuń WITH klauzula i JOIN dla braku filtrów właściwości w ogóle.

problem przy tej klasie zapytań (licząc wszystkie dopasowania częściowe) jest wydajność . Moje pierwsze zapytanie jest mniej dynamiczne, ale zazwyczaj znacznie szybsze. (Po prostu przetestuj za pomocą EXPLAIN ANALYZE .) Specjalnie dla większych stołów i rosnącej liczby obiektów.

Najlepsze z obu światów?

To rozwiązanie z rekurencyjnym CTE powinno być dobrym kompromisem:szybkie i dynamiczny:

WITH RECURSIVE input AS (
   SELECT count(*)     OVER () AS ct
        , row_number() OVER () AS rn
        , *
   FROM  (
      VALUES  -- provide n rows with input parameters here
        (1, '101')
      , (2, '102')
      -- more?
      ) i (property_name_id, value)
   )
 , rcte AS (
   SELECT i.ct, i.rn, up.user_id AS id
   FROM   input             i
   JOIN   property_value    pv USING (property_name_id, value)
   JOIN   user_property_map up ON up.property_value_id = pv.id
   WHERE  i.rn = 1

   UNION ALL
   SELECT i.ct, i.rn, up.user_id
   FROM   rcte              r
   JOIN   input             i ON i.rn = r.rn + 1
   JOIN   property_value    pv USING (property_name_id, value)
   JOIN   user_property_map up ON up.property_value_id = pv.id
                              AND up.user_id = r.id
   )
SELECT u.*
FROM   rcte  r
JOIN   users u USING (id)
WHERE  r.ct = r.rn;          -- has all matches

tutaj dbfiddle

Podręcznik dotyczący rekurencyjnych CTE.

Dodatkowa złożoność nie opłaca się za małe stoły, gdzie dodatkowe koszty przeważają nad korzyściami lub różnica jest na początku znikoma. Ale skaluje się znacznie lepiej i jest coraz lepszy od technik „liczenia” z rosnącymi tabelami i rosnącą liczbą filtrów właściwości.

Techniki liczenia muszą odwiedzić wszystkie wiersze w user_property_map dla wszystkich podanych filtrów właściwości, podczas gdy to zapytanie (podobnie jak pierwsze zapytanie) może wcześnie wyeliminować nieodpowiednich użytkowników.

Optymalizacja wydajności

Z aktualnymi statystykami tabeli (rozsądne ustawienia, autovacuum działa), Postgres ma wiedzę na temat „najczęstszych wartości” w każdej kolumnie i zmieni kolejność złączeń w pierwszym zapytaniu aby najpierw ocenić najbardziej selektywne filtry właściwości (lub przynajmniej nie najmniej selektywne). Do pewnego limitu:join_collapse_limit . Powiązane:

  • Postgresql join_collapse_limit i czas na planowanie zapytań
  • Dlaczego niewielka zmiana wyszukiwanego hasła tak bardzo spowalnia zapytanie?

Ta interwencja „deus-ex-machina” nie jest możliwa w przypadku trzeciego zapytania (rekurencyjne CTE). Aby poprawić wydajność (być może dużo), musisz najpierw samodzielnie umieścić bardziej selektywne filtry. Ale nawet przy najgorszym przypadku zamówienia nadal będzie przewyższać liczbę zapytań.

Powiązane:

  • Sprawdź cele statystyk w PostgreSQL

Dużo więcej krwawych szczegółów:

  • Częściowy indeks PostgreSQL nie jest używany podczas tworzenia w tabeli z istniejącymi danymi

Więcej wyjaśnień w instrukcji:

  • Statystyki używane przez planistę


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Wyjaśnienie JSONB wprowadzone przez PostgreSQL

  2. Wysoka dostępność PostgreSQL dzięki architekturze Master-Slave i Master-Master

  3. Gdzie warunek dla połączonego stołu w Sequelize ORM

  4. pgpredict – Analiza predykcyjna w PostgreSQL

  5. Postgres:BŁĄD:plan w pamięci podręcznej nie może zmieniać typu wyniku