如何优化执行时间过长的存储过程?

时间:2015-02-18 14:47:17

标签: sql-server tsql stored-procedures

我已经编写了一个存储过程来生成随机短信记录/事件。

插入120万行时,查询需要数百分钟

exec insert_random_sms 1200000

我已经在程序中对存储过程进行了编码'办法。但是,从我看来,SQL在这方面效率不高。

create proc insert_random_sms 
    @number_of_records int
as
begin
    declare @cnt int = 0;   -- loop counter
    declare @phone_id int;
    declare @dest_id int;

    while (@cnt < @number_of_records)
    begin
        declare @charge int = rand() * 100; -- will generate a random charge value between 0 and 100.
        declare @tarrif_plan int = round(rand() * 5, 0); 

        select top 1 @phone_id = phone_no 
        from tbl_phone_agenda 
        order by newid();

        select top 1 @dest_id = phone_no 
        from tbl_phone_agenda 
        order by newid();

        insert into tbl_sms (phone_id, dest_id, charge, tarrif_plan) 
        values (@phone_id, @dest_id, @charge, 
                convert(nvarchar(50), @tarrif_plan));

        set @cnt += 1;
    end
end
go

优化此存储过程的方法是什么?

2 个答案:

答案 0 :(得分:7)

我喜欢用于生成x个记录的方法是堆叠CTE方法(由Aaron Bertrand读过this article,他认为Itzik Ben-Gan的堆叠CTE方法):

WITH N1 (N) AS 
(   SELECT 1 
    FROM (VALUES 
            (1), (1), (1), (1), (1), 
            (1), (1), (1), (1), (1)
        ) n (Number)
),
N2 (N) AS (SELECT 1 FROM N1 AS N1 CROSS JOIN N1 AS N2),
N3 (N) AS (SELECT 1 FROM N2 AS N1 CROSS JOIN N2 AS N2),
N4 (N) AS (SELECT 1 FROM N3 AS N1 CROSS JOIN N3 AS N2)
SELECT COUNT(*)
FROM N4

这只是从10行开始,并保持交叉连接,直到上面的情况有100,000,000行。这就是我生成行的方式,

当您使用基于集合的方法时,您不能再单独使用RAND()因为它是运行时常量,以便为需要合并的每一行获取新的评估{{1} } RAND()每行唯一,因此以下将生成0到100之间的随机数,每行不同:

NEWID()

我要做的下一件事是将所有的phonenumbers放入临时表中,以便它们具有顺序ID(这将用于随机分配):

SELECT  CAST(ROUND(RAND(CHECKSUM(NEWID())) * 100, 0) AS INT)

所以你的最终查询将是

CREATE TABLE #Phone
(
    ID INT IDENTITY NOT NULL PRIMARY KEY,
    PhoneNo VARCHAR(50) NOT NULL
);
INSERT #Phone (PhoneNo)
SELECT PhoneNo 
FROM tbl_phone_agenda;

在我的测试中,大约需要20-30秒才能生成120万条记录,可以查找100,000个电话号码。

答案 1 :(得分:0)

random phone_no从现有表tbl_phone_agenda中取出的方式稍有改动,我在~50秒内完成了120万条记录的插入。毫无疑问,GarethD的解决方案是最快的。

-- create stored procedure to insert random records into the sms table,     automatically   |   tried and tested
create proc insert_random_sms @number_of_records int
as
begin
declare @cnt int = 0;   -- loop counter
declare @phone_id int;
declare @dest_id int;
while (@cnt < @number_of_records)
    begin
        declare @charge int = rand() * 100; -- will generate a random charge value between 0 and 100.
        declare @tarrif_plan int = round(rand() * 5, 0); 
        -- here come the changes
        select top 1 @phone_id = phone_no from tbl_phone_agenda where (abs(cast((binary_checksum(*) * rand()) as int)) % 100) < 10
        select top 1 @dest_id = phone_no from tbl_phone_agenda where (abs(cast((binary_checksum(*) * rand()) as int)) % 100) < 10
        insert into tbl_sms (phone_id, dest_id, charge, tariff_plan) values (@phone_id, @dest_id, @charge , convert(nvarchar(50), @tarrif_plan));
    set @cnt += 1;
    end
end
go

我的解决方案的灵感可以在这里找到:MSDN article - Selecting Rows Randomly from a Large Table

相关问题