【问题标题】:Optimizing a stored procedure that uses cursor优化使用游标的存储过程
【发布时间】:2020-02-04 21:57:44
【问题描述】:

有一个长时间运行的存储过程。第二个表(访问)仅用于 where 子句中使用的结果,同时从第一个表(批准)中检索记录。

下面是涉及到的两个表的DDL,以及存储过程的脚本。显然,因为它只是一个样本,它不会显示任何记录;因此,该样本数据没有观察到瓶颈。但是想象一下,游标中有许多 userr_id。 由于它正在调用另一个存储过程,因此还有额外的成本。有什么办法可以优化查询吗?

表的 DDL

 CREATE TABLE [dbo].[Approvals](
    userr_id [int] NOT NULL,
    appro_id [int] NOT NULL,
    app_units [decimal](9, 2) NOT NULL,
    c_units [tinyint] NOT NULL,
    usedunits [decimal](9, 2) NOT NULL,
    deleted [bit] NOT NULL
) 

INSERT INTO approvals 
VALUES
(4262,  29, 36.00,  1,  0.00,   0),
(1717,  30, 24.00,  1,  0.00,   0),
(4743,  31, 76.00,  1,  0.00,   0),
(4460,  33, 36.00,  1,  0.00,   0),
(4488,  35, 36.00,  1,  0.00,   0),
(3871,  36, 24.00,  1,  0.00,   0),
(3561,  37, 12.00,  1,  3.00,   0),
(4828,  38, 36.00,  1,  0.00,   0),
(3828,  39, 24.00,  1,  0.00,   0),
(4101,  40, 24.00,  1,  0.00,   0)

CREATE TABLE [dbo].[Visit](
    userr_id [int] NULL,
    appro_id [int] NULL,
    c_secondary [bit] NULL,
    auth_exceeded [bit] NOT NULL,
    tperiod [int] NOT NULL,
    contratrate [decimal](9, 2) NOT NULL
) 

INSERT INTO visit 
VALUES
(5329,  NULL,   0, 0,   419,    0.00),
(4262,  NULL,   0, 0,   419,    0.00),
(5244,  NULL,   0, 0, 419,  0.00),
(4205,  NULL,   0, 0,   419,    0.00),
(4828,  NULL,   0, 0,   419,    0.00),
(5531,  NULL,   0,0,    419,    0.00),
(5558,  NULL,   0,  0, 419, 0.00),
(4460,  NULL,   0,  0, 419, 0.00),
(5547,  NULL,   0,  0, 419, 0.00),
(5219,  NULL,   0,  0, 419, 0.00)

存储过程:

CREATE PROCEDURE [dbo].[stored_procedure] 
AS 

DECLARE @userr_id Int, @cnt Int
SET @cnt = 0

DECLARE c CURSOR FOR


    SELECT DISTINCT userr_id 
      FROM (SELECT userr_id, app_units, c_units, usedunits,
                   (SELECT count(*) FROM Visit WHERE c_secondary = 0 and appro_id = Approvals.appro_id and auth_exceeded = 0) AS count_notexceeded,
                   (SELECT count(*) FROM Visit WHERE c_secondary = 0 and appro_id = Approvals.appro_id ) AS count_all,
                   (SELECT SUM(tperiod) / 60.00 FROM Visit WHERE c_secondary = 0 and appro_id = Approvals.appro_id ) AS th_all,
                   (SELECT SUM(tperiod) / 60.00 FROM Visit WHERE c_secondary = 0 and appro_id = Approvals.appro_id and auth_exceeded = 0) AS th_notexceeded,
                   (SELECT SUM(contratrate) FROM Visit WHERE c_secondary = 0 and appro_id = Approvals.appro_id and auth_exceeded = 0) AS tr_notexceeded,
                   (SELECT SUM(contratrate) FROM Visit WHERE c_secondary = 0 and appro_id = Approvals.appro_id ) AS tr_all
              FROM Approvals where deleted = 0) t
     WHERE ((c_units = 0 and (count_all <> usedunits or count_notexceeded > app_units))
        OR  (c_units = 2 and (th_all <> usedunits or th_notexceeded > app_units))
        OR  (c_units = 3 and (tr_all <> usedunits or tr_notexceeded > app_units)))

 OPEN c
FETCH NEXT FROM c INTO @userr_id
WHILE @@fetch_status = 0
BEGIN
  SET @cnt = @cnt + 1

  EXEC [_name_of_another_stored_procedure] @userr_id

  FETCH NEXT FROM c INTO @userr_id
END
CLOSE c
DEALLOCATE c

谢谢。任何指南都非常受欢迎。

【问题讨论】:

  • 为什么运行时间长?是打开光标吗?它是否正在运行其他存储过程?无论哪种情况,查询计划和等待统计信息是什么?
  • 基本上就是打开光标。我已经单独尝试过了。我想,是因为它多次从访问表中选择,以便处理它以在 where 子句中使用。

标签: sql-server tsql stored-procedures cursor


【解决方案1】:

尝试用类似这样的方式替换游标查询,希望不必多次扫描:

with t as
(
    SELECT userr_id, app_units, c_units, usedunits,
        sum (case when auth_exceeded = 0 then 1 else 0 end) AS count_notexceeded,
        count(*) AS count_all,
        SUM(tperiod) / 60.00 AS th_all,
        SUM(case when auth_exceeded = 0 then tperiod else 0 end) / 60.00 AS th_notexceeded,
        SUM(case when auth_exceeded = 0 then contratrate else 0 end) AS tr_notexceeded,
        SUM(contratrate) AS tr_all
    FROM Approvals 
    JOIN Visit  
      on Visit.appro_id = Approvals.appro_id
    where deleted = 0
    and Visit.c_secondary = 0
    group by userr_id, app_units, c_units, usedunits

)
SELECT DISTINCT userr_id 
FROM t
WHERE ((c_units = 0 and (count_all <> usedunits or count_notexceeded > app_units))
OR  (c_units = 2 and (th_all <> usedunits or th_notexceeded > app_units))
OR  (c_units = 3 and (tr_all <> usedunits or tr_notexceeded > app_units)))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多