2012-06-21 17 views
25

Mam tabelę SQL BookChapters z ponad 20 milionami wierszy. Ma klastrowany klucz podstawowy (bookChapterID) i nie ma żadnych innych kluczy ani indeksów. To trwa milisekundy uruchomić następujące zapytanieLiczba obliczeń SQL (*)

if (select count(*) from BookChapters) = 0 
... 

Jednak trwa ponad 10 minut, kiedy mogę ją zmienić jak tak

if (select count(*) from BookChapters) = 1 
... 

lub

if (select count(*) from BookChapters) > 1 
... 

Dlaczego tak jest? Jak uzyskać select count(*), aby działał szybciej?

+2

Generalnie wybierz " count (*) from table "- może mieć duże problemy z większymi danymi;) –

Odpowiedz

40

Mikael Eriksson ma dobre wytłumaczenie miech dlaczego pierwsze zapytanie jest szybki:

serwer SQL zoptymalizować go do: if exists(select * from BookChapters). Czyli szuka obecności jednego rzędu zamiast zliczania wszystkich wierszy w tabeli.

Dla pozostałych dwóch zapytań SQL Server użyłby następującej reguły. Aby wykonać kwerendę taką jak SELECT COUNT(*), SQL Server użyje najwęższego nieklastrowego indeksu, aby zliczyć wiersze. Jeśli tabela nie ma żadnego nieklastrowanego indeksu, będzie musiała przeskanować tabelę.

Ponadto, jeśli tabela ma indeks skupione można uzyskać liczyć nawet szybciej za pomocą następującej kwerendy (zapożyczoną z tej strony Get Row Counts Fast!)

--SQL Server 2005/2008 
SELECT OBJECT_NAME(i.id) [Table_Name], i.rowcnt [Row_Count] 
FROM sys.sysindexes i WITH (NOLOCK) 
WHERE i.indid in (0,1) 
ORDER BY i.rowcnt desc 

--SQL Server 2000 
SELECT OBJECT_NAME(i.id) [Table_Name], i.rows [Row_Count] 
FROM sysindexes i (NOLOCK) 
WHERE i.indid in (0,1) 
ORDER BY i.rows desc 

Wykorzystuje tabelę systemową sysindexes. Więcej informacji można znaleźć tutaj SQL Server 2000, SQL Server 2005, SQL Server 2008, SQL Server 2012

Oto inny link Why is my SELECT COUNT(*) running so slow? z innego rozwiązania. Pokazuje technikę, której używa Microsoft do szybkiego wyświetlania liczby wierszy po kliknięciu prawym przyciskiem myszy na stole i wybraniu właściwości.

select sum (spart.rows) 
from sys.partitions spart 
where spart.object_id = object_id(’YourTable’) 
and spart.index_id < 2 

Powinieneś odkryć, że to szybko powraca, bez względu na to, ile masz stołów.

Jeśli nadal korzystasz z SQL 2000, możesz użyć tabeli sysindexes, aby uzyskać numer.

select max(ROWS) 
from sysindexes 
where id = object_id(’YourTable’) 

Ta liczba może być nieco off w zależności od tego, jak często aktualizuje tabelę SQL sysindexes, ale to zwykle corrent (lub przynajmniej na tyle blisko).

+1

Czy zgadzasz się z moją sugestią? –

+1

Cześć Aleksey, jesteś geniuszem, to jest, byłem po prostu ciekawy, że mój nowy stół BookChapters2 znacznie szybciej zwróci liczbę zapytań, zobaczyłem, tak jak powiedziałeś, ponieważ w tabeli znajduje się indeks inny niż klaster, dzięki dużo – danmiao

+1

+1 za wspaniałą odpowiedź - dzięki! Tylko jeden dodatkowy punkt. Upewnij się, że indeksy/statystyki są aktualne w tabeli, której dotyczy zapytanie. Ponieważ używa tych statystyk do obliczeń, nieaktualne statystyki dostarczają niedokładnych wyników. – jabs

4

Czy wzięto pod uwagę zapytanie select count(BookChapterId) from BookChapterTable? - gdzie `BookChapterId jest nieklastrowym indeksem. To powinno sprawić, że będzie działać znacznie szybciej.

Zależnie od tabela służy i wiersze dostępne, zapytań przeciwko indeks nieklastrowany może być kluczowy punkt: Właśnie zrobiłem kilka punktów z MDSN:

  • Przed utworzeniem indeksy nieklastrowany, zrozumieć swoje dane otrzyma dostęp do . Rozważ użycie indeksów nieklastrowanych dla:
  • Kolumny zawierające dużą liczbę różnych wartości, takich jak
    kombinacja nazwiska i imienia (jeśli indeks klastrowy jest używany dla innych kolumn). Jeśli istnieje bardzo niewiele różnych wartości, takich jak
    tylko 1 i 0, większość zapytań nie będzie korzystać z indeksu, ponieważ skanowanie tabeli jest zwykle bardziej wydajne.
  • Zapytania, które nie zwracają dużych zestawów wyników.
  • Kolumny często uczestniczące w warunkach wyszukiwania zapytania (WHERE
    ), które zwracają dokładne dopasowania.
  • Aplikacje wspomagające podejmowanie decyzji, do których często są wymagane łączenia i grupowanie, są często wymagane w przypadku . Utwórz wiele indeksów nieklastrowych w kolumnach związanych z operacjami łączenia i grupowania oraz indeks klastrowany w kolumnach klucza obcego na .
  • Obejmowanie wszystkich kolumn z jednej tabeli w danym zapytaniu. Eliminuje to dostęp do tabeli lub indeks klastrowany w ogóle.
+0

Cześć EIYusubov, testowałem, bez różnicy, wciąż bardzo wolno – danmiao

+0

proszę powrócić do indeksów, być może masz złożone indeksy lub musisz je odbudować . –

+0

hi EIYusubov, jest to indeks pojedynczej kolumny klastra, nie złożone indeksy, mam usunąć klucz podstawowy i odtworzyć go, ale bez różnicy. – danmiao

7

Jeśli spojrzysz na plany wykonania swoich zapytań, zobaczysz, co się dzieje.

Twoje pierwsze zapytanie if (select count(*) from BookChapters) = 0 jest rozpoznawane przez optymalizator zapytań tak samo, jak if exists(select * from BookChapters). SQL Server wie, że wyrażenie jest prawdziwe, jeśli obecny jest przynajmniej jeden wiersz, więc szuka obecności jednego wiersza zamiast zliczać wszystkie wiersze w tabeli.

Dla innych zapytań nie może być tak inteligentny i musi policzyć liczbę wierszy w tabeli, zanim będzie mógł zdecydować, czy wyrażenie ma wartość prawda czy fałsz.

3

spróbować, jeśli trzeba wykryć, jeśli tabela ma więcej wierszy niż jednego:

if (SELECT COUNT(*) FROM (SELECT TOP 2 * FROM BookChapters) AS b) > 1 
11

spróbować jeśli tylko chcą wiedzieć policzyć wiersze:

exec sp_spaceused [TABLE_NAME]