有没有一种方法可以优化或替换SQL中的while循环?

时间:2019-04-15 20:50:45

标签: sql sql-server tsql while-loop

我们需要通过在start_at日期和end_at日期之间的每个日期添加一行来“爆炸”几百万行数据。 while循环是查询中耗时最长的循环。

关于如何优化或替换它的任何想法?

IF (OBJECT_ID('TempDb..#exploded_services') IS NOT NULL)
  DROP TABLE #exploded_services;

CREATE TABLE #exploded_services
  (
   target_date date,
   move_id varchar(30),
   initiation_id varchar(30),
   initiated_at date,
   booked_at date,
   transferee varchar(60),
   account_id varchar(30),
   mc_id varchar(30),
   po varchar(60),
   weight int,
   service varchar(150),
   started_at date,
   ended_at date,
   location_id nvarchar(64),
   description varchar(max),
   provider varchar(max),
   mode varchar(60),
   origin_location_id nvarchar(64),
   destination_location_id nvarchar(64),
   transferee_phone varchar(40),
   transferee_email varchar(100),
   status varchar(10),
   ordinal int
  );


WHILE (@pointer <= @end_date)
 BEGIN
   INSERT INTO #exploded_services
   SELECT
     @pointer,
     svcs.*
   FROM #Services svcs
   WHERE @pointer BETWEEN svcs.started_at AND COALESCE(svcs.ended_at,@end_date)
   SET @pointer = DATEADD(dd, 1, @pointer)
 END;

3 个答案:

答案 0 :(得分:1)

  1. 用一个日期列创建一个表。
  2. 填充适用于您服务的所有可能日期。
  3. 使用以下命令填充目标表:
 INSERT INTO #exploded_services
   SELECT
     dates_table.date,
     svcs.*
   FROM #Services svcs
   INNER JOIN dates_table ON dates_table.date BETWEEN svcs.started_at AND COALESCE(svcs.ended_at,_arbitrary_end_date_)

答案 1 :(得分:0)

这可以使用Tally表来实现。这是一个如何使用级联ctes动态创建的示例的示例。

WITH 
E(n) AS(
    SELECT n FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0))E(n)
),
E2(n) AS(
    SELECT a.n FROM E a, E b
),
E4(n) AS(
    SELECT a.n FROM E2 a, E2 b
),
cteTally(n) AS(
    SELECT TOP(DATEDIFF(DD, @pointer, @end_date) + 1) 
            ROW_NUMBER() OVER(ORDER BY (SELECT NULL))-1 n
    FROM E4
)
INSERT INTO #exploded_services
SELECT
    DATEADD( dd, n @pointer),
    svcs.*
FROM #Services svcs
JOIN cteTally t ON DATEADD( dd, n @pointer) BETWEEN svcs.started_at AND COALESCE(svcs.ended_at,@end_date);

答案 2 :(得分:0)

您可以尝试使用CTE在下面的代码中生成所需的所有日期:

 -- cte to get all dates needed
 ;with cte as (
    select @pointer ptr
    union all
    select DATEADD(dd, 1, @pointer) from cte
    where @pointer < @end_date
 )
 -- adjusted insert query
 INSERT INTO #exploded_services
 select c.*, s.*
 from #Services s
 join cte c on c.ptr between s.started_at and coalesce(svcs.ended_at,@end_date)