2009-01-25 14 views
15

me gustaría ejecutar un procedimiento almacenado sobre cada fila en un conjunto sin necesidad de utilizar un cursor con algo como esto:

SELECT EXEC dbo.Sproc @Param1 = Table1.id
FROM Table1


Estoy usando T-SQL en SQL Server 2005. Creo que esto podría ser posible usando una función, pero me gustaría usar un procedimiento almacenado si es posible (estándares de la compañía)¿Es posible ejecutar un procedimiento almacenado en un conjunto sin utilizar un cursor?

Respuesta

0

9 de cada 10 veces puede hacer lo que quiera sin un cursor o un ciclo while. Sin embargo, si debe usar uno, he encontrado que aunque los bucles tienden a ser más rápidos.

Además, si usted no desea borrar o actualizar la tabla, se puede usar algo como esto:

DECLARE @id [type] 
SELECT @id = MIN([id]) FROM [table] 
WHILE @id IS NOT NULL 
BEGIN 
    EXEC [sproc] @id 
    SELECT @id = MIN([id]) FROM [table] WHERE [id] > @id 
END 
+1

¿Es esto completamente exacto? ¿Puedes ordenar por ID sin especificar una cláusula group by? –

+0

No funciona bien. No se detiene después de leer la identificación máxima. –

+1

-1 No funciona. Como dijo Richard Collette, la sintaxis seleccionada está rota. Y si arreglas la sintaxis, el tiempo se queda atascado en un ciclo infinito, como señala Tony_Henrich). He agregado una respuesta que tiene un ciclo de trabajo, pero fue lenta en mi experiencia. –

17

Sí. Si usted tiene una columna se puede utilizar dentro de la tabla para marcar los procesados, puede utilizar mientras exista:

DECLARE @Id int 
WHILE EXISTS(SELECT * FROM Table1 WHERE Processed='N') 
BEGIN 
SELECT Top 1 @Id = id from Table1 WHERE Procesed='N' 
EXEC dbo.Sproc @Id 
UPDATE Table1 SET Processed = 'Y' WHERE Id = @Id 
END 

Alternativamente, volcar los identificadores en una variable de tabla temporal o mesa y eliminar cuando haya terminado:

DECLARE @HoldTable table (Id int PRIMARY KEY) 
DECLARE @Id int 
INSERT INTO @HoldTable SELECT Id FROM Table1 
WHILE EXISTS(SELECT * FROM @HoldTable) 
BEGIN 
SELECT @Id = id from @HoldTable 
EXEC dbo.Sproc @Id 
DELETE FROM @HoldTable where Id = @Id 
END 
+1

editado la segunda respuesta. Estabas seleccionando en @Id en lugar de @HoldTable y tenías una cláusula where de 'where processed = 'N'' pero @HoldTable no tiene dicha columna. La segunda opción ahora debería funcionar. Espero haber entendido tu intención correctamente. – kralco626

3

Francamente si necesitaba para ejecutar un procedimiento almacenado para un conjunto de datos en lugar si el registro fue escrito para, escribiría el código basado en conjunto en lugar de usar el proceso almacenado. Esta es la única manera eficiente de hacer esto si va a ejecutar contra un conjunto de datos grande. Puede pasar de horas para hacer la inserción que el proceso almacenado hace en segundos o incluso milisegundos. No utilice el procesamiento basado en registros cuando necesite procesar un conjunto de datos, especialmente si no es por una razón tonta, como la reutilización del código. El rendimiento supera la reutilización del código.

+0

Hay algunos elementos en los que absolutamente * debe * utilizar cursores, aunque como en el caso de GOTO, la necesidad real de ellos es realmente muy pequeña. Acuerde que es mejor hacer el procesamiento basado en conjuntos donde sea que tenga sentido, pero su "rendimiento prevalece sobre la reutilización del código" es un poco extremista. –

+0

Como estamos hablando de la diferencia entre minutos y milisegundos u horas y segundos (o minutos), no creo que sea extremista. Los programadores que no consideran el rendimiento de la base de datos sobre la reutilización del código (particularmente cuando se trata de usar cursores) generalmente tienen bases de datos que funcionan muy mal. – HLGEM

+0

Usted fue quien dijo que era segundos vs. horas. En ese caso de hombre de paja, ¿quién podría discutir? Pero si la diferencia es de milisegundos frente a segundos o milisegundos frente al doble de milisegundos, depende. – stannius

1

De ser posible, escribiría una segunda versión del proceso almacenado que se lee desde una tabla temporal.

Si eso no es posible, probablemente no tenga suerte.

12

Si solo está usando SQL Server 2005 o posterior, no se preocupa por la compatibilidad con versiones anteriores y puede convertir su código en una Función definida por el usuario (en lugar de un proceso almacenado), puede usar el nuevo " "CROSS APPLY", que usa una sintaxis muy similar a la que desea. He encontrado here una breve introducción (por supuesto, también puede leer los BOLs y MSDN)

Suponiendo que el SP devuelve un solo valor denominado out_int, su ejemplo podría ser reescrita como:

SELECT T.id, UDF.out_int 
FROM 
    Table1 T 
CROSS APPLY 
    dbo.fn_My_UDF(T.id) AS UDF 

Este recuperará cada "id" de la Tabla1 y lo usará para llamar a fn_My_UDF, cuyo resultado aparecerá en el conjunto de resultados final además del parámetro original.

Una variante de "APLICACIÓN CRUZADA" es "APLICACIÓN EXTERIOR". Son equivalentes de "INNER JOIN" y "LEFT JOIN", pero trabajan para unirse a una tabla y un UDF (y llamar al segundo al mismo tiempo).

Si usted debe (por orden explícito del jefe con el pelo puntiagudo) utilice los SPs insead, bueno, ¡mala suerte! Tendrá que seguir con los cursores o intentar hacer trampa un poco: cambie el código en UDF y cree SP de envoltura: D.

+0

CROSS APPLY no funciona para los procedimientos almacenados, ya que no devuelven conjuntos de datos, solo informan las salidas. Las funciones devuelven cualquier cosa, por lo que SQL solo puede usarlas cuando se usa CROSS APPLY. Abajo votado. – Fandango68

+8

@ Fernando68 Indiqué 3 condiciones necesarias para usar CROSS APPLY como solución al principio de mi respuesta, la 3ª de las cuales es "puede convertir su código para que sea un UDF en lugar de SP". –

0

Este bucle funciona, pero fue lento para mi SP.Creo que la respuesta correcta es por HLGEM, su mejor apuesta es escribir una mejor consulta masiva.

DECLARE @id INT 
SET @id = 0 

DECLARE @max INT 
SELECT TOP 1 @max = TableID 
FROM dbo.Table 
ORDER BY TableID desc 

-- loop until BREAK 
-- this is how you can perform a DO-WHILE loop in TSQL 
WHILE (1 = 1) 
BEGIN 
    SELECT  
     TOP 1 @id = TableID 
     FROM dbo.Table 
     WHERE TableID > @id 

    IF @id IS NOT NULL 
    BEGIN   
     -- call you SP here 
     PRINT @id   
    END 

    -- break out of the loop once the max id has been reached 
    IF @id = @max BREAK 
END 
Cuestiones relacionadas