SQL Server: requête rapide, mais lente à partir de la procédure

257

Une requête s'exécute rapidement:

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

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

coût sous-arbre: 0,502

Mais mettre le même SQL dans une procédure stockée est lent et avec un plan d'exécution totalement différent

CREATE PROCEDURE dbo.ViewOpener @SessionGUID uniqueidentifier AS
SELECT *
FROM Report_Opener
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank

EXECUTE ViewOpener @SessionGUID

Coût du sous-arbre: 19,2

J'ai couru

sp_recompile ViewOpener

Et il fonctionne toujours de la même manière (mal), et j'ai également changé la procédure stockée en

CREATE PROCEDURE dbo.ViewOpener @SessionGUID uniqueidentifier AS
SELECT *, 'recompile please'
FROM Report_Opener
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank

Et encore une fois, en essayant de vraiment le tromper en recompilation.

J'ai supprimé et recréé la procédure stockée afin de générer un nouveau plan.

J'ai essayé de forcer les recompilations et d'empêcher le reniflement des paramètres en utilisant une variable leurre:

CREATE PROCEDURE dbo.ViewOpener @SessionGUID uniqueidentifier AS

DECLARE @SessionGUIDbitch uniqueidentifier
SET @SessionGUIDbitch = @SessionGUID

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

J'ai également essayé de définir la procédure stockée WITH RECOMPILE:

CREATE PROCEDURE dbo.ViewOpener @SessionGUID uniqueidentifier 
WITH RECOMPILE
AS
SELECT *
FROM Report_Opener
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank

Pour que son plan ne soit jamais mis en cache, j'ai essayé de forcer une recompilation lors de l'exécution:

EXECUTE ViewOpener @SessionGUID WITH RECOMPILE

Ce qui n'a pas aidé.

J'ai essayé de convertir la procédure en SQL dynamique:

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

Ce qui n'a pas aidé.

L'entité " Report_Opener" est une vue qui n'est pas indexée. La vue fait uniquement référence aux tables sous-jacentes. Aucune table ne contient de colonnes calculées, indexées ou non.

Pour l'enfer, j'ai essayé de créer la vue avec

SET ANSI_NULLS ON
SET QUOTED_IDENTIFER ON

Cela ne l'a pas corrigé.

Comment se fait-il que

  • la requête est rapide
  • déplacer la requête vers une vue et effectuer une sélection dans la vue est rapide
  • la sélection dans la vue à partir d'une procédure stockée est 40 fois plus lente?

J'ai essayé de déplacer la définition de la vue directement dans la procédure stockée (violant 3 règles métier et cassant une encapsulation importante), ce qui ne la rend que 6 fois plus lente.

Pourquoi la version de la procédure stockée est-elle si lente? Qu'est-ce qui peut expliquer que SQL Server exécute un SQL ad hoc plus rapidement qu'un autre type de SQL ad hoc?

Je préfère vraiment ne pas

  • intégrer le SQL dans le code
  • changer le code du tout

    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)

Mais qu'est-ce qui peut expliquer que SQL Server ne puisse pas s'exécuter aussi rapidement que SQL Sever exécutant une requête, sinon le reniflage de paramètres.


Ma prochaine tentative sera d'avoir StoredProcedureAappel StoredProcedureBappel StoredProcedureCappel StoredProcedureDpour interroger la vue.

Et à défaut, demandez à la procédure stockée d'appeler une procédure stockée, d'appeler un UDF, d'appeler un UDF, d'appeler une procédure stockée, d'appeler un UDF pour interroger la vue.


Pour résumer, les éléments suivants s'exécutent rapidement depuis QA, mais sont lents lorsqu'ils sont placés dans une procédure stockée:

L'original:

--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)

Plans d'exécution

Le bon plan:

      |--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)

Le mauvais plan

       |--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)

Le mauvais est impatient de mettre en file d'attente 6 millions de lignes; l'autre ne l'est pas.

Remarque: il ne s'agit pas de régler une requête. J'ai une requête qui tourne très vite. Je veux juste que SQL Server s'exécute rapidement à partir d'une procédure stockée.

Ian Boyd
la source
Je remarque que chaque fois que vous prenez un paramètre et que vous le réaffectez à un autre, puis que vous l'utilisez dans une requête plus tard, cela peut se produire et, comme la réponse le suggère, Optimiser pour @ "someparamname" inconnu peut fonctionner.
JustDave

Réponses:

405

J'ai eu le même problème que l'affiche originale mais la réponse citée n'a pas résolu le problème pour moi. La requête s'exécutait toujours très lentement à partir d'une procédure stockée.

J'ai trouvé une autre réponse ici "Reniflage de paramètres" , merci Omnibuzz. Cela revient à utiliser des "variables locales" dans vos requêtes de procédures stockées, mais lisez l'original pour plus de compréhension, c'est une excellente écriture. par exemple

Chemin lent:

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

Manière rapide:

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

    SELECT * 
    FROM orders
    WHERE customerid = @LocCustID
END

J'espère que cela aide quelqu'un d'autre, ce faisant, j'ai réduit mon temps d'exécution de 5+ minutes à environ 6-7 secondes.

Adam Marshall
la source
23
+1 Mais, c'est très étrange, et soulève beaucoup de questions comme devrions-nous le faire pour toutes les procédures et sinon, quand le faire?
gotqn
32
Suis-je le seul à être déconcerté par ce comportement ?? Obligation de déclarer des variables locales pour éviter le reniflement des paramètres ?? SQL Server ne devrait-il pas être suffisamment intelligent pour empêcher que cela ne se produise en premier lieu? Cela provoque simplement un gonflement de code inutile par la conception à courte vue de Microsoft IMHO.
l46kok
4
15 min -> 8 sec! épargnant de la vie
Tony Brix
3
@BennettDill WITH RECOMPILEn'a pas fait de différence pour moi, seulement les paramètres locaux.
mrogers
8
Ceci peut maintenant être réalisé en utilisant l'indicateur de requête - OPTION (OPTIMISER POUR (@varA UNKNOWN, @varB UNKNOWN)
Dave
131

J'ai trouvé le problème, voici le script des versions lente et rapide de la procédure stockée:

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

Si vous n'avez pas remarqué la différence, je ne vous en veux pas. La différence n'est pas du tout dans la procédure stockée. La différence qui transforme une requête de coût rapide de 0,5 en une requête qui fait une bobine impatiente de 6 millions de lignes:

Lent: SET ANSI_NULLS OFF

Vite: SET ANSI_NULLS ON


Cette réponse pourrait également avoir un sens, car la vue a une clause de jointure qui dit:

(table.column IS NOT NULL)

Il y a donc des NULLimplications.


L'explication est encore prouvée en revenant à Query Analizer et en exécutant

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

Et la requête est lente.


Le problème n'est donc pas dû au fait que la requête est exécutée à partir d'une procédure stockée. Le problème est que l'option par défaut de connexion d'Enterprise Manager est ANSI_NULLS off, plutôt que ANSI_NULLS on, qui est la valeur par défaut de QA.

Microsoft reconnaît ce fait dans KB296769 (BOGUE: impossible d'utiliser SQL Enterprise Manager pour créer des procédures stockées contenant des objets serveur liés). La solution de contournement consiste à inclure l' ANSI_NULLSoption dans la boîte de dialogue de procédure stockée:

Set ANSI_NULLS ON
Go
Create Proc spXXXX as
....
Ian Boyd
la source
2
Je ne comprends toujours pas comment le tournage ANSI_NULLS ONfait une énorme différence de performances.
Justin Helgerson
2
@ Ek0nomik Parce que les JOINclauses à l'intérieur de la vue ont une signification différente quand ANSI_NULLS OFF. Soudain, les lignes correspondent, ce qui entraîne l'optimiseur à exécuter la requête complètement différemment. Imaginez qu'au lieu d'éliminer 99,9% de toutes les lignes, elles reviennent soudainement.
Ian Boyd du
2
Remarque: ANSI_NULLS OFFest déconseillé et considéré comme une mauvaise pratique
jean
2
link "Dans une future version de SQL Server, ANSI_NULLS sera toujours activé et toutes les applications qui définissent explicitement l'option sur OFF généreront une erreur. Évitez d'utiliser cette fonctionnalité dans les nouveaux travaux de développement et prévoyez de modifier les applications qui utilisent actuellement cette fonctionnalité. "
sotn
N'a pas aidé dans mon cas.
st_stefanov
19

Faites cela pour votre base de données. J'ai le même problème - cela fonctionne bien dans une base de données mais lorsque je copie cette base de données dans une autre à l'aide de l'importation SSIS (pas la restauration habituelle), ce problème se produit pour la plupart de mes procédures stockées. Donc, après avoir googlé un peu plus, j'ai trouvé le blog de Pinal Dave (qui d'ailleurs, j'ai rencontré la plupart de ses messages et m'a beaucoup aidé alors merci Pinal Dave) .

J'exécute la requête ci-dessous sur ma base de données et cela a corrigé mon problème:

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

J'espère que cela t'aides. Je passe juste l'aide des autres qui m'ont aidé.

Carenski
la source
2
Juste un info pour les futurs lecteurs: DBCC REINDEXa été déprécié, vous devriez donc chercher des alternatives.
gvee
1
Correction de mon problème, merci (1m20s à 2s!). Re DBCC DBREINDEX:, MS dit: "Cette fonctionnalité sera supprimée dans une future version de Microsoft SQL Server. N'utilisez pas cette fonctionnalité dans les nouveaux travaux de développement et modifiez les applications qui utilisent actuellement cette fonctionnalité dès que possible. Utilisez plutôt ALTER INDEX."
AjV Jsy
Je ne sais pas si c'est la meilleure réponse mais dans mon cas, sp_updatestats est tout ce qu'il a fallu, alors +1
Todd Menier
..oui et, n'oubliez pas que la reconstruction des index peut prendre du temps et de l'espace donc avant de l'exécuter sur votre serveur de production, assurez-vous que vous pouvez vous permettre un ralentissement possible. Je suggère de se pencher sur RÉORGANISER ou RECONSTRUIRE AVEC (EN LIGNE = ON)
Milan
14

J'étais confronté au même problème et ce message m'a été très utile, mais aucune des réponses publiées n'a résolu mon problème spécifique. Je voulais publier la solution qui a fonctionné pour moi dans l'espoir qu'elle puisse aider quelqu'un d'autre.

https://stackoverflow.com/a/24016676/814299

À la fin de votre requête, ajoutez OPTION (OPTIMISER POUR (@now UNKNOWN))

jessieloo
la source
4

Cette fois, vous avez trouvé votre problème. Si la prochaine fois, vous avez moins de chance et ne pouvez pas le comprendre, vous pouvez utiliser le gel du plan et ne plus vous soucier d'un mauvais plan d'exécution.

AK
la source
Selon cette entrée de blog, le gel du plan est uniquement pour MS SQL 2005 et plus, donc cela n'aiderait pas l'OP.
Coxy
Le problème était qu'il utilisait le mauvais plan de requête. je ne voudrais pas le geler au mauvais.
Ian Boyd
4

Je rencontrais ce problème. Ma requête ressemblait à quelque chose comme:

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

Ma procédure stockée a été définie comme:

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

J'ai changé le type de données en datetime et le tour est joué! Je suis passé de 30 minutes à 1 minute!

create procedure my_procedure (@dtFrom datetime)
as
select a, b, c from sometable where date > @dtFrom
Lee Tickett
la source
2
Merci beaucoup Lee, cela m'a sauvé la journée! Voici comment j'obtiens uniquement la partie date d'un champ datetime: DATEADD (jj, 0, DATEDIFF (jj, 0, table.field))
Julien B.
1
CECI a résolu mon problème. J'avais la colonne varchar (20) et mon paramètre était nvarchar (50), une fois que j'ai fait le même type de paramètre que le type de colonne - plus de retards.
st_stefanov
3

Avez-vous essayé de reconstruire les statistiques et / ou les index sur la table Report_Opener. Toutes les recomplies du SP ne valent rien si les statistiques affichent toujours des données datant de la première mise à jour de la base de données.

La requête initiale elle-même fonctionne rapidement car l'optimiseur peut voir que le paramètre ne sera jamais nul. Dans le cas du SP, l'optimiseur ne peut pas être sûr que le paramètre ne sera jamais nul.

AnthonyWJones
la source
Existe-t-il un moyen d'indiquer dans une déclaration de procédure stockée que le paramètre i ne peut pas être nul? Et n'est-ce pas quelque chose qui serait corrigé par sp_executesql?
Ian Boyd
En un mot non, pas en 2000. 2005 a ajouté un indice de requête où vous pouvez fournir un exemple de valeur pour un paramètre, l'optimiseur optimiserait comme s'il savait que ce paramètre était toujours utilisé. Cela dit, j'ai généralement trouvé que ce genre de chose était un problème de statistiques.
AnthonyWJones
S'il s'agit d'un problème de statistiques, ils fonctionnent correctement à partir du contrôle qualité lorsque je l'exécute ad-hoc, sp_executesql, exec (). Et pourquoi s'exécutent-ils tous mal alors lorsqu'une procédure stockée contient le sql ad-hoc, sp_executesql, exec ()?
Ian Boyd
1

Bien que je sois généralement contre (bien que dans ce cas, il semble que vous ayez une véritable raison), avez-vous essayé de fournir des indices de requête sur la version SP de la requête? Si SQL Server prépare un plan d'exécution différent dans ces deux instances, pouvez-vous utiliser un indice pour lui indiquer quel index utiliser, afin que le plan corresponde au premier?

Pour quelques exemples, vous pouvez aller ici .

EDIT: Si vous pouvez publier votre plan de requête ici, nous pouvons peut-être identifier une certaine différence entre les plans qui le disent.

DEUXIÈME: mise à jour du lien pour être spécifique à SQL-2000. Vous devrez faire défiler vers le bas de plusieurs façons, mais il y a une seconde intitulée "Tableau Hints" qui est ce que vous recherchez.

TROISIÈME: La requête "Bad" semble ignorer le [IX_Openers_SessionGUID] sur la table "Openers" - toute chance d'ajouter un indice INDEX pour le forcer à utiliser cet index changera-t-il les choses?

SqlRyan
la source
Les conseils de requête les plus utiles dans cette référence ne sont pas disponibles sur SQL 2000, qui est la version en question ici.
AnthonyWJones
De plus, quels conseils sont nécessaires? SQL Server est en mesure de le résoudre sans problème lorsque je l'exécute ad-hoc.
Ian Boyd
Bien sûr, et ça a toujours été mon expérience aussi. Cependant, dans ce cas, il dit qu'il propose un plan d'exécution totalement différent. Peut-être y a-t-il un index utilisé ad-hoc, mais pour une raison quelconque, il est ignoré dans le proc. Il peut forcer SQL Server à utiliser l'index avec l'indication "INDEX".
SqlRyan
1

C'est probablement peu probable, mais étant donné que votre comportement observé est inhabituel, il doit être vérifié et personne d'autre ne l'a mentionné.

Êtes-vous absolument sûr que tous les objets appartiennent à dbo et que vous n'avez pas non plus de copies escrocs appartenant à vous-même ou à un autre utilisateur?

De temps en temps, quand j'ai vu un comportement étrange, c'est parce qu'il y avait en fait deux copies d'un objet et celle que vous obtenez dépend de ce qui est spécifié et de qui vous êtes connecté. Par exemple, il est parfaitement possible d'avoir deux copies d'une vue ou d'une procédure portant le même nom mais appartenant à des propriétaires différents - une situation qui peut se produire lorsque vous n'êtes pas connecté à la base de données en tant que dbo et oubliez de spécifier dbo en tant que propriétaire de l'objet lorsque vous créez l'objet.

Notez que dans le texte, vous exécutez certaines choses sans spécifier le propriétaire, par exemple

sp_recompile ViewOpener

si, par exemple, là où deux copies de viewOpener sont détenues par dbo et [un autre utilisateur], celle que vous recompilerez si vous ne spécifiez pas dépend des circonstances. Idem avec la vue Report_Opener - s'il y a deux copies (et dont la spécification ou le plan d'exécution peuvent différer), alors ce qui est utilisé dépend des circonstances - et comme vous ne spécifiez pas le propriétaire, il est parfaitement possible que votre requête adhoc en utilise une et la procédure compilée peut utiliser utiliser l'autre.

Comme je l'ai dit, c'est probablement peu probable mais c'est possible et devrait être vérifié car vos problèmes pourraient être que vous recherchez simplement le bogue au mauvais endroit.

Cruachan
la source
1

Cela peut sembler idiot et semble évident d'après le nom SessionGUID, mais la colonne est-elle un identifiant unique sur Report_Opener? Si ce n'est pas le cas, vous voudrez peut-être essayer de le convertir dans le type correct et lui donner un coup de feu ou déclarer votre variable au type correct.

Le plan créé dans le cadre du sproc peut ne pas fonctionner de manière intuitive et faire un casting interne sur une grande table.

David Rendall
la source
Ce n'est pas. Mais j'ai vu des problèmes de performances avec une clause where qui comparait une varcharcolonne avec une nvarcharvaleur (par exemple WHERE CustomerName = N'zrendall'). SQL Server a dû convertir chaque valeur de colonne en un nvarcharavant la comparaison.
Ian Boyd
0

J'ai une autre idée. Et si vous créez cette fonction basée sur une table:

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

Et puis sélectionné à partir de celui-ci en utilisant la déclaration suivante (même en mettant cela dans votre SP):

SELECT *
FROM tbfSelectFromView(@SessionGUID)

Il semble que ce qui se passe (ce que tout le monde a déjà commenté) est que SQL Server fait simplement une hypothèse quelque part qui ne va pas, et cela l'obligera peut-être à corriger l'hypothèse. Je déteste ajouter l'étape supplémentaire, mais je ne sais pas quoi d'autre pourrait en être la cause.

SqlRyan
la source
0

- Voici la solution:

create procedure GetOrderForCustomers(@CustID varchar(20))

as

begin

select * from orders

where customerid = ISNULL(@CustID, '')

end

-- C'est tout

Koldoon
la source