SQL Server: Zapytanie szybkie, ale wolne od procedury

257

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ć StoredProcedureApołączenie StoredProcedureBwywołanie StoredProcedureCpołączenie, StoredProcedureDaby 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.

Ian Boyd
źródło
Za każdym razem, gdy bierzesz parametr i przenosisz go do innego, a następnie używasz go w zapytaniu, może się to zdarzyć i, jak sugeruje odpowiedź, Optymalizuj dla @ „someparamname” nieznany może działać.
JustDave

Odpowiedzi:

404

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:

CREATE PROCEDURE GetOrderForCustomers(@CustID varchar(20))
AS
BEGIN
    SELECT * 
    FROM orders
    WHERE customerid = @CustID
END

Szybki sposób:

CREATE PROCEDURE GetOrderForCustomersWithoutPS(@CustID varchar(20))
AS
BEGIN
    DECLARE @LocCustID varchar(20)
    SET @LocCustID = @CustID

    SELECT * 
    FROM orders
    WHERE customerid = @LocCustID
END

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.

Adam Marshall
źródło
23
+1 Ale to jest bardzo dziwne i rodzi się wiele pytań, takich jak powinniśmy to zrobić dla wszystkich procedur, a jeśli nie, to kiedy?
gotqn
31
Czy jestem jedynym, który jest zaskoczony tym zachowaniem? Wymaga zadeklarowania zmiennych lokalnych, aby zapobiec wąchaniu parametrów? Czy SQL Server nie powinien być wystarczająco inteligentny, aby temu zapobiec? To tylko powoduje niepotrzebne wzdęcie kodu przez krótkowzroczny projekt Microsoftu IMHO.
l46kok
4
15 min -> 8 sekund! ratownik
Tony Brix,
3
@BennettDill WITH RECOMPILEnie zrobił dla mnie różnicy, tylko parametry lokalne.
mrogers
8
Można to teraz osiągnąć, korzystając ze wskazówki dotyczącej zapytania - OPCJA (OPTYMALIZUJ DLA (@varA UNKNOWN, @varB UNKNOWN)
Dave
131

Znalazłem problem, oto skrypt wolnych i szybkich wersji procedury składowanej:

dbo.ViewOpener__RenamedForCruachan__Slow.PRC

SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS OFF 
GO

CREATE PROCEDURE dbo.ViewOpener_RenamedForCruachan_Slow
    @SessionGUID uniqueidentifier
AS

SELECT *
FROM Report_Opener_RenamedForCruachan
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank
GO

SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO

dbo.ViewOpener__RenamedForCruachan__Fast.PRC

SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO

CREATE PROCEDURE dbo.ViewOpener_RenamedForCruachan_Fast
    @SessionGUID uniqueidentifier 
AS

SELECT *
FROM Report_Opener_RenamedForCruachan
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank
GO

SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO

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:

(table.column IS NOT NULL)

Więc jest trochę NULLzaangażowanych.


Wyjaśnienie to zostało dodatkowo udowodnione przez powrót do Query Analizer i uruchomienie

SET ANSI_NULLS OFF

.

DECLARE @SessionGUID uniqueidentifier
SET @SessionGUID = 'BCBA333C-B6A1-4155-9833-C495F22EA908'

.

SELECT *
FROM Report_Opener_RenamedForCruachan
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank

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 offraczej ANSI_NULLS ondomyś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_NULLSopcji w oknie dialogowym procedury składowanej:

Set ANSI_NULLS ON
Go
Create Proc spXXXX as
....
Ian Boyd
źródło
2
Nadal nie rozumiem, w jaki sposób obracanie ANSI_NULLS ONrobi tak ogromną różnicę w wydajności.
Justin Helgerson
2
@ Ek0nomik Ponieważ JOINklauzule w widoku mają inne znaczenie, kiedy ANSI_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ą.
Ian Boyd,
2
Uwaga: ANSI_NULLS OFFjest przestarzała i uważana za złą praktykę
Jean
2
link "W przyszłej wersji SQL Server ANSI_NULLS zawsze będzie WŁĄCZONY, a wszelkie aplikacje, które jawnie ustawią opcję WYŁ., wygenerują błąd. Unikaj używania tej funkcji w nowych pracach programistycznych i planuj modyfikowanie aplikacji, które aktualnie używają tej funkcji. „
sotn
W moim przypadku nie pomogło.
st_stefanov
19

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:

EXEC sp_MSforeachtable @command1="print '?' DBCC DBREINDEX ('?', ' ', 80)"
GO
EXEC sp_updatestats
GO 

Mam nadzieję że to pomoże. Przekazywanie pomocy innym, które mi pomogły.

Carenski
źródło
2
Tylko informacja dla przyszłych czytelników: DBCC REINDEXjest przestarzała, więc powinieneś poszukać alternatyw.
gvee
1
Naprawiono mój problem, dzięki (1m20s do 2s!). Re: DBCC DBREINDEXMS 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”.
AjV Jsy,
Nie wiem, czy to najlepsza odpowiedź, ale w moim przypadku wystarczy sp_updatestats, więc +1
Todd Menier
.. tak i nie zapominaj, że odbudowanie indeksów może zająć trochę czasu i miejsca, więc zanim wykonasz to na serwerze produkcyjnym, upewnij się, że możesz sobie pozwolić na spowolnienie. Proponuję zajrzeć do REORGANIZACJI lub ODBUDOWANIA Z (ONLINE = ON)
Milan
14

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

Na końcu zapytania dodaj OPCJA (OPTYMALIZUJ DLA (@now NIEZNANE))

jessieloo
źródło
4

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.

AK
źródło
Zgodnie z tym wpisem na blogu, zamrożenie planu dotyczy tylko MS SQL 2005 i nowszych wersji, więc nie pomogłoby OP.
Coxy,
Problem polegał na tym, że korzystał z niewłaściwego planu zapytań. nie chciałbym zamrozić go na niewłaściwy.
Ian Boyd
4

Wystąpił ten problem. Moje zapytanie wyglądało mniej więcej tak:

select a, b, c from sometable where date > '20140101'

Moja procedura składowana została zdefiniowana następująco:

create procedure my_procedure (@dtFrom date)
as
select a, b, c from sometable where date > @dtFrom

Zmieniłem typ danych na datetime i voila! Poszedłem z 30 minut do 1 minuty!

create procedure my_procedure (@dtFrom datetime)
as
select a, b, c from sometable where date > @dtFrom
Lee Tickett
źródło
2
Dzięki DUŻO Lee, to uratowało mi dzień! Oto jak otrzymuję tylko część daty pola daty i godziny: DATEADD (dd, 0, DATEDIFF (dd, 0, table.field))
Julien B.
1
TO Naprawiono mój problem. Miałem kolumnę varchar (20), a moim parametrem był nvarchar (50), gdy raz ustawiłem typ parametru taki sam jak typ kolumny - nie ma już opóźnień.
st_stefanov
3

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.

AnthonyWJones
źródło
Czy istnieje sposób wskazania w deklaracji procedury składowanej, że parametr i nie może mieć wartości NULL? I czy nie jest to coś, co naprawiłoby sp_executesql?
Ian Boyd
Jednym słowem nope, nie w 2000 roku. 2005 dodała wskazówkę dotyczącą zapytania, w której można podać przykładową wartość parametru, optymalizator zoptymalizowałby się tak, jakby wiedział, że ten parametr był zawsze używany. Powiedziawszy, że ogólnie uważam tego rodzaju problem za statystyki.
AnthonyWJones
Jeśli jest to problem ze statystykami, czy działa poprawnie z kontroli jakości, kiedy uruchamiam go ad-hoc, sp_executesql, exec (). I dlaczego wszystkie one działają słabo, gdy procedura przechowywana zawiera ad-hoc sql, sp_executesql, exec ()?
Ian Boyd
1

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?

SqlRyan
źródło
Najbardziej przydatne wskazówki dotyczące zapytań w tym odwołaniu nie są dostępne w SQL 2000, który jest tutaj omawianą wersją.
AnthonyWJones
Ponadto, jakie wskazówki są potrzebne? SQL Server jest w stanie to rozwiązać bez problemu po uruchomieniu go ad-hoc.
Ian Boyd
Jasne, i to zawsze było moje doświadczenie. Jednak w tym przypadku mówi, że ma zupełnie inny plan egzekucyjny. Być może istnieje indeks wykorzystywany ad-hoc, ale z jakiegoś powodu jest ignorowany w proc. Może zmusić SQL Server do używania indeksu ze wskazówką „INDEKS”.
SqlRyan
1

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

sp_recompile ViewOpener

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.

Cruachan
źródło
1

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.

David Rendall
źródło
Nie jest. Ale widziałem problemy z wydajnością z klauzulą ​​where, która porównywała varcharkolumnę z nvarcharwartością (np WHERE CustomerName = N'zrendall'.). SQL Server musiał przekonwertować każdą wartość kolumny na nvarcharprzed porównaniem.
Ian Boyd,
0

Mam inny pomysł. Co jeśli utworzysz tę funkcję opartą na tabeli:

CREATE FUNCTION tbfSelectFromView
(   
    -- Add the parameters for the function here
    @SessionGUID UNIQUEIDENTIFIER
)
RETURNS TABLE 
AS
RETURN 
(
    SELECT *
    FROM Report_Opener
    WHERE SessionGUID = @SessionGUID
    ORDER BY CurrencyTypeOrder, Rank
)
GO

A następnie wybrano go za pomocą następującej instrukcji (nawet umieszczając to w SP):

SELECT *
FROM tbfSelectFromView(@SessionGUID)

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ą.

SqlRyan
źródło
0

- Oto rozwiązanie:

create procedure GetOrderForCustomers(@CustID varchar(20))

as

begin

select * from orders

where customerid = ISNULL(@CustID, '')

end

-- Otóż to

Koldoon
źródło