2012-10-03 21 views
5

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ć)

  1. „INNER JOIN”: łączenie filtrów poprzez sprzężenia wewnętrzne filtrować wyniki.
  2. "Z podzapytań": Stosuj filtry przez podzapytania w instrukcji FROM.
  3. "GDZIE podkwerendy": Filtruj spójne podzapytania w klauzuli WHERE.
  4. "Zapytania wewnętrzne INNER JOIN": dziwna hybryda.

Utworzyłem SQL skrzypce wykazać (i profilu) im:

http://sqlfiddle.com/#!3/4e17b/9

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

Odpowiedz

0

Jeśli zawsze będziesz stosował logikę "i", a wewnętrzne sprzężenie jest prawdopodobnie dobrym podejściem (generalizuję, ale będzie się ono różnić zależnie od wielu czynników, w tym rozmiaru tabeli i indeksów itp.). Będziesz musiał użyć jednego z innych rozwiązań, jeśli chcesz mieć możliwość stosowania filtrowania "i" lub "lub".

Ponadto, należy przetestować działanie przy użyciu istnieje punktach:

SELECT COUNT(*) 
     FROM @TestTable Test0 
     WHERE 
      EXISTS (SELECT 1 FROM @TestTable Test1 WHERE Test0.ID = Test1.ID AND Test1.ID % @i = 0) 
      EXISTS (SELECT 1 FROM @TestTable Test2 WHERE Test0.ID = Test2.ID AND Test2.ID % @j = 0) 
      EXISTS (SELECT 1 FROM @TestTable Test3 WHERE Test0.ID = Test3.ID AND Test3.ID % @k = 0) 
+0

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

Powiązane problemy