Zapytanie działa szybko:
DECLARE @SessionGUID uniqueidentifier
SET @SessionGUID = 'BCBA333C-B6A1-4155-9833-C495F22EA908'
SELECT *
FROM Report_Opener
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank
koszt poddrzewa: 0,502
Ale umieszczenie tego samego kodu SQL w procedurze przechowywanej przebiega powoli i przy zupełnie innym planie wykonania
CREATE PROCEDURE dbo.ViewOpener @SessionGUID uniqueidentifier AS
SELECT *
FROM Report_Opener
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank
EXECUTE ViewOpener @SessionGUID
Koszt poddrzewa: 19,2
Uciekłem
sp_recompile ViewOpener
I nadal działa tak samo (źle), a także zmieniłem procedurę przechowywaną na
CREATE PROCEDURE dbo.ViewOpener @SessionGUID uniqueidentifier AS
SELECT *, 'recompile please'
FROM Report_Opener
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank
I znowu, próbując naprawdę oszukać to w ponownej kompilacji.
Usunąłem i odtworzyłem procedurę przechowywaną, aby wygenerować nowy plan.
Próbowałem wymusić rekompilację i zapobiec wąchaniu parametrów za pomocą zmiennej wabika:
CREATE PROCEDURE dbo.ViewOpener @SessionGUID uniqueidentifier AS
DECLARE @SessionGUIDbitch uniqueidentifier
SET @SessionGUIDbitch = @SessionGUID
SELECT *
FROM Report_Opener
WHERE SessionGUID = @SessionGUIDbitch
ORDER BY CurrencyTypeOrder, Rank
Próbowałem również zdefiniować procedurę przechowywaną WITH RECOMPILE
:
CREATE PROCEDURE dbo.ViewOpener @SessionGUID uniqueidentifier
WITH RECOMPILE
AS
SELECT *
FROM Report_Opener
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank
Aby jego plan nigdy nie był buforowany, próbowałem wymusić rekompilację przy wykonywaniu:
EXECUTE ViewOpener @SessionGUID WITH RECOMPILE
Co nie pomogło.
Próbowałem przekonwertować procedurę na dynamiczny SQL:
CREATE PROCEDURE dbo.ViewOpener @SessionGUID uniqueidentifier
WITH RECOMPILE AS
DECLARE @SQLString NVARCHAR(500)
SET @SQLString = N'SELECT *
FROM Report_OpenerTest
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank'
EXECUTE sp_executesql @SQLString,
N'@SessionGUID uniqueidentifier',
@SessionGUID
Co nie pomogło.
Podmiot „ Report_Opener
” to widok, który nie jest indeksowany. Widok odwołuje się tylko do tabel bazowych. Żadna tabela nie zawiera kolumn obliczanych, indeksowanych lub innych.
Do diabła z tym próbowałem stworzyć widok z
SET ANSI_NULLS ON
SET QUOTED_IDENTIFER ON
To nie naprawiło tego.
Jak to jest to
- zapytanie jest szybkie
- przeniesienie zapytania do widoku i wybranie go z widoku jest szybkie
- wybranie z widoku z procedury składowanej jest 40x wolniejsze?
Próbowałem przenieść definicję widoku bezpośrednio do procedury składowanej (naruszenie 3 reguł biznesowych i złamanie ważnej enkapsulacji), co powoduje, że jest ona około 6 razy wolniejsza.
Dlaczego wersja procedury przechowywanej jest tak wolna? Co może tłumaczyć, że SQL Server działa ad-hoc SQL szybciej niż inny rodzaj SQL ad-hoc?
Naprawdę wolałbym nie
- osadzić SQL w kodzie
w ogóle zmień kod
Microsoft SQL Server 2000 - 8.00.2050 (Intel X86) Mar 7 2008 21:29:56 Copyright (c) 1988-2003 Microsoft Corporation Standard Edition on Windows NT 5.2 (Build 3790: Service Pack 2)
Ale co może tłumaczyć, że SQL Server nie może działać tak szybko, jak SQL Sever uruchamiający zapytanie, jeśli nie wąchanie parametrów.
Moja kolejna próba będzie mieć StoredProcedureA
połączenie StoredProcedureB
wywołanie StoredProcedureC
połączenie, StoredProcedureD
aby wykonać zapytanie do widoku.
W przeciwnym razie poproś procedurę przechowywaną o wywołanie procedury przechowywanej, wywołanie UDF, wywołanie UDF, wywołanie procedury składowanej, wywołanie UDF w celu zapytania o widok.
Podsumowując, następujące są uruchamiane szybko z kontroli jakości, ale są powolne po wprowadzeniu do procedury składowanej:
Oryginalny:
--Runs fine outside of a stored procedure
SELECT *
FROM Report_OpenerTest
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank
sp_executesql
:
--Runs fine outside of a stored procedure
DECLARE @SQLString NVARCHAR(500)
SET @SQLString = N'SELECT *
FROM Report_OpenerTest
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank'
EXECUTE sp_executesql @SQLString,
N'@SessionGUID uniqueidentifier',
@SessionGUID
EXEC(@sql)
:
--Runs fine outside of a stored procedure
DECLARE @sql NVARCHAR(500)
SET @sql = N'SELECT *
FROM Report_OpenerTest
WHERE SessionGUID = '''+CAST(@SessionGUID AS varchar(50))+'''
ORDER BY CurrencyTypeOrder, Rank'
EXEC(@sql)
Plany wykonania
Dobry planu:
|--Sort(ORDER BY:([Expr1020] ASC, [Currencies].[Rank] ASC))
|--Compute Scalar(DEFINE:([Expr1020]=If ([Currencies].[CurrencyType]='ctCanadianCash') then 1 else If ([Currencies].[CurrencyType]='ctMiscellaneous') then 2 else If ([Currencies].[CurrencyType]='ctTokens') then 3 else If ([Currencies].[CurrencyType]
|--Nested Loops(Left Outer Join, OUTER REFERENCES:([Openers].[OpenerGUID]))
|--Filter(WHERE:((([Currencies].[IsActive]<>0 AND [Currencies].[OnOpener]<>0) AND ((((((([Currencies].[CurrencyType]='ctUSCoin' OR [Currencies].[CurrencyType]='ctMiscellaneousUS') OR [Currencies].[CurrencyType]='ctUSCash') OR [Currencies].
| |--Nested Loops(Left Outer Join, OUTER REFERENCES:([Currencies].[CurrencyGUID], [Openers].[OpenerGUID]) WITH PREFETCH)
| |--Nested Loops(Left Outer Join)
| | |--Bookmark Lookup(BOOKMARK:([Bmk1016]), OBJECT:([GrobManagementSystemLive].[dbo].[Windows]))
| | | |--Nested Loops(Inner Join, OUTER REFERENCES:([Openers].[WindowGUID]))
| | | |--Bookmark Lookup(BOOKMARK:([Bmk1014]), OBJECT:([GrobManagementSystemLive].[dbo].[Openers]))
| | | | |--Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[Openers].[IX_Openers_SessionGUID]), SEEK:([Openers].[SessionGUID]=[@SessionGUID]) ORDERED FORWARD)
| | | |--Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[Windows].[IX_Windows]), SEEK:([Windows].[WindowGUID]=[Openers].[WindowGUID]) ORDERED FORWARD)
| | |--Clustered Index Scan(OBJECT:([GrobManagementSystemLive].[dbo].[Currencies].[IX_Currencies_CurrencyType]))
| |--Clustered Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[OpenerDetails].[IX_OpenerDetails_OpenerGUIDCurrencyGUID]), SEEK:([OpenerDetails].[OpenerGUID]=[Openers].[OpenerGUID] AND [OpenerDetails].[CurrencyGUID]=[Currenc
|--Hash Match(Cache, HASH:([Openers].[OpenerGUID]), RESIDUAL:([Openers].[OpenerGUID]=[Openers].[OpenerGUID]))
|--Stream Aggregate(DEFINE:([Expr1006]=SUM(If (((([Currencies].[CurrencyType]='ctMiscellaneous' OR [Currencies].[CurrencyType]='ctTokens') OR [Currencies].[CurrencyType]='ctChips') OR [Currencies].[CurrencyType]='ctCanadianCoin') OR [
|--Nested Loops(Inner Join, OUTER REFERENCES:([OpenerDetails].[CurrencyGUID]) WITH PREFETCH)
|--Nested Loops(Inner Join)
| |--Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[Openers].[IX_Openers_OneOpenerPerSession]), SEEK:([Openers].[OpenerGUID]=[Openers].[OpenerGUID]) ORDERED FORWARD)
| |--Clustered Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[OpenerDetails].[IX_OpenerDetails_OpenerGUIDCurrencyGUID]), SEEK:([OpenerDetails].[OpenerGUID]=[Openers].[OpenerGUID]) ORDERED FORWARD)
|--Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[Currencies].[PK_Currencies_CurrencyGUID]), SEEK:([Currencies].[CurrencyGUID]=[OpenerDetails].[CurrencyGUID]) ORDERED FORWARD)
Złe planu
|--Sort(ORDER BY:([Expr1020] ASC, [Currencies].[Rank] ASC))
|--Compute Scalar(DEFINE:([Expr1020]=If ([Currencies].[CurrencyType]='ctCanadianCash') then 1 else If ([Currencies].[CurrencyType]='ctMiscellaneous') then 2 else If ([Currencies].[CurrencyType]='ctTokens') then 3 else If ([Currencies].[Currency
|--Nested Loops(Left Outer Join, OUTER REFERENCES:([Openers].[OpenerGUID]))
|--Filter(WHERE:((([Currencies].[IsActive]<>0 AND [Currencies].[OnOpener]<>0) AND ((((((([Currencies].[CurrencyType]='ctUSCoin' OR [Currencies].[CurrencyType]='ctMiscellaneousUS') OR [Currencies].[CurrencyType]='ctUSCash') OR [Currenc
| |--Nested Loops(Left Outer Join, OUTER REFERENCES:([Currencies].[CurrencyGUID], [Openers].[OpenerGUID]) WITH PREFETCH)
| |--Filter(WHERE:([Openers].[SessionGUID]=[@SessionGUID]))
| | |--Concatenation
| | |--Nested Loops(Left Outer Join)
| | | |--Table Spool
| | | | |--Hash Match(Inner Join, HASH:([Windows].[WindowGUID])=([Openers].[WindowGUID]), RESIDUAL:([Windows].[WindowGUID]=[Openers].[WindowGUID]))
| | | | |--Clustered Index Scan(OBJECT:([GrobManagementSystemLive].[dbo].[Windows].[IX_Windows_CageGUID]))
| | | | |--Table Scan(OBJECT:([GrobManagementSystemLive].[dbo].[Openers]))
| | | |--Table Spool
| | | |--Clustered Index Scan(OBJECT:([GrobManagementSystemLive].[dbo].[Currencies].[IX_Currencies_CurrencyType]))
| | |--Compute Scalar(DEFINE:([Openers].[OpenerGUID]=NULL, [Openers].[SessionGUID]=NULL, [Windows].[UseChipDenominations]=NULL))
| | |--Nested Loops(Left Anti Semi Join)
| | |--Clustered Index Scan(OBJECT:([GrobManagementSystemLive].[dbo].[Currencies].[IX_Currencies_CurrencyType]))
| | |--Row Count Spool
| | |--Table Spool
| |--Clustered Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[OpenerDetails].[IX_OpenerDetails_OpenerGUIDCurrencyGUID]), SEEK:([OpenerDetails].[OpenerGUID]=[Openers].[OpenerGUID] AND [OpenerDetails].[CurrencyGUID]=[Cu
|--Hash Match(Cache, HASH:([Openers].[OpenerGUID]), RESIDUAL:([Openers].[OpenerGUID]=[Openers].[OpenerGUID]))
|--Stream Aggregate(DEFINE:([Expr1006]=SUM([partialagg1034]), [Expr1007]=SUM([partialagg1035]), [Expr1008]=SUM([partialagg1036]), [Expr1009]=SUM([partialagg1037]), [Expr1010]=SUM([partialagg1038]), [Expr1011]=SUM([partialagg1039]
|--Nested Loops(Inner Join)
|--Stream Aggregate(DEFINE:([partialagg1034]=SUM(If (((([Currencies].[CurrencyType]='ctMiscellaneous' OR [Currencies].[CurrencyType]='ctTokens') OR [Currencies].[CurrencyType]='ctChips') OR [Currencies].[CurrencyType]='
| |--Nested Loops(Inner Join, OUTER REFERENCES:([OpenerDetails].[CurrencyGUID]) WITH PREFETCH)
| |--Clustered Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[OpenerDetails].[IX_OpenerDetails_OpenerGUIDCurrencyGUID]), SEEK:([OpenerDetails].[OpenerGUID]=[Openers].[OpenerGUID]) ORDERED FORWARD)
| |--Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[Currencies].[PK_Currencies_CurrencyGUID]), SEEK:([Currencies].[CurrencyGUID]=[OpenerDetails].[CurrencyGUID]) ORDERED FORWARD)
|--Index Seek(OBJECT:([GrobManagementSystemLive].[dbo].[Openers].[IX_Openers_OneOpenerPerSession]), SEEK:([Openers].[OpenerGUID]=[Openers].[OpenerGUID]) ORDERED FORWARD)
Zły jeden chętnie wykonuje 6 milionów wierszy; drugi nie jest.
Uwaga: To nie jest pytanie o dostrajanie zapytania. Mam zapytanie, które działa błyskawicznie. Chcę tylko, aby SQL Server działał szybko z procedury składowanej.
źródło
Odpowiedzi:
Miałem ten sam problem, co oryginalny plakat, ale cytowana odpowiedź nie rozwiązała problemu. Kwerenda nadal działała bardzo wolno z procedury przechowywanej.
Znalazłem inną odpowiedź tutaj „ Wąchanie parametrów” , Dzięki Omnibuzz. Sprowadza się do używania „lokalnych zmiennych” w zapytaniach dotyczących procedur składowanych, ale przeczytaj oryginał, aby lepiej zrozumieć, to świetny zapis. na przykład
Wolny sposób:
Szybki sposób:
Mam nadzieję, że to pomoże komuś innemu, dzięki temu skróciłem czas wykonywania z ponad 5 minut do około 6-7 sekund.
źródło
WITH RECOMPILE
nie zrobił dla mnie różnicy, tylko parametry lokalne.Znalazłem problem, oto skrypt wolnych i szybkich wersji procedury składowanej:
dbo.ViewOpener__RenamedForCruachan__Slow.PRC
dbo.ViewOpener__RenamedForCruachan__Fast.PRC
Jeśli nie zauważyłeś różnicy, nie obwiniam cię. Różnica w ogóle nie dotyczy procedury przechowywanej. Różnica, która zamienia szybkie zapytanie o wartości 0,5 w takie, które chętnie wykonuje 6 milionów wierszy:
Powolny:
SET ANSI_NULLS OFF
Szybki:
SET ANSI_NULLS ON
Ta odpowiedź może mieć również sens, ponieważ widok zawiera klauzulę łączenia, która mówi:
Więc jest trochę
NULL
zaangażowanych.Wyjaśnienie to zostało dodatkowo udowodnione przez powrót do Query Analizer i uruchomienie
.
.
A zapytanie jest wolne.
Problem nie polega na tym, że zapytanie jest uruchamiane z procedury składowanej. Problem polega na tym, że domyślną opcją połączenia Enterprise Manager jest
ANSI_NULLS off
raczejANSI_NULLS on
domyślna kontrola jakości.Microsoft uznaje ten fakt w KB296769 ( BŁĄD : Nie można użyć SQL Enterprise Manager do tworzenia procedur składowanych zawierających połączone obiekty serwerów). Obejściem jest włączenie
ANSI_NULLS
opcji w oknie dialogowym procedury składowanej:źródło
ANSI_NULLS ON
robi tak ogromną różnicę w wydajności.JOIN
klauzule w widoku mają inne znaczenie, kiedyANSI_NULLS OFF
. Nagle wiersze się zgadzają, co powoduje, że optymalizator uruchamia zapytanie zupełnie inaczej. Wyobraź sobie, że zamiast wyeliminować 99,9% wszystkich wierszy, nagle wracają.ANSI_NULLS OFF
jest przestarzała i uważana za złą praktykęZrób to dla swojej bazy danych. Mam ten sam problem - działa dobrze w jednej bazie danych, ale kiedy kopiuję tę bazę danych do innej za pomocą SSIS Import (nie zwykłego przywracania), ten problem występuje w większości moich procedur przechowywanych. Więc po przejściu na trochę więcej, znalazłem blog Pinal Dave (który przy okazji, spotkałem większość jego postu i bardzo mi pomógł, więc dziękuję Pinal Dave) .
Wykonuję poniższe zapytanie w mojej bazie danych i rozwiązało to mój problem:
Mam nadzieję że to pomoże. Przekazywanie pomocy innym, które mi pomogły.
źródło
DBCC REINDEX
jest przestarzała, więc powinieneś poszukać alternatyw.DBCC DBREINDEX
MS mówi: „Ta funkcja zostanie usunięta w przyszłej wersji Microsoft SQL Server. Nie używaj tej funkcji w nowych pracach programistycznych i jak najszybciej modyfikuj aplikacje, które obecnie używają tej funkcji. Zamiast tego użyj ALTER INDEX”.Napotkałem ten sam problem i ten post był dla mnie bardzo pomocny, ale żadna z opublikowanych odpowiedzi nie rozwiązała mojego konkretnego problemu. Chciałem opublikować rozwiązanie, które dla mnie zadziałało, w nadziei, że może pomóc komuś innemu.
https://stackoverflow.com/a/24016676/814299
źródło
Tym razem znalazłeś swój problem. Jeśli następnym razem będziesz mieć mniej szczęścia i nie będziesz w stanie tego rozgryźć, możesz użyć zamrożenia planu i przestać się martwić o niewłaściwy plan wykonania.
źródło
Wystąpił ten problem. Moje zapytanie wyglądało mniej więcej tak:
Moja procedura składowana została zdefiniowana następująco:
Zmieniłem typ danych na datetime i voila! Poszedłem z 30 minut do 1 minuty!
źródło
Czy próbowałeś odbudować statystyki i / lub indeksy w tabeli Report_Opener? Wszystkie rekompensaty SP nie będą nic warte, jeśli statystyki nadal pokazują dane z pierwszego uruchomienia bazy danych.
Samo zapytanie początkowe działa szybko, ponieważ optymalizator widzi, że parametr nigdy nie będzie miał wartości zerowej. W przypadku SP optymalizator nie może być pewien, że parametr nigdy nie będzie miał wartości zerowej.
źródło
Chociaż zwykle jestem temu przeciwny (choć w tym przypadku wydaje się, że masz prawdziwy powód), czy próbowałeś podać jakieś wskazówki dotyczące wersji SP zapytania? Jeśli SQL Server przygotowuje inny plan wykonania w tych dwóch instancjach, czy możesz użyć podpowiedzi, aby powiedzieć mu, jakiego indeksu użyć, aby plan pasował do pierwszego?
Aby zobaczyć kilka przykładów, możesz przejść tutaj .
EDYCJA: Jeśli możesz opublikować tutaj swój plan zapytań, być może możemy zidentyfikować pewną różnicę między planami, które mówią.
PO DRUGIE: Zaktualizowano łącze, aby było specyficzne dla SQL-2000. Będziesz musiał przewinąć w dół, ale jest druga zatytułowana „Wskazówki dotyczące tabeli”, której szukasz.
PO TRZECIE: Zapytanie „Bad” wydaje się ignorować [IX_Openers_SessionGUID] w tabeli „Openers” - czy jest szansa, że dodanie wskazówki INDEKSU, aby zmusić go do użycia tego indeksu, zmieni rzeczy?
źródło
Jest to prawdopodobnie mało prawdopodobne, ale biorąc pod uwagę, że obserwowane zachowanie jest niezwykłe, należy je sprawdzić i nikt o tym nie wspominał.
Czy jesteś absolutnie pewien, że wszystkie obiekty są własnością dbo i nie masz nieuczciwych kopii będących w posiadaniu ciebie lub innego użytkownika?
Czasami, gdy widziałem dziwne zachowanie, to dlatego, że faktycznie były dwie kopie obiektu, a ten, który dostajesz, zależy od tego, co zostało określone i kto jest zalogowany. Na przykład jest całkowicie możliwe posiadanie dwóch kopii widoku lub procedury o tej samej nazwie, ale należących do różnych właścicieli - sytuacja, która może wystąpić, gdy użytkownik nie jest zalogowany do bazy danych jako dbo i zapomina podać dbo jako właściciela obiektu, gdy tworzysz obiekt.
Pamiętaj, że w tekście uruchamiasz niektóre rzeczy bez określania właściciela, np
jeśli na przykład tam, gdzie dwie kopie viewOpener są własnością dbo i [innego użytkownika], to który z nich rekompilujesz, jeśli nie określisz, zależy od okoliczności. To samo dotyczy widoku Report_Opener - jeśli są tam dwie kopie (i mogą różnić się specyfikacją lub planem wykonania), to, co jest używane, zależy od okoliczności - a ponieważ nie określisz właściciela, jest całkowicie możliwe, że twoje zapytanie adhoc może użyć jednej i Skompilowana procedura może użyć innego.
Jak mówię, jest to prawdopodobnie mało prawdopodobne, ale jest możliwe i powinno zostać sprawdzone, ponieważ twoje problemy mogą polegać na tym, że po prostu szukasz błędu w niewłaściwym miejscu.
źródło
Może to zabrzmieć głupio i wydaje się oczywiste z nazwy SessionGUID, ale czy kolumna jest unikalnym identyfikatorem w Report_Opener? Jeśli nie, możesz spróbować rzucić go na odpowiedni typ i dać mu szansę lub zadeklarować zmienną na właściwym typie.
Plan utworzony jako część sproc może działać niezauważalnie i wykonać wewnętrzne rzucenie na dużym stole.
źródło
varchar
kolumnę znvarchar
wartością (npWHERE CustomerName = N'zrendall'
.). SQL Server musiał przekonwertować każdą wartość kolumny nanvarchar
przed porównaniem.Mam inny pomysł. Co jeśli utworzysz tę funkcję opartą na tabeli:
A następnie wybrano go za pomocą następującej instrukcji (nawet umieszczając to w SP):
Wygląda na to, że to, co się dzieje (o czym wszyscy już skomentowali), polega na tym, że SQL Server po prostu przyjmuje założenie, które jest złe, i może to zmusi je do poprawienia założenia. Nie chcę dodawać dodatkowego kroku, ale nie jestem pewien, co jeszcze może być przyczyną.
źródło
- Oto rozwiązanie:
-- Otóż to
źródło