2010-10-26 11 views
6

Supongamos que tengo un grupo de filas en un DB (SQLServer 2008 en este caso) que pueden usarse para crear ecuaciones.Cursores vs. ciclo while - SQLServer

----------------------------------------------------- 
OperationID | EquationID | Operation | Amount | Order 
----------------------------------------------------- 
    1  |  1  |  +  | 12 | 1 
    2  |  1  |  +  | 12 | 2 
    3  |  2  | / | 2 | 3 
    4  |  2  |  +  | 12 | 1 
    5  |  2  |  -  | 2 | 2 
----------------------------------------------------- 

Necesito encontrar una forma de evaluar las ecuaciones en esta tabla.

Ecuación 1: 12 + 12 = 24
Ecuación 2: (12 - 2)/2 = 5

no puedo pensar en una manera de conseguir estos resultados sin iteración a través de las filas. La única forma en que sé cómo hacerlo es con un cursor o mediante el uso de una tabla temporal y un ciclo while. ¿Hay alguna forma mejor de hacer esto? Si no es generalmente, ¿qué llevará a cabo mejores cursores o bucles while?

Nota: Esto es algo simplificado y en esta etapa del proyecto solo podemos conjeturar sobre cómo se verán los datos. La suposición es que cada 'ecuación' tendrá alrededor de 100 a 1000 operaciones y que habrá unas miles de 'ecuaciones' por día que deberán procesarse.

+2

si está en el servidor sql 2005+ puede usar un CTE recursivo, usando un CASO para +, -,/o * Supongo, ¿cómo se manejan los paréntesis? ¿Se acaba de hacer según la columna Orden? –

+1

¿Cómo sabes a dónde deberían ir los corchetes? – AdaTheDev

+0

Mi pensamiento inicial es, presumiblemente, que tienes una ecuación "bien formada" antes de insertarla en esta estructura, así que me pregunto por qué no puedes simplemente almacenar la ecuación en su totalidad. Siempre puede parametrizarlo (por ejemplo, "(@ p1 - @ p2)/@ p3") para permitir asignar valores en el momento de la ejecución. Sería mucho más simple ... y más simple = bueno :) – AdaTheDev

Respuesta

3

Se ha demostrado que un CTE recursivo funciona mejor que un bucle para generar totales acumulados. Esto es solo un total acumulado con un operador variable realmente, por lo que el beneficio de rendimiento debería aplicarse aquí.

La manera de crear un CTE recursiva que se comporta como un bucle es de esta manera:

 ;WITH cte AS (
     SELECT equation, number, order FROM table WHERE order = 1 
     UNION ALL 
     SELECT table.equation, 
      CASE WHEN table.operation = '+' THEN cte.number + table.number 
       WHEN table.operation = '-' THEN cte.number - table.number END AS number, --etc. 
table.order FROM table INNER JOIN cte ON table.order = cte.order + 1 AND table.equation = cte.equation 
     ) 
    SELECT equation, number, order 
    FROM cte 
    OPTION (MAXRECURSION 1000); 

El primer SELECT agarra su número más a la izquierda, y la unión de todo hace las siguientes operaciones en el número devuelto por el mismo. La opción maxrecursion limita el número de operaciones en una ecuación a 1000. Puede, por supuesto, establecer esto más alto.

Esta respuesta es algo incompleta, porque la consulta de selección final devolvería resultados intermedios. Sin embargo, eso es bastante simple de filtrar.

+0

¿Cómo devolvería la consulta final los resultados intermedios? – jcolebrand

+0

Si los selecciono a todos, devolverá: Ecuación: 1 Orden: 1 Número: 12 y Pedido: 2 Número: 24 por ejemplo. Este ejemplo es más un total acumulado donde solo se necesita el total real. Por supuesto, si solo quieres el resultado de una ecuación, puedes SELECCIONAR TOP 1 DONDE ecuación = 1 ORDER BY orden DESC –

+0

+1 Me tomé la libertad de completar tu código un poco en [esta respuesta] (http: // stackoverflow.com/questions/4027477/cursors-vs-while-loop-sqlserver/4027870#4027870). Hice mi wiki de comunidad de respuestas como mereces el crédito. –

3

He limpiado/desarrollado mootinator's answer un poco y estoy presentando ese código aquí. Marqué esta wiki de la comunidad de respuestas porque el motivador merece el crédito por la respuesta. Esta era la forma más sencilla de presentar ese código sin editar su respuesta.

declare @equations table (
    OperationID int, 
    EquationID int, 
    Operation char(1), 
    Amount int, 
    [Order] int 
) 

insert into @equations 
    (OperationID, EquationID, Operation, Amount, [Order]) 
    values 
    (1, 1, '+', 12, 1), 
    (2, 1, '+', 12, 2), 
    (3, 2, '/', 2, 3), 
    (4, 2, '+', 12, 1), 
    (5, 2, '-', 2, 2) 

;with cteCalc as (
    select EquationID, Amount, [Order] 
     from @equations 
     where [Order] = 1 
    union all 
    select e.equationid, 
      case when e.Operation = '+' then c.Amount + e.Amount 
       when e.Operation = '-' then c.Amount - e.Amount 
       when e.Operation = '*' then c.Amount * e.Amount 
       when e.Operation = '/' then c.Amount/e.Amount 
      end AS Amount, 
      e.[Order] 
     from @equations e 
      inner join cteCalc c 
       on e.EquationID= c.EquationID 
     where e.[Order] = c.[Order] + 1 
), 
cteMaxOrder as (
    select EquationID, MAX([Order]) as MaxOrder 
     from cteCalc 
     group by EquationID 
) 
select c.EquationID, c.Amount 
    from cteMaxOrder mo 
     inner join cteCalc c 
      on mo.EquationID = c.EquationID 
       and mo.MaxOrder = c.[Order] 
    order by c.EquationID 
    option (maxrecursion 1000) 
Cuestiones relacionadas