SQL Server: consulta rápida, pero lenta desde el procedimiento


257

Una consulta se ejecuta rápido:

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

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

costo de subárbol: 0.502

Pero poner el mismo SQL en un procedimiento almacenado se ejecuta lentamente y con un plan de ejecución totalmente diferente

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

EXECUTE ViewOpener @SessionGUID

Costo del subárbol: 19.2

He corrido

sp_recompile ViewOpener

Y todavía funciona igual (mal), y también he cambiado el procedimiento almacenado a

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

Y de vuelta, intentando engañarlo para que vuelva a compilar.

Dejé caer y recreé el procedimiento almacenado para que genere un nuevo plan.

He intentado forzar recompilaciones y evitar el rastreo de parámetros , utilizando una variable señuelo:

CREATE PROCEDURE dbo.ViewOpener @SessionGUID uniqueidentifier AS

DECLARE @SessionGUIDbitch uniqueidentifier
SET @SessionGUIDbitch = @SessionGUID

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

También he intentado definir el procedimiento almacenado WITH RECOMPILE:

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

Para que su plan nunca se almacene en caché, y he intentado forzar una recompilación en la ejecución:

EXECUTE ViewOpener @SessionGUID WITH RECOMPILE

Lo cual no ayudó.

Intenté convertir el procedimiento a SQL dinámico:

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

Lo cual no ayudó.

La entidad " Report_Opener" es una vista, que no está indexada. La vista solo hace referencia a tablas subyacentes. Ninguna tabla contiene columnas calculadas, indexadas o no.

Por el simple hecho de que traté de crear la vista con

SET ANSI_NULLS ON
SET QUOTED_IDENTIFER ON

Eso no lo solucionó.

Cómo es que

  • la consulta es rápida
  • mover la consulta a una vista y seleccionar desde la vista es rápido
  • seleccionar desde la vista desde un procedimiento almacenado es 40 veces más lento?

Traté de mover la definición de la vista directamente al procedimiento almacenado (violando 3 reglas comerciales y rompiendo una encapsulación importante), y eso hace que sea solo 6 veces más lento.

¿Por qué la versión del procedimiento almacenado es tan lenta? ¿Qué puede explicar el SQL Server que ejecuta SQL ad-hoc más rápido que un tipo diferente de SQL ad-hoc?

Realmente prefiero no

  • incrustar el SQL en el código
  • cambiar el código en absoluto

    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)

Pero, ¿qué puede explicar que SQL Server no pueda ejecutarse tan rápido como SQL Sever ejecuta una consulta, si no es que detecta parámetros?


Mi próximo intento será hacer que StoredProcedureAcall StoredProcedureBcall StoredProcedureCcall StoredProcedureDpara consultar la vista.

Y en su defecto, haga que el procedimiento almacenado llame a un procedimiento almacenado, llame a una UDF, llame a una UDF, llame a un procedimiento almacenado, llame a una UDF para consultar la vista.


En resumen, lo siguiente corre rápido desde QA, pero lento cuando se coloca en un procedimiento almacenado:

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

Planes de ejecucion

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

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

El malo está ansioso por enrollar 6 millones de filas; el otro no lo es.

Nota: Esta no es una pregunta sobre cómo ajustar una consulta. Tengo una consulta que se ejecuta a la velocidad del rayo. Solo quiero que SQL Server se ejecute rápidamente desde un procedimiento almacenado.


Noto que cada vez que toma un parámetro y lo reasigna a otro y luego lo usa en una consulta, esto puede suceder y, como la respuesta sugiere, Optimizar para @ "someparamname" desconocido puede funcionar.
JustDave

Respuestas:


405

Tuve el mismo problema que el póster original, pero la respuesta citada no me resolvió el problema. La consulta todavía se ejecutó muy lentamente desde un procedimiento almacenado.

Encontré otra respuesta aquí "Parameter Sniffing" , gracias Omnibuzz. Se reduce al uso de "Variables locales" en sus consultas de procedimientos almacenados, pero lea el original para una mejor comprensión, es una excelente redacción. p.ej

Camino lento:

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

Manera rápida:

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

    SELECT * 
    FROM orders
    WHERE customerid = @LocCustID
END

Espero que esto ayude a alguien más, hacerlo redujo mi tiempo de ejecución de más de 5 minutos a unos 6-7 segundos.


23
+1 Pero, esto es muy extraño, y surgen muchas preguntas, ¿deberíamos hacer esto para todos los procedimientos y, de no ser así, cuándo hacerlo?
gotqn

31
¿Soy el único que está desconcertado por este comportamiento? ¿Requerir que se declaren las variables locales para evitar la detección de parámetros? ¿No debería ser SQL Server lo suficientemente inteligente como para evitar que esto suceda en primer lugar? Esto solo causa una hinchazón de código innecesaria por el diseño miope de mi humilde opinión.
l46kok

44
15 min -> 8 segundos! salvavidas
Tony Brix

3
@BennettDill WITH RECOMPILEno marcó la diferencia para mí, solo los parámetros locales.
mrogers

8
Esto ahora se puede lograr utilizando la sugerencia de consulta - OPTION (OPTIMIZE FOR (@varA UNKNOWN, @varB UNKNOWN)
Dave

131

Encontré el problema, aquí está el script de las versiones lenta y rápida del procedimiento almacenado:

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 no viste la diferencia, no te culpo. La diferencia no está en el procedimiento almacenado en absoluto. La diferencia que convierte una consulta rápida de 0.5 en una consulta que genera 6 millones de filas:

Lento: SET ANSI_NULLS OFF

Rápido: SET ANSI_NULLS ON


Esta respuesta también podría tener sentido, ya que la vista tiene una cláusula de unión que dice:

(table.column IS NOT NULL)

Entonces hay algunos NULLs involucrados.


La explicación se demuestra aún más al volver al Analizador de consultas y ejecutar

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

Y la consulta es lenta.


Entonces, el problema no es porque la consulta se ejecuta desde un procedimiento almacenado. El problema es que la opción predeterminada de conexión de Enterprise Manager es ANSI_NULLS off, en lugar de ANSI_NULLS on, cuál es el valor predeterminado de QA.

Microsoft reconoce este hecho en KB296769 (ERROR: No se puede utilizar el Administrador corporativo de SQL para crear procedimientos almacenados que contienen objetos de servidor vinculados). La solución alternativa es incluir la ANSI_NULLSopción en el cuadro de diálogo del procedimiento almacenado:

Set ANSI_NULLS ON
Go
Create Proc spXXXX as
....

2
Todavía no entiendo cómo girar ANSI_NULLS ONhace una enorme diferencia de rendimiento.
Justin Helgerson

2
@ Ek0nomik Porque las JOINcláusulas dentro de la vista tienen un significado diferente cuando ANSI_NULLS OFF. De repente, las filas coinciden, lo que hace que el optimizador ejecute la consulta de manera completamente diferente. Imagine que en lugar de eliminar el 99.9% de todas las filas, de repente regresan.
Ian Boyd

2
Nota: ANSI_NULLS OFFestá en desuso y se considera una mala práctica
jean

2
link "En una versión futura de SQL Server, ANSI_NULLS siempre estará ENCENDIDO y cualquier aplicación que establezca explícitamente la opción en APAGADO generará un error. Evite usar esta característica en un nuevo trabajo de desarrollo y planee modificar las aplicaciones que actualmente usan esta característica. "
sotn

No ayudó en mi caso.
st_stefanov

19

Haga esto para su base de datos. Tengo el mismo problema: funciona bien en una base de datos, pero cuando copio esta base de datos a otra usando Importar SSIS (no la restauración habitual), este problema ocurre en la mayoría de mis procedimientos almacenados. Entonces, después de buscar en Google un poco más, encontré el blog del Pinal Dave (que por cierto, encontré la mayor parte de su publicación y me ayudó mucho, así que gracias Pinal Dave) .

Ejecuté la consulta a continuación en mi base de datos y corrigió mi problema:

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

Espero que esto ayude. Simplemente pasando la ayuda de otros que me ayudaron.


2
Solo un FYI para futuros lectores: DBCC REINDEXha quedado en desuso, por lo que debe buscar alternativas.
Gvee

1
Se solucionó mi problema, gracias (¡1m20s a 2s!). Re: DBCC DBREINDEXMS dice: "Esta característica se eliminará en una versión futura de Microsoft SQL Server. No use esta característica en nuevos trabajos de desarrollo y modifique las aplicaciones que actualmente usan esta característica lo antes posible. Utilice ALTER INDEX".
AjV Jsy

No sé si esta es la mejor respuesta, pero en mi caso sp_updatestats es todo lo que se necesitó, así que +1
Todd Menier

... sí, y no olvides que la reconstrucción de índices puede llevar tiempo y espacio, así que antes de ejecutar esto en tu servidor de producción, asegúrate de que puedes permitirte una posible desaceleración. Sugeriría buscar REORGANIZAR o RECONSTRUIR CON (ONLINE = ON)
Milán

14

Estaba enfrentando el mismo problema y esta publicación fue muy útil para mí, pero ninguna de las respuestas publicadas resolvió mi problema específico. Quería publicar la solución que funcionó para mí con la esperanza de que pueda ayudar a alguien más.

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

Al final de su consulta, agregue OPTION (OPTIMIZE FOR (@now UNKNOWN))


4

Esta vez encontraste tu problema. Si la próxima vez tiene menos suerte y no puede resolverlo, puede usar la congelación del plan y dejar de preocuparse por un plan de ejecución incorrecto.


De acuerdo con esa entrada del blog, la congelación de planes es solo para MS SQL 2005 y versiones posteriores, por lo que no ayudaría al OP.
Coxy

El problema era que estaba usando el plan de consulta incorrecto. No quisiera congelarlo al equivocado.
Ian Boyd el

4

Estaba experimentando este problema. Mi consulta se parecía a:

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

Mi procedimiento almacenado se definió como:

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

¡Cambié el tipo de datos a datetime y listo! ¡Pasé de 30 minutos a 1 minuto!

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

2
Muchas gracias Lee, ¡esto me salvó el día! Así es como obtengo solo la parte de fecha de un campo de fecha y hora: DATEADD (dd, 0, DATEDIFF (dd, 0, table.field))
Julien B.

1
ESTO solucionó mi problema. Tenía la columna varchar (20) y mi parámetro era nvarchar (50), una vez que hice el tipo de parámetro igual que el tipo de columna, no más retrasos.
st_stefanov

3

¿Has intentado reconstruir las estadísticas y / o los índices en la tabla Report_Opener? Todas las recomendaciones del SP no valdrán nada si las estadísticas aún muestran datos de la primera inauguración de la base de datos.

La consulta inicial en sí misma funciona rápidamente porque el optimizador puede ver que el parámetro nunca será nulo. En el caso del SP, el optimizador no puede estar seguro de que el parámetro nunca será nulo.


¿Hay alguna manera de indicar en una declaración de procedimiento almacenado que el parámetro i no puede ser nulo? ¿Y no es algo que arreglaría sp_executesql?
Ian Boyd el

En una palabra no, no en 2000. 2005 agregó una sugerencia de consulta donde podría proporcionar un valor de ejemplo para un parámetro, el optimizador optimizaría como si supiera que ese parámetro siempre se usó. Habiendo dicho eso, generalmente he encontrado que este tipo de cosas es un problema de estadísticas.
AnthonyWJones

Si se trata de un problema de estadísticas, funcionan bien desde QA cuando lo ejecuto ad-hoc, sp_executesql, exec (). ¿Y por qué todos se ejecutan mal cuando un procedimiento almacenado contiene el sql ad-hoc, sp_executesql, exec ()?
Ian Boyd el

1

Aunque generalmente estoy en contra (aunque en este caso parece que tiene una razón genuina), ¿ha intentado proporcionar alguna sugerencia de consulta sobre la versión SP de la consulta? Si SQL Server está preparando un plan de ejecución diferente en esas dos instancias, ¿puede usar una pista para decirle qué índice usar, para que el plan coincida con el primero?

Para algunos ejemplos, puedes ir aquí .

EDITAR: Si puede publicar su plan de consulta aquí, tal vez podamos identificar alguna diferencia entre los planes que dicen.

SEGUNDO: Se actualizó el enlace para que sea específico de SQL-2000. Tendrás que desplazarte hacia abajo, pero hay un segundo titulado "Sugerencias de tabla" que es lo que estás buscando.

TERCERO: La consulta "Mala" parece estar ignorando el [IX_Openers_SessionGUID] en la tabla "Openers". ¿Alguna posibilidad de agregar una pista INDEX para forzarlo a usar ese índice cambiará las cosas?


Las sugerencias de consulta más útiles en esa referencia no están disponibles en SQL 2000, que es la versión en cuestión aquí.
AnthonyWJones

Además, ¿qué sugerencias son necesarias? SQL Server puede resolverlo sin problemas cuando lo ejecuto ad-hoc.
Ian Boyd el

Claro, y esa siempre ha sido mi experiencia también. Sin embargo, en este caso, él está diciendo que está surgiendo un plan ejecutivo totalmente diferente. Tal vez hay un índice que se utiliza ad-hoc, pero por alguna razón se ignora en el proceso. Puede obligar a SQL Server a usar el índice con la sugerencia "INDEX".
SqlRyan

1

Esto es probablemente improbable, pero dado que su comportamiento observado es inusual, debe verificarse y nadie más lo ha mencionado.

¿Estás absolutamente seguro de que todos los objetos son propiedad de dbo y no tienes copias falsas de tu propiedad o de un usuario diferente?

Solo ocasionalmente, cuando he visto un comportamiento extraño, es porque en realidad había dos copias de un objeto y cuál obtienes depende de lo que se especifique y de con quién hayas iniciado sesión. Por ejemplo, es perfectamente posible tener dos copias de una vista o procedimiento con el mismo nombre pero propiedad de diferentes propietarios, una situación que puede surgir cuando no está conectado a la base de datos como un dbo y olvida especificar dbo como propietario del objeto cuando Tú creas el objeto.

Tenga en cuenta que en el texto está ejecutando algunas cosas sin especificar el propietario, por ejemplo

sp_recompile ViewOpener

si, por ejemplo, hay dos copias de viewOpener presentes propiedad de dbo y [algún otro usuario], entonces cuál recompilar si no especifica depende de las circunstancias. Lo mismo ocurre con la vista Report_Opener: si hay dos copias (y podrían diferir en la especificación o el plan de ejecución), lo que se usa depende de las circunstancias, y como no especifica el propietario, es perfectamente posible que su consulta ad hoc use una y el procedimiento compilado podría usar use the other.

Como digo, probablemente sea poco probable, pero es posible y debe verificarse porque sus problemas podrían ser que simplemente está buscando el error en el lugar incorrecto.


1

Esto puede sonar tonto y parece obvio por el nombre SessionGUID, pero ¿es la columna un identificador único en Report_Opener? De lo contrario, puede intentar convertirlo al tipo correcto y darle una oportunidad o declarar su variable al tipo correcto.

El plan creado como parte del sproc puede funcionar de manera poco intuitiva y realizar un reparto interno en una mesa grande.


No lo es. Pero he visto problemas de rendimiento con una cláusula where que comparaba una varcharcolumna con un nvarcharvalor (por ejemplo WHERE CustomerName = N'zrendall'). SQL Server tuvo que convertir cada valor de columna a nvarcharantes de la comparación.
Ian Boyd

0

Tengo otra idea ¿Qué pasa si crea esta función basada en tablas?

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

Y luego seleccionó de él usando la siguiente declaración (incluso poniendo esto en su SP):

SELECT *
FROM tbfSelectFromView(@SessionGUID)

Parece que lo que está sucediendo (que todo el mundo ya ha comentado) es que SQL Server simplemente asume que algo está mal, y tal vez esto lo obligará a corregir la suposición. Odio agregar el paso adicional, pero no estoy seguro de qué más podría estar causándolo.


0

- Aquí está la solución:

create procedure GetOrderForCustomers(@CustID varchar(20))

as

begin

select * from orders

where customerid = ISNULL(@CustID, '')

end

-- Eso es

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.