Najszybszy sposób ustalenia, czy rekord istnieje

143

Jak sugeruje tytuł ... Próbuję znaleźć najszybszy sposób przy jak najmniejszym narzucie, aby określić, czy rekord istnieje w tabeli, czy nie.

Przykładowe zapytanie:

SELECT COUNT(*) FROM products WHERE products.id = ?;

    vs

SELECT COUNT(products.id) FROM products WHERE products.id = ?;

    vs

SELECT products.id FROM products WHERE products.id = ?;

Powiedzmy, że ?jest zamienione na 'TB100'... zarówno pierwsze, jak i drugie zapytanie zwróci dokładnie ten sam wynik (powiedzmy ... 1dla tej rozmowy). Ostatnie zapytanie zwróci 'TB100'zgodnie z oczekiwaniami lub nic, jeśli idnie ma w tabeli.

Celem jest ustalenie, czy idjest w tabeli, czy nie. Jeśli nie, program następnie wstawi rekord, jeśli tak, program go pominie lub wykona zapytanie UPDATE w oparciu o inną logikę programu spoza zakresu tego pytania.

Który jest szybszy i mniejszy? (Będzie to powtarzane dziesiątki tysięcy razy podczas każdego uruchomienia programu i będzie uruchamiane wiele razy dziennie).

(Uruchamianie tego zapytania względem M $ SQL Server z Java za pośrednictwem sterownika JDBC dostarczonego przez M $)

SnakeDoc
źródło
1
Może to zależeć od bazy danych. Na przykład liczenie na Postgres jest raczej powolne.
Mike Christensen
Przepraszamy, to jest Java komunikująca się z M $ SQL przez sterownik jdbc. Zaktualizuję mój OP.
SnakeDoc,
2
Jest istnieje również.
Nikola Markovinović
@Nikola Markovinović: jak byś go użył w tym przypadku?
zerkms
5
@zerkms Zależy od kontekstu. Jeśli w procedurze składowanej byłoby if exists(select null from products where id = @id); jeśli w zapytaniu wywoływanym bezpośrednio przez klienta select case when exists (...) then 1 else 0 end.
Nikola Markovinović

Odpowiedzi:

170

SELECT TOP 1 products.id FROM products WHERE products.id = ?; osiągnie lepsze wyniki niż wszystkie Twoje sugestie, ponieważ zakończy wykonywanie po znalezieniu pierwszego rekordu.

Declan_K
źródło
5
Czy optymalizator nie bierze tego pod uwagę podczas przeszukiwania PK (lub innego unikalnego klucza)?
zerkms
3
Nigdy nie stwierdził, że to PK, ale jeśli tak, to tak, optymalizator weźmie to pod uwagę.
Declan_K,
3
@Declan_K: wygląda na to, że moja magiczna kula zawiodła w tym przypadku i kolumna zatytułowana jak idnie jest PK. Więc +1 do twojej rady.
zerkms
4
Jeśli nie jest to PK, sugerowałbym również upewnienie się, że w tej kolumnie jest indeks. W przeciwnym razie zapytanie będzie musiało wykonać skanowanie tabeli zamiast szybszego wyszukiwania tabeli.
CD Jorgensen
3
Myślę, że powinniśmy rozważyć odpowiedź @ nenad-zivkovic zamiast tego.
Giulio Caccin
192

EXISTS(lub NOT EXISTS) jest specjalnie zaprojektowany do sprawdzania, czy coś istnieje i dlatego powinien być (i jest) najlepszą opcją. Zatrzyma się w pierwszym wierszu, który pasuje, więc nie wymaga TOPklauzuli i w rzeczywistości nie wybiera żadnych danych, więc nie ma narzutu związanego z rozmiarem kolumn. Możesz bezpiecznie użyć SELECT *tutaj - nie różni się od SELECT 1, SELECT NULLlub SELECT AnyColumn... (możesz nawet użyć niepoprawnego wyrażenia, takiego jak SELECT 1/0i nie zepsuje się) .

IF EXISTS (SELECT * FROM Products WHERE id = ?)
BEGIN
--do what you need if exists
END
ELSE
BEGIN
--do what needs to be done if not
END
Nenad Zivkovic
źródło
czy nie musi to najpierw wykonać instrukcji SELECT, a następnie instrukcji IF EXISTS ... powodując dodatkowe obciążenie, a tym samym dłuższy czas przetwarzania?
SnakeDoc
7
@SnakeDoc No. Existsdziała selectw taki sposób, że kończy pracę, gdy tylko zostanie znaleziony jeden wiersz. Ponadto istnieje po prostu odnotowuje istnienie rekordu, a nie rzeczywiste wartości w rekordzie, oszczędzając potrzebę ładowania wiersza z dysku (oczywiście zakładając, że kryteria wyszukiwania są indeksowane). Jeśli chodzi o koszty ogólne if- i tak będziesz musiał spędzić ten malutki czas.
Nikola Markovinović
1
@ NikolaMarkovinović ciekawy punkt. Nie jestem pewien, czy indeks istnieje w tym polu, a mój nowicjusz SQL nie wie, jak się tego dowiedzieć. Pracuję z tą bazą danych z Javy przez JDBC, a baza danych znajduje się gdzieś zdalnie w colo. Otrzymałem tylko „podsumowanie bazy danych”, które zawiera szczegółowe informacje o polach istniejących w każdej tabeli, ich typach i dowolnych polach FK lub PK. Czy to coś zmienia?
SnakeDoc
3
@SnakeDoc Aby dowiedzieć się o strukturze tabeli, w tym o kluczach obcych i indeksach, uruchom sp_help nazwa_tabeli . Indeksy są niezbędne, jeśli chodzi o pobieranie kilku wierszy z wielu, niezależnie od tego, czy używasz select toplub exists; jeśli ich nie ma, silnik sql będzie musiał wykonać skanowanie tabeli. Jest to najmniej pożądana opcja wyszukiwania tabeli. Jeśli nie masz uprawnień do tworzenia indeksów, będziesz musiał skontaktować się z personelem technicznym po drugiej stronie, aby dowiedzieć się, czy dostosowują je automatycznie, czy też oczekują, że zasugerujesz indeksy.
Nikola Markovinović
1
@Konstantin Możesz zrobić coś takiegoSELECT CASE WHEN EXISTS(..) THEN 1 ELSE 0 END;
Nenad Zivkovic
21

Nic nie może pokonać -

SELECT TOP 1 1 FROM products WHERE id = 'some value';

Nie musisz liczyć, aby wiedzieć, czy w tabeli znajdują się dane. I nie używaj aliasu, gdy nie jest to konieczne.

AgentSQL
źródło
5
Mimo swojej nazwy idnie ma klucza podstawowego. Tak więc, nawet jeśli nie liczysz , nadal musisz znaleźć wszystkie pasujące rekordy, być może tysiące z nich. O aliasowaniu - kod jest ciągle w toku. Nigdy nie wiesz, kiedy będziesz musiał wrócić. Aliasing pomaga zapobiegać głupim błędom w czasie wykonywania; na przykład unikalna nazwa kolumny, która nie wymagała aliasu, nie jest już unikalna, ponieważ ktoś utworzył kolumnę o tej samej nazwie w innej, połączonej tabeli.
Nikola Markovinović
Tak, masz całkowitą rację. Aliasing bardzo pomaga, ale nie sądzę, że ma to jakiekolwiek znaczenie, gdy nie używa się łączenia. Więc powiedziałem, nie używaj go, jeśli nie jest to konieczne. :) A można znaleźć długą dyskusję tutaj na sprawdzanie istnienia. :)
AgentSQL,
3
Nie wiem, dlaczego zaakceptowałem ten termin aliasing. Poprawny termin to qualifying. Oto dłuższe wyjaśnienie Alexa Kuznetzova . O zapytaniami jednej tabeli - jest to pojedynczy stół teraz . Ale później, gdy zostanie wykryty błąd i próbujesz powstrzymać powódź, klient jest zdenerwowany, dołączasz do innego stołu tylko po to, aby zmierzyć się z komunikatem o błędzie - łatwo korygowanym komunikatem, ale nie w tym spoconym momencie, uderza mały skok - i poprawiasz błąd zapamiętywania, że ​​nigdy nie opuszczałem kolumny ...
Nikola Markovinović
1
Nie mogę tego teraz zignorować. Dzięki!! :)
AgentSQL,
15
SELECT CASE WHEN EXISTS (SELECT TOP 1 *
                         FROM dbo.[YourTable] 
                         WHERE [YourColumn] = [YourValue]) 
            THEN CAST (1 AS BIT) 
            ELSE CAST (0 AS BIT) END

To podejście zwraca dla Ciebie wartość logiczną.

Kris Coleman
źródło
1
Prawdopodobnie można pominąć instrukcję Top i *, aby uczynić ją nieco szybszą, ponieważ Exist zakończy działanie po znalezieniu rekordu, więc coś takiego: SELECT CASE WHEN EXISTS (SELECT 1 FROM dbo. [YourTable] WHERE [YourColumn] = [YourValue]) THEN CAST (1 AS BIT) INNE CAST (0 AS BIT) END
Stefan Zvonar
Ta sugestia nie wspomina, dlaczego byłoby to szybsze w porównaniu z wbudowanymi instrukcjami istnieje / nie istnieje w SQL Server. Bez analizy porównawczej trudno byłoby mi uwierzyć, że opis przypadku przyniesie szybszy wynik niż natychmiastowa odpowiedź prawda / fałsz.
Bonez024
8

Możesz także użyć

 If EXISTS (SELECT 1 FROM dbo.T1 WHERE T1.Name='Scot')
    BEGIN
         --<Do something>
    END 

ELSE    
     BEGIN
       --<Do something>
     END
atik sarker
źródło
7

Nie myśl, że ktoś jeszcze o tym wspomniał, ale jeśli masz pewność, że dane pod tobą się nie zmienią, możesz również zastosować wskazówkę NoLock, aby upewnić się, że nie jest blokowana podczas czytania.

SELECT CASE WHEN EXISTS (SELECT 1 
                     FROM dbo.[YourTable] WITH (NOLOCK)
                     WHERE [YourColumn] = [YourValue]) 
        THEN CAST (1 AS BIT) 
        ELSE CAST (0 AS BIT) END
Stefan Zvonar
źródło
3
SELECT COUNT(*) FROM products WHERE products.id = ?;

Jest to rozwiązanie obejmujące wiele relacyjnych baz danych, które działa we wszystkich bazach danych.

nieuczciwy chłopak
źródło
7
Jednak zmuszasz db do zapętlenia wszystkich rekordów, bardzo wolno w przypadku dużych tabel
amd
@amd chcesz wyjaśnić, dlaczego?
UmNyobe
@amd Twój komentarz ma sens. To zapytanie to bardziej ZNAJDŹ WSZYSTKO niż ZNAJDŹ WSZELKIE
UmNyobe
1

Poniżej znajduje się najprostszy i najszybszy sposób określenia, czy rekord istnieje w bazie danych, czy nie. Dobrze, że działa we wszystkich relacyjnych bazach danych

SELECT distinct 1 products.id FROM products WHERE products.id = ?;
manish Prasad
źródło
0
create or replace procedure ex(j in number) as
i number;
begin
select id into i from student where id=j;
if i is not null then
dbms_output.put_line('exists');
end if;
exception
   when no_data_found then
        dbms_output.put_line(i||' does not exists');

end;
kiran
źródło
2
Być może twój kod działa świetnie, ale byłoby lepiej, gdybyś dodał dodatkowe informacje, aby były lepiej zrozumiałe.
idmean
0

Używałem tego w przeszłości i nie wymaga pełnego skanowania tabeli, aby sprawdzić, czy coś istnieje. Jest super szybki ...

UPDATE TableName SET column=value WHERE column=value
IF @@ROWCOUNT=0
BEGIN
     --Do work
END             
Eric Parsons
źródło
0

Dla tych, którzy natkną się na to w tle MySQL lub Oracle - MySQL obsługuje klauzulę LIMIT, aby wybrać ograniczoną liczbę rekordów, podczas gdy Oracle używa ROWNUM.

Werner
źródło