Rozumiem, że podzapytania są notorycznie złe dla wydajności, gdy są niewłaściwie używane. Mam bardzo specyficzny scenariusz, w którym użytkownik musi pobrać przefiltrowany zestaw rekordów z tabeli. Dostępna będzie szeroka gama filtrów, które muszą obsługiwać kompozycję. Co więcej, nowe filtry będą tworzone regularnie przez grupę programistów.Podzapytanie SQL w klauzuli OD
Nie podoba mi się pomysł jednego rosnącego, monolitycznego zapytania SQL z dużą liczbą parametrów. Nie podoba mi się pomysł mnóstwa autonomicznych zapytań SQL z identycznymi instrukcjami SELECT i zmieniającymi się klauzulami WHERE. Podoba mi się pomysł dynamicznego zapytania SQL, ale nie jestem pewien, jakiej struktury powinienem użyć. Mogę myśleć o 4 podstawowych opcji: (jeśli jest ich więcej, że jestem brakuje, to nie wahaj się ich sugerować)
- „INNER JOIN”: łączenie filtrów poprzez sprzężenia wewnętrzne filtrować wyniki.
- "Z podzapytań": Stosuj filtry przez podzapytania w instrukcji FROM.
- "GDZIE podkwerendy": Filtruj spójne podzapytania w klauzuli WHERE.
- "Zapytania wewnętrzne INNER JOIN": dziwna hybryda.
Utworzyłem SQL skrzypce wykazać (i profilu) im:
Poniżej znajduje się wyciąg z skrzypce zapewnić pomysł, co ja” m rozmawia o:
------------------------------------------------------------------------
--THIS IS AN EXCERPT FROM THE SQL FIDDLE -- IT IS NOT MEANT TO COMPILE--
------------------------------------------------------------------------
--
--"INNER JOIN" test
SELECT COUNT(*)
FROM
@TestTable Test0
INNER JOIN @TestTable Test1 ON Test1.ID=Test0.ID AND Test1.ID % @i = 0
INNER JOIN @TestTable Test2 ON Test2.ID=Test0.ID AND Test2.ID % @j = 0
INNER JOIN @TestTable Test3 ON Test3.ID=Test0.ID AND Test3.ID % @k = 0
--
--"FROM subqueries" test
SELECT COUNT(*) FROM (
SELECT * FROM (
SELECT * FROM (
SELECT * FROM @TestTable Test3 WHERE Test3.ID % @k = 0
) Test2 WHERE Test2.ID % @j = 0
) Test1 WHERE Test1.ID % @i = 0
) Test0
--
--"WHERE subqueries" test
SELECT COUNT(*)
FROM @TestTable Test0
WHERE
Test0.ID IN (SELECT ID FROM @TestTable Test1 WHERE Test1.ID % @i = 0)
AND Test0.ID IN (SELECT ID FROM @TestTable Test2 WHERE Test2.ID % @j = 0)
AND Test0.ID IN (SELECT ID FROM @TestTable Test3 WHERE Test3.ID % @k = 0)
--
--"INNER JOIN subqueries" test
SELECT COUNT(*)
FROM
TestTable Test0
INNER JOIN (SELECT ID FROM TestTable WHERE ID % @i = 0) Test1 ON Test1.ID=Test0.ID
INNER JOIN (SELECT ID FROM TestTable WHERE ID % @j = 0) Test2 ON Test2.ID=Test0.ID
INNER JOIN (SELECT ID FROM TestTable WHERE ID % @k = 0) Test3 ON Test3.ID=Test0.ID
--
--"EXISTS subqueries" test
SELECT COUNT(*)
FROM TestTable Test0
WHERE
EXISTS (SELECT 1 FROM TestTable Test1 WHERE Test1.ID = Test0.ID AND Test1.ID % @i = 0)
AND EXISTS (SELECT 1 FROM TestTable Test2 WHERE Test2.ID = Test0.ID AND Test2.ID % @j = 0)
AND EXISTS (SELECT 1 FROM TestTable Test3 WHERE Test3.ID = Test0.ID AND Test3.ID % @k = 0)
ocenami (czas do wykonywania testów)
SQL Fiddle:
|INNER JOIN|FROM SUBQUERIES|WHERE SUBQUERIES|INNER JOIN SUBQUERIES|EXISTS SUBQUERIES|
-------------------------------------------------------------------------------------
| 5174 | 777 | 7240 | 5478 | 7359 |
lokalnego środowiska: (bez cache: wyczyszczenie bufora przed każdym testem)
|INNER JOIN|FROM SUBQUERIES|WHERE SUBQUERIES|INNER JOIN SUBQUERIES|EXISTS SUBQUERIES|
-------------------------------------------------------------------------------------
| 3281 | 2851 | 2964 | 3148 | 3071 |
lokalnego środowiska: (z pamięci podręcznej: bieganie zapytaniami dwa razy z rzędu i nagrać czas drugiego przebiegu)
|INNER JOIN|FROM SUBQUERIES|WHERE SUBQUERIES|INNER JOIN SUBQUERIES|EXISTS SUBQUERIES|
-------------------------------------------------------------------------------------
| 284 | 50 | 3334 | 278 | 408 |
Istnieją zalety/wady każdego rozwiązania. Podzapytania w klauzuli WHERE mają dość fatalną wydajność. Podzapytania w klauzuli FROM mają całkiem niezłą wydajność (w rzeczywistości zwykle działają najlepiej) (UWAGA: wierzę, że ta metoda negowałaby zalety indeksów?). INNER JOINs mają całkiem niezłą wydajność, ale wprowadza pewne interesujące zagadnienia dotyczące scopingu, ponieważ w przeciwieństwie do podzapytań, INNER JOINs działałyby w tym samym kontekście (musiałby istnieć system pośredniczący, aby uniknąć kolizji aliasów tabeli).
Ogólnie myślę, że najczystszym rozwiązaniem są podzapytania w klauzuli FROM. Filtry byłyby łatwe do napisania i przetestowania (ponieważ w przeciwieństwie do INNER JOINs nie musiałyby być dostarczane z zapytaniem kontekstowym/podstawowym).
Myśli? Czy jest to prawidłowe użycie podzapytań lub katastrofa czekająca na zdarzenie?
UPDATE (04.10.2012):
- Aktualizacja SQL Fiddle obejmować test dla "istnieje" metoda
- Dodane testu wydajności z SQL skrzypce i lokalnego środowiska
Dodano testy dla „istnieje” metody. Osiąga on pozycję zbliżoną do końca ogona, chociaż byłem zaskoczony, widząc różnicę między "SELECT *" i "SELECT 1" (z pewnością nie był to najbardziej kontrolowany test, ale była pewna zmienność). Ogólna metodologia zostanie zastosowana do różnych tabel, dlatego wielkość i indeksy są trudne do przewidzenia z góry. Po prostu szukam "najbardziej przyjaznej piaskownicy". – makerplays