Dlaczego program SQL Server zwraca niektóre wiersze podczas wykonywania zapytania, a czasami nie?

33

Są zapytania, w których gdy klikniemy „wykonaj”, pokazuje niektóre wiersze i wciąż rośnie, ale zapytanie jeszcze się nie skończyło. Czasami jednak czeka do końca zapytania.

Dlaczego to się dzieje? Czy istnieje sposób, aby to kontrolować?

Racer SQL
źródło

Odpowiedzi:

43

Odpowiedź, jak zwykle (w porządku, przez większość czasu), leży w planie wykonania.

Istnieją pewne operatory, które wymagają, aby wszystkie wiersze do nich dotarły, zanim będą mogły rozpocząć przetwarzanie tych wierszy i przekazywanie ich dalej, na przykład:

  • Hash Join (przy budowaniu tabeli skrótów)
  • Hash Match
  • Sortuj (z wyjątkiem wyróżnienia przepływu mieszania)

Z tego powodu nazywane są albo blokowaniem, albo operatorami „zatrzymaj i jedź”, i często są wybierani, gdy optymalizator uważa, że ​​będzie musiał przetworzyć całą masę danych, aby znaleźć twoje dane.

Istnieją inni operatorzy, którzy mogą natychmiast rozpocząć transmisję strumieniową lub przekazać znalezione wiersze

  • Pętle zagnieżdżone
  • Obsługiwane połączenia scalone przez indeks
  • Stream Aggregates

Gdy zapytania zaczynają zwracać dane natychmiast, ale nie kończą się natychmiast, zwykle jest to znak, że optymalizator wybrał plan szybkiego zlokalizowania i zwrócenia niektórych wierszy przy użyciu operatorów o niższych kosztach uruchomienia.

Może się to zdarzyć z powodu celów rzędów wprowadzonych przez Ciebie lub przez optymalizatora.

Może się to również zdarzyć, jeśli z jakiegoś powodu zostanie wybrany zły plan (brak SARGability, wąchanie parametrów, niewystarczająca statystyka itp.), Ale to wymaga więcej wykopalisk.

Aby uzyskać więcej informacji, sprawdź blog Roba Farleya tutaj

I seria Paula White'a na temat bramek tutaj , tutaj , tutaj i tutaj .

Należy również zauważyć, że jeśli mówimy o SSMS, wiersze pojawiają się tylko po wypełnieniu całego bufora, a nie tylko nie chcąc.

Erik Darling
źródło
14

Jeśli rozumiem, co obserwujesz, w ten sposób Management Studio renderuje wiersze i ma niewiele wspólnego z tym, jak SQL Server zwraca wiersze. W rzeczywistości często, gdy zwracasz duże wyniki do SSMS i próbujesz wyrenderować je w siatce, SSMS nie może nadążyć, a SQL Server czeka na przetworzenie przez aplikację większej liczby wierszy. W takim przypadku zobaczysz, że SQL Server gromadzi ASYNC_NETWORK_IOoczekiwania.

Możesz to nieco kontrolować, używając wyników do tekstu zamiast wyników do siatki, ponieważ SSMS może rysować tekst szybciej niż może rysować siatki, ale prawdopodobnie okaże się, że może to wpłynąć na czytelność w zależności od liczby kolumn i typów danych. Oba mają wpływ na to, kiedy SSMS decyduje się faktycznie zapisać wyniki w tym okienku, co zależy od tego, jak pełny jest bufor wyjściowy.

Jeśli masz wiele instrukcji i chcesz zmusić bufor do renderowania wyników w okienku komunikatów, możesz zastosować małą sztuczkę drukowania między instrukcjami:

RAISERROR('', 0, 1) WITH NOWAIT;

Ale to nie pomoże, gdy próbujesz zmusić SSMS do szybszego renderowania wierszy, gdy wszystkie dane wyjściowe pochodzą z jednej instrukcji.

Bardziej bezpośrednio, możesz kontrolować to, ograniczając liczbę wyników renderowanych w SSMS. Często widzę, jak ludzie narzekają na to, ile czasu zajmuje powrót miliona wierszy do siatki. Nie mam pojęcia, co, u licha, zrobi milion wierszy w siatce SSMS.

Istnieje kilka takich hacków OPTION (FAST 100), które zoptymalizują pobieranie pierwszych 100 wierszy (lub dowolnych 100 wierszy, jeśli nie ma zewnętrznego ORDER BY), ale może to kosztować znacznie wolniejsze wyszukiwanie pozostałych wierszy i planu, który jest bardziej ogólnie nieefektywne, więc tak naprawdę nie jest to opcja IMHO.

Aaron Bertrand
źródło
1

Twoje pytanie nie dotyczy samego SQLServera, ale:

  • SQLServer
  • sieć
  • SSMS jako aplikacja kliencka

Czy istnieje sposób, aby to kontrolować?

Krótka odpowiedź :

  1. Spróbuj sqlcmdzamiast ssmslub sqlcmd-mode zssms
  2. Sprawdź ustawienia połączenia i sesji

Długa odpowiedź :

Oczywiście! Ale nie jeden - prob

  1. Wykonaj zapytanie za pomocą sqlcmdlub w sqlcmdtrybie -mms w ssms.
  2. Jeśli chcesz wykluczyć rolę sieci - uruchom zapytanie na serwerze z połączeniem z pamięcią współdzieloną.
  3. Jeśli wydajność zapytania jest niezadowalająca nawet w przypadku połączenia z pamięcią współużytkowaną - przeanalizuj swoje plany wykonania. Jeśli zapytanie działa nieprawidłowo przez sieć - skontaktuj się z administratorem sieci w celu uzyskania pomocy. Jeśli twoje zapytanie działa źle tylko w SSMS - czytaj dalej.
  4. Teraz jesteśmy pewni, że problemy występują po stronie klienta (w tym przypadku ssms). Spójrz na ustawienia połączenia i sesji w SSMS. Nie wierz w interfejs ssms i sprawdź za pomocą SQL Profiler: znajdź swoje połączenie, spida otrzymasz pełną listę ustawień sesji. Porównaj z ustawieniami sqlcmdsesji. Jeśli nic nie kliknie - skopiuj wszystkie ustawienia sesji z profilera do skryptu zapytania, uruchom w trybie sqlcmd-mode i stopniowo zmieniając ustawienia znajdziesz swojego winowajcę.

Powodzenia!

Alex Yu
źródło
-2

Aby dodać do odpowiedzi sp_BlitzErik, weź przykład używając a NOT IN ()z wyborem podrzędnym. Aby ustalić, czy element jest wynikiem zagnieżdżonego zapytania, (ogólnie) konieczne jest pobranie całego wyniku.

Tak więc jednym ze sposobów, w jaki znalazłem sposób na poprawienie wydajności takich zapytań, jest przepisanie ich tak, aby LEFT OUTER JOINwarunek dla RIGHTstrony był zerowy (możesz to odwrócić, oczywiście, ale kto używa RIGHT OUTER JOINS?). Pozwala to na natychmiastowe rozpoczęcie wyników.

JimmyJames
źródło
Nie wydaje mi się Jeśli porównywane kolumny nie mają wartości zerowej, wówczas wyniki powinny być takie same, a plany - zwykle - takie same, dla 3 wersji antijoin (NIE W, NIE ISTNIEJĄ, POŁĄCZENIE Z LEWEM / JEST NULL). Nie trzeba pobierać całego wyniku.
ypercubeᵀᴹ
Jeśli podselekcja jest naprawdę złożona, że ​​wygenerowane zapytanie musi ocenić cały podselekcja przed sprawdzeniem warunku BRAK WEJŚCIA WHERE t.x IN (<complex SELECT subquery>), równoważne LEWE DOŁĄCZ LEFT JOIN (<complex SELECT subquery>) AS r ON r.x = t.x .... WHERE r.x IS NULL, wtedy również podzapytanie będzie musiało zostać ocenione (tak samo złożony plan z NOT W wersji).
ypercubeᵀᴹ
@ ypercubeᵀᴹ To działało dla mnie w przeszłości. Widziałem, jak zapytania wracają od minut do powrotu do drugiej sekundy.
JimmyJames
@ ypercubeᵀᴹ Złożyłem prosty przykład w Oracle (przepraszam, że w tej chwili nie mam dostępu do SQLServer) i zdecydowanie mieli inne plany wyjaśniania. Być może nie były to znaczące różnice, ale wyglądają zupełnie inaczej.
JimmyJames,
@JimmyJames: nie jest to prosty sposób, jeśli chcesz uzyskać stabilną wydajność, a takie „optymalizacje” są bardzo wrażliwe na wersję SQLServer. I nie popełniaj błędu przemawiającego do Oracle (która wersja?). Historycznie SQLServer preferował, NOT EXISTSale Oracle NOT INw zapytaniach. Ale dziś należy to uznać za błąd w generatorze planów
Alex Yu