2009-05-20 15 views
163

Tengo un procedimiento almacenado que altera los datos del usuario de cierta manera. Lo paso user_id y lo hace. Quiero ejecutar una consulta en una tabla y luego, para cada user_id, encuentro ejecutar el procedimiento almacenado una vez en ese user_id¿Cómo ejecuto un procedimiento almacenado una vez por cada fila devuelta por la consulta?

¿Cómo escribiría la consulta para esto?

+5

Es necesario para especificar qué RDBMS: la respuesta será diferente para SQL Server, Oracle, MySql, etc. –

+5

Lo más probable es que no necesite un procedimiento almacenado. ¿Puedes describir exactamente "qué" es el procedimiento almacenado? Tal vez todo el proceso se puede expresar como una única declaración de actualización. Por lo general, se debe evitar el patrón "do una vez para cada registro", si es posible. – Tomalak

+0

¿Qué base de datos estás usando? –

Respuesta

196

utilizar un cursor

Adición: [ejemplo de MS SQL cursor]

declare @field1 int 
declare @field2 int 
declare cur CURSOR LOCAL for 
    select field1, field2 from sometable where someotherfield is null 

open cur 

fetch next from cur into @field1, @field2 

while @@FETCH_STATUS = 0 BEGIN 

    --execute your sproc on each row 
    exec uspYourSproc @field1, @field2 

    fetch next from cur into @field1, @field2 
END 

close cur 
deallocate cur 

en MS SQL, here's an example article

nota que los cursores son más lentas que las operaciones establecidas a base, pero más rápido que el manual while-loops; más detalles in this SO question

ADDENDUM 2: si va a procesar más que unos pocos registros, primero tire de ellos a una tabla temporal y pase el cursor sobre la tabla temporal; esto evitará que SQL se intensifique en bloqueos de tabla y acelere la operación

ADDENDUM 3: y por supuesto, si puede alinear lo que su procedimiento almacenado está haciendo con cada ID de usuario y ejecutar todo como una única declaración de actualización SQL , eso sería óptimo

+0

cuidado para elaborar? – BigOmega

+0

@ [Ryan]: ver el apéndice. –

+0

Increcta sintaxis cerca de la palabra clave 'cerrar'? – BigOmega

7

Algo como esto sustituciones será necesario para sus tablas y nombres de campo.

Declare @TableUsers Table (User_ID, MyRowCount Int Identity(1,1) 
Declare @i Int, @MaxI Int, @UserID nVarchar(50) 

Insert into @TableUser 
Select User_ID 
From Users 
Where (My Criteria) 
Select @MaxI = @@RowCount, @i = 1 

While @i <= @MaxI 
Begin 
Select @UserID = UserID from @TableUsers Where MyRowCount = @i 
Exec prMyStoredProc @UserID 
Select 

@i = @i + 1, @UserID = null 
End 
+2

mientras que los bucles son más lentos que los cursores –

+0

No se admite el constructo o la instrucción Declare cursor SQL (??) – BigOmega

46

intente cambiar su método si necesita hacer un ciclo.

dentro del procedimiento primario almacenado, cree una tabla #temp que contenga los datos que necesita procesar. Llame al procedimiento almacenado secundario, la tabla #temp estará visible y podrá procesarla, con suerte trabajando con todo el conjunto de datos y sin un cursor o bucle.

esto realmente depende de lo que este procedimiento almacenado hijo está haciendo. Si está ACTUALIZANDO, puede "actualizar desde" unirse en la tabla #temp y hacer todo el trabajo en una declaración sin un bucle. Lo mismo se puede hacer para INSERT y DELETE. Si necesita realizar varias actualizaciones con IF, puede convertirlas a múltiples UPDATE FROM con la tabla #temp y usar las sentencias CASE o WHERE conditions.

Cuando trabaje en una base de datos intente perder la mentalidad de bucle, es un drenaje de rendimiento real, causará bloqueo/bloqueo y ralentizará el procesamiento. Si realiza un bucle en todas partes, su sistema no se escalará muy bien y será muy difícil acelerarlo cuando los usuarios comiencen a quejarse por la lentitud de las actualizaciones.

Publique el contenido de este procedimiento que desea llamar en un bucle, y apostaría 9 de cada 10 veces, podría escribirlo para trabajar en un conjunto de filas.

+4

Le daría cien votos ascendentes si pudiera – HLGEM

+2

+1 para una muy buena solución, suponiendo que controla el sproc secundario –

+0

un poco de pensamiento, esta solución es muy superior! – encc

4

¿No se puede hacer esto con una función definida por el usuario para replicar lo que sea que esté haciendo el procedimiento almacenado?

SELECT udfMyFunction(user_id), someOtherField, etc FROM MyTable WHERE WhateverCondition 

donde udfMyFunction es una función que hace que se lleva en el ID de usuario y hace lo que tiene que hacer con él.

Ver http://www.sqlteam.com/article/user-defined-functions un poco más de fondo

Estoy de acuerdo que los cursores realmente debe evitarse siempre que sea posible. ¡Y usualmente es posible!

(por supuesto, mi respuesta presupone que solo está interesado en obtener el resultado del SP y que no está cambiando los datos reales. Encuentro que "altera los datos del usuario de cierta manera" es un poco ambiguo desde la pregunta original, así que pensé en ofrecer esto como una posible solución. ¡Depende completamente de lo que esté haciendo!)

+1

OP: "procedimiento almacenado que altera los datos del usuario de cierta manera" [MSDN] (http://technet.microsoft.com/en-us/library/ms191320.aspx): las funciones definidas por el usuario no se pueden usar para realizar acciones que modifican el estado de la base de datos. Sin embargo SQLSVR 2014 no parece tener un problema con él –

3

Puede hacerlo con una consulta dinámica.

declare @cadena varchar(max) = '' 
select @cadena = @cadena + 'exec spAPI ' + ltrim(id) + ';' 
from sysobjects; 
exec(@cadena); 
1

Me gusta la forma de consulta dinámica de Dave Rincon ya que no utiliza cursores y es pequeña y fácil. Gracias Dave por compartir.

Pero para mis necesidades en SQL Azure y con un "distinta" en la consulta, tuve que modificar el código como el siguiente:

Declare @SQL nvarchar(max); 
-- Set SQL Variable 
-- Prepare exec command for each distinctive tenantid found in Machines 
SELECT @SQL = (Select distinct 'exec dbo.sp_S2_Laser_to_cache ' + 
       convert(varchar(8),tenantid) + ';' 
       from Dim_Machine 
       where iscurrent = 1 
       FOR XML PATH('')) 

--for debugging print the sql 
print @SQL; 

--execute the generated sql script 
exec sp_executesql @SQL; 

espero que esto ayude a alguien ...

Cuestiones relacionadas