Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server 如何优化执行时间过长的存储过程?_Sql Server_Tsql_Stored Procedures - Fatal编程技术网

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

Sql server 如何优化执行时间过长的存储过程?,sql-server,tsql,stored-procedures,Sql Server,Tsql,Stored Procedures,我已经编写了一个存储过程来生成随机SMS记录/事件 插入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

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

插入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

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

我喜欢使用的生成x个记录的方法是Aaron Bertrand阅读的堆叠CTE方法,Aaron Bertrand将堆叠CTE方法归功于Itzik Ben Gan:

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行开始,并保持交叉连接,直到在上述情况下有100000000行。这将是我生成您的行的方式

当您使用基于集合的方法时,您不能再单独使用RAND,因为它是一个运行时常数,为了获得每行的新计算值,您需要将RAND与每行唯一的NEWID组合起来,因此下面将生成一个0到100之间的随机数,每行的随机数不同:

SELECT  CAST(ROUND(RAND(CHECKSUM(NEWID())) * 100, 0) AS INT)
下一步我要做的是将您的所有电话号码放入临时表中,以便它们具有顺序ID,这将用于随机分配:

CREATE TABLE #Phone
(
    ID INT IDENTITY NOT NULL PRIMARY KEY,
    PhoneNo VARCHAR(50) NOT NULL
);
INSERT #Phone (PhoneNo)
SELECT PhoneNo 
FROM tbl_phone_agenda;
因此,您的最终查询将是

CREATE PROC insert_random_sms @number_of_records IN
AS
BEGIN
    CREATE TABLE #Phone
    (
        ID INT IDENTITY NOT NULL PRIMARY KEY,
        PhoneNo VARCHAR(50) NOT NULL
    );
    INSERT #Phone (PhoneNo)
    SELECT PhoneNo 
    FROM tbl_phone_agenda;

    -- NEEDED SO WE KNOW WHAT NUMBER TO GENERATE A RANDOM
    -- NUMBER IN THE RIGHT RANGE LATER
    DECLARE @PhoneCount INT = (SELECT COUNT(*) FROM #Phone);

    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)

    INSERT tbl_sms (phone_id, dest_id, charge, tarrif_plan) 
    SELECT  TOP (@number_of_records)
            p.PhoneNo,
            d.PhoneNo,
            Charge = CAST(ROUND(RAND(CHECKSUM(NEWID())) * 100, 0) AS INT),
            tarrif_plan = CAST(ROUND(RAND(CHECKSUM(NEWID())) * 5, 0) AS INT)
    FROM    N4
            INNER JOIN #Phone p
                ON p.ID = CAST(CEILING(RAND(CHECKSUM(NEWID())) * @PhoneCount) AS INT)
            INNER JOIN #Phone d
                ON d.ID = CAST(CEILING(RAND(CHECKSUM(NEWID())) * @PhoneCount) AS INT)

END

在我的测试中,这只需20-30秒就可以生成120万条记录,查询100000个电话号码。

通过对从现有表tbl\u phone\u 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

我的解决方案的灵感可以在这里找到:

我认为循环中的2个选择是试图从该表中获得2个随机电话号码?如果随机选择号码的开头并用它搜索前1,会怎么样?现在,您将对整个表进行排序,我认为这是一个大表。随机抽取的位数应取决于您的数据/您错过查找任何内容的频率。是。它从另一个包含网络中所有电话号码的表中获取随机电话号码。tbl_电话会议议程表包含120万人。还有唱片。对不起,这是个愚蠢的主意,用什么代替选择前1名。。。您是否选择top 1000或10000并在循环中处理它当然,只有当相同的数字没有出现时,这才是有效的,因为我经常发现是什么导致了长时间的延迟。这是从现有表tbl\U phone\U agenda中获取随机电话号码的方式。这句话实际上是:按newid从tbl\U phone\u议程顺序中选择top 1@phone\u id=phone\u no;很好的解决方案,但只有在手机id中没有空白时,这才有效column@t-clausen.dk考虑到桌上电话是在程序中创建的,有自己的标识栏,我看不出会有什么差距?我意识到,但我假设你想让它对问题中的数据起作用,为什么它对问题中的数据不起作用?我从tbl_phone_议程中插入电话,所以所有电话号码都在那里,它们刚刚被分配了一个身份,以避免您所说的问题-间隙。要么是这样,要么使用ROW_NUMBER动态生成序列,但我认为临时表的性能会更好,因为ID列需要两次。我没有使用伪代码,我编写的过程应该可以正常工作。我现在看到了,抱歉。干得好