有没有办法在sql中优化或替换while循环?

问题描述 投票:0回答:3

我们有几百万行数据需要通过在started_at日期和ends_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;
sql sql-server tsql while-loop
3个回答
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_)

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);

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)
© www.soinside.com 2019 - 2024. All rights reserved.