Czy SQL źle wpływa na wydajność?

Mam zapytanie robiące coś w stylu:

SELECT FieldX, FieldY FROM A
WHERE FieldW IN (108, 109, 113, 138, 146, 160,
307, 314, 370, 371, 441, 454 ,457, 458, 479, 480,
485, 488, 490, 492, 519, 523, 525, 534, 539, 543,
546, 547, 550, 564, 573, 629, 642, 643, 649, 650,
651, 694, 698, 699, 761, 762, 768, 772, 773, 774,
775, 778, 784, 843, 844, 848, 851, 852, 853, 854,
855, 856, 857, 858, 859, 860, 861, 862, 863, 864,
865, 868, 869, 871, 872, 873, 891) 

Posiadanie klauzuli IN Z tak wieloma opcjami, jest złe dla wydajności zapytań? Doświadczam wielu przerw czasowych w mojej aplikacji i wierzę, że może to być źródłem tego rodzaju problemu. Czy Mogę zoptymalizować zapytanie bez usuwania liczb, używając dobrej podpowiedzi SQL?

EDIT:

@KM to są klucze w innej tabeli. Jest to aplikacja forum, wyjaśniająca krótko: c# pobiera wszystkie fora z bazy danych i przechowuje je w pamięć podręczna aplikacji. Zanim C# wywoła procedurę, która pobiera wątki dla tych forów i dla tego użytkownika, C# robi logikę filtrującą kolekcję "all forums", biorąc pod uwagę uprawnienia i logikę biznesową. Timeout występuje w bazie danych, a nie w samej aplikacji. Wykonanie całej tej logiki na zapytaniu będzie wymagało wielu wewnętrznych złączeń i nie jestem w 100% pewien, że mogę to wszystko zrobić wewnątrz procedury.

Używam SQL Server 2000

Author: Academy of Programmer, 2009-06-18

14 answers

Podczas pisania zapytania za pomocą operatora IN można wziąć pod uwagę kilka czynników, które mogą mieć wpływ na wydajność.

Po pierwsze, klauzule IN są zwykle wewnętrznie przepisywane przez większość baz danych w celu użycia łącznika logicznego OR. więc {[0] } jest przepisywane na: (COL = 'a') OR (COL = 'b') or (COL = 'c'). Plan wykonania dla obu zapytań prawdopodobnie będzie równoważny, zakładając, że masz indeks na col.

Po drugie, gdy używasz albo IN albo ze zmienną liczbą argumentów, jesteś powoduje, że baza danych musi ponownie przeanalizować zapytanie i odbudować plan wykonania za każdym razem, gdy zmieniają się argumenty. budowanie planu wykonania dla zapytania może być kosztownym krokiem. Większość baz danych buforuje plany wykonania zapytań, które uruchamiają, używając dokładnego tekstu zapytania jako klucza. Jeśli wykonasz podobne zapytanie, ale z różnymi wartościami argumentów w predykacie-najprawdopodobniej sprawisz, że baza danych spędzi znaczną ilość czasu na analizowaniu i budowaniu planów wykonawczych. To jest dlaczego zmienne bind są zdecydowanie zalecane jako sposób zapewnienia optymalnej wydajności zapytań.

Po Trzecie, wiele baz danych ma limit złożoności zapytań, które mogą wykonywać - jednym z tych ograniczeń jest liczba logicznych połączeń, które mogą być zawarte w predykacie. w Twoim przypadku, kilkadziesiąt wartości jest mało prawdopodobne, aby osiągnąć wbudowany limit bazy danych, ale jeśli spodziewasz się przekazać setki lub tysiące wartości do klauzuli IN-to z pewnością może się zdarzyć. W w takim przypadku baza danych po prostu anuluje zapytanie.

Po czwarte, zapytania zawierające predykat i lub nie zawsze mogą być optymalnie przepisywane w środowisku równoległym. istnieją różne przypadki, w których optymalizacja równoległego serwera nie jest stosowana - MSDN ma przyzwoite wprowadzenie do optymalizacji zapytań pod kątem równoległości. Ogólnie jednak zapytania, które używają operatora UNION ALL, są trywialnie parrallelizowalne w większości baz danych - i są preferowane do logiczne połączenia (jak OR I IN), jeśli to możliwe.

 97
Author: LBushkin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-18 17:10:44

Jeśli masz dobry indeks na FieldW, użycie tego w jest całkowicie właściwe.

Właśnie przetestowałem i SQL 2000 robi skan indeksu klastrowego podczas korzystania z IN.

 5
Author: tekBlues,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-18 17:10:46

Możesz spróbować utworzyć tabelę tymczasową, wstawić do niej swoje wartości i użyć tabeli w predykacie IN.

AFAIK, SQL Server 2000 nie może zbudować tabeli hash zbioru stałych, co pozbawia optymalizatora możliwości użycia HASH SEMI JOIN.

To pomoże tylko jeśli nie masz indeksu na FieldW (który powinieneś mieć).

Możesz również spróbować dołączyć swoje kolumny FieldX i FieldY do indeksu:

CREATE INDEX ix_a_wxy ON a (FieldW, FieldX, FieldY)

Tak aby zapytanie mogło być obsłużone tylko za pomocą indeksu.

SQL Server 2000 Brak opcji INCLUDE dla CREATE INDEX i może to nieco obniżyć wydajność DML, ale poprawić wydajność zapytań.

Update:

Z twojego planu wykonania widzę, że potrzebujesz indeks złożony na (SettingsID, SectionID)

SQL Server 2000 rzeczywiście może zbudować tabelę hashową ze stałej listy( i robi to), ale Hash Semi Join najprawdopodobniej będzie mniej wydajna niż Nested Loop dla zapytań.

I jeszcze jedna uwaga: jeśli musisz znać liczba wierszy spełniających warunek WHERE, nie używaj COUNT(column), zamiast tego użyj COUNT(*).

A COUNT(column) nie liczy wierszy, dla których wartością column jest NULL.

Oznacza to, że po pierwsze, możesz uzyskać wyniki, których się nie spodziewałeś, a po drugie, optymalizator będzie musiał zrobić dodatkowe Key Lookup / Bookmark Lookup jeśli twoja kolumna nie jest objęta indeksem spełniającym warunek WHERE.

Ponieważ ThreadId wydaje się być CLUSTERED PRIMARY KEY, jest w porządku dla tego samego zapytania, ale staraj się go unikać ogólnie.

 5
Author: Quassnoi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-18 22:39:45

W zależności od dystrybucji danych, dodatkowe predykaty w klauzuli WHERE mogą poprawić wydajność. Na przykład, jeśli zestaw identyfikatorów jest mały w stosunku do całkowitej liczby w tabeli i wiesz, że identyfikatory są stosunkowo blisko siebie (być może będą to zwykle Ostatnie dodatki, a zatem klastry na najwyższym końcu zakresu), możesz spróbować dołączyć predykat "i FieldW między 109 A 891" (po określeniu id min & max w twoim zestawie w kodzie C#). Informatyka może być tak, że skanowanie zakresu tych kolumn (jeśli jest indeksowane) działa szybciej niż obecnie używane.

 3
Author: Steve Broberg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-18 16:55:35

Są lepsze sposoby, aby to zakodować, ale wątpię, że to jest przyczyna twoich przerw czasowych, zwłaszcza jeśli jest to tylko SELECT. Powinieneś być w stanie to określić, patrząc na ślady zapytań. Ale przekodowanie tego byłoby optymalizacją przez zgadywanie, i mało prawdopodobne przypuszczenie na to.

Zacznijmy od planu zapytań dla zapytania, które jest faktycznie timing out. Czy wiesz na pewno, które to zapytanie?

 3
Author: dkretz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-18 17:05:40

IN to dokładnie to samo, co pisanie dużej listy RNO. Lub często sprawia, że zapytania nie są kierowane, więc indeksy mogą być ignorowane, a plan obejmuje pełne skanowanie.

 2
Author: Remus Rusanu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-18 16:50:37

Zazwyczaj klauzula IN jest szkodliwa dla wydajności, ale to, co jest "złe", zależy od aplikacji, danych, wielkości bazy danych itp. Musisz przetestować własną aplikację, aby zobaczyć, co jest najlepsze.

 1
Author: Bryan Migliorisi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-18 16:44:07

Zasadniczo to, co robi klauzula where, TO " FieldW = 108 lub FieldW = 109 lub FieldW = 113...". Czasami można uzyskać lepszą wydajność, wykonując wiele wyborów i łącząc je z union. Na przykład:

SELECT FieldX, FieldY FROM A WHERE FieldW = 108
UNION ALL
SELECT FieldX, FieldY FROM A WHERE FieldW = 109
Ale oczywiście jest to niepraktyczne, gdy porównujesz się do tak wielu wartości.

Inną opcją może być wstawienie tych wartości do tabeli tymczasowej, a następnie dołączenie tabeli A do tej tabeli tymczasowej.

 1
Author: Tommi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-18 16:46:54

Rozmiar tabeli określi prędkość podczas korzystania z tej instrukcji. Jeśli to nie jest zbyt duży stół...to stwierdzenie nie wpływa na Twoje wyniki.

 1
Author: Eric,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-18 16:47:03

Oto Twoja odpowiedź...

Http://www.4guysfromrolla.com/webtech/031004-1.shtml

Zasadniczo chcesz utworzyć funkcję, która podzieli łańcuch znaków i wypełni tabelę tymczasową podzieloną zawartością. Następnie możesz dołączyć do tej tabeli tymczasowej i manipulować danymi. Powyższe wyjaśnia rzeczy całkiem dobrze. Często używam tej techniki.

W twoim konkretnym przypadku użyj join do tabeli temp zamiast klauzuli in, znacznie szybciej.

 1
Author: infocyde,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-18 17:15:48

Ogólnie używałbym zdefiniowanego przez użytkownika typu tabeli dla takich zapytań.

CREATE TYPE [dbo].[udt_int] AS TABLE (
    [id] [int] NOT NULL
)

Używając zmiennej tabel i wypełniając ją wierszami dla każdej z liczb, możesz zrobić:

SELECT 
    FieldX, 
    FieldY
FROM A
INNER JOIN @myIds B ON
    A.FieldW = B.id
 1
Author: Donald.Record,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-09 19:18:30

Wydajność może być oceniana tylko w kontekście tego, co próbujesz zrobić. W tym przypadku żądasz odzyskania około 70 wierszy (zakładając, że są to unikalne wartości), więc możesz spodziewać się czegoś w rodzaju 70 razy czasu pobierania pojedynczej wartości. Może być mniej z powodu buforowania lub kursu.

Jednak optymalizator zapytań może potrzebować lub wybrać pełne skanowanie tabeli w celu pobrania wartości, w którym to przypadku performace będzie niewiele różnił się od pobierania jedną wartość za pośrednictwem tego samego planu dostępu.

 0
Author: David Aldridge,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-18 16:48:37

Jeśli możesz użyć innych rzeczy niż w: zrób to (używałem w w niektórych przypadkach nie bardzo dobry sposób : mogę łatwo zastąpić exist i jest szybciej)

W Twoim przypadku: nie wydaje się tak źle.

 0
Author: Hugues Van Landeghem,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-18 16:50:26

Możesz spróbować czegoś takiego:

select a.FieldX, a.FieldY
from (
    select FieldW = 108 union
    select FieldW = 109 union
    select FieldW = 113 union
    ...
    select FieldW = 891
) _a
join A a on a.FieldW = _a.FieldW

Może to być odpowiednie dla twojej sytuacji, na przykład gdy chcesz dynamicznie wygenerować pojedyncze polecenie SQL. Na moim komputerze (SQL Server 2008 Express), testowanie z małą liczbą (5) wartości FieldW i dużą liczbą (100,000) wierszy W A, używa wyszukiwania indeksu na a z zagnieżdżoną pętlą łączącą się między A i _a, co prawdopodobnie jest tym, czego szukasz.

 0
Author: yfeldblum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-18 17:29:31