优化速度简单的存储过程

时间:2009-09-15 06:48:11

标签: sql sql-server tsql sql-server-2008

在SQL 2008中,我使用了这个简单但又不好写的sp:

ALTER PROCEDURE [dbo].[paActualizaCapacidadesDeZonas]
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @IdArticulo AS INT
    DECLARE @ZonaAct AS INT
    DECLARE @Suma AS INT

    UPDATE CapacidadesZonas SET Ocupado=0

    DECLARE csrSumas CURSOR FOR
         SELECT AT.IdArticulo, T.NumZona, SUM(AT.Cantidad) 
         FROM ArticulosTickets AT 
              INNER JOIN Tickets T ON AT.IdTicket = T.IdTicket
         GROUP BY AT.IdArticulo, T.NumZona

    OPEN csrSumas

    FETCH NEXT FROM csrSumas INTO @IdArticulo, @ZonaAct, @Suma

    WHILE @@FETCH_STATUS = 0
    BEGIN

         UPDATE CapacidadesZonas SET Ocupado = @Suma
         WHERE NumZona = @ZonaAct AND IdArticulo = @IdArticulo

         FETCH NEXT FROM csrSumas INTO @IdArticulo, @ZonaAct, @Suma
    END

    CLOSE csrSumas
    DEALLOCATE csrSumas   
END

我知道:我必须避免使用游标,所以我很确定它可以以适当的方式完成。

我尝试过一次更新查询:

UPDATE CapacidadesZonas SET Ocupado = 
(SELECT SUM(AT.Cantidad) 
 FROM ArticulosTickets AT 
       INNER JOIN Tickets T ON AT.IdTicket = T.IdTicket
     GROUP BY AT.IdArticulo, T.NumZona)

但这确实是错误的,因为select会返回多行。

我对此感觉很糟糕,因为我觉得这对我来说很容易,但我找不到相应的查询。

有什么建议吗?

提前致谢。

2 个答案:

答案 0 :(得分:3)

此问题有许多不同的解决方案 - 有关一些选项,请参阅this article。这是一种方法:使用派生表。

UPDATE CapacidadesZonas SET Ocupado=0 WHERE Ocupado <> 0;

UPDATE CapacidadesZonas 
SET Ocupado = SUM(s.Cantidad)
FROM CapacidadesZonas C INNER JOIN 
(
SELECT T.NumZona, AT.IdArticulo, SUM(AT.Cantidad) as Ocupado
    FROM ArticulosTickets AT 
    INNER JOIN Tickets T ON AT.IdTicket = T.IdTicket
    GROUP BY AT.IdArticulo, T.NumZona
) s ON s.NumZona = C.NumZona AND s.IdArticulo = C.IdArticulo;

警告:

  • 您是否预计在更新发生时,CapacidadesZon​​as表可用于实时应用程序?如果是这样,您可能会遇到锁定或性能问题,因为SQL可能会锁定整个表以更新每一行。如果是这种情况,请考虑批量更新(例如每个1000行)。 UPDATE TOP使批处理变得容易。
  • 有时候SQL会为这样的查询选择一个次优的计划。加载临时表(如上面的旁观者解决方案,但使用临时表而不是表var)可能比尝试将更新作为单个查询更快。如果您这样做,请记住在更新之前确保临时表上有一个索引(IDArticulo,NumZona)。

答案 1 :(得分:1)

尝试:

UPDATE cz
SET Ocupado = SUM(AT.Cantidad)
FROM CapacidadesZonas as cz
INNER JOIN ArticulosTickets AT ON cz.numZona = at.numZona and cz.IDArticulo = at.IDArticulo
INNER JOIN Tickets T ON AT.IdTicket = T.IdTicket
GROUP BY AT.IdArticulo, T.NumZona