【问题标题】:How do I transform the data in this SQL Table into a specific format?如何将此 SQL 表中的数据转换为特定格式?
【发布时间】:2018-02-21 12:49:18
【问题描述】:

我正在使用 SQL Server 2012,并且我有一个名为 T1 的表(摘录如下),其中包含大约 100,000 条记录。

Property    Room    Season   Datefrom    Dateto      Name
ABC LTD     DLX       P     01-01-2018  01-03-2018   John
XYZ LTD     SUP       P     01-01-2018  01-04-2018   Alan

我需要一个 SQL 查询来转换来自该表 T1 的数据并给我以下输出:

Property    Room    Season     Date       Name
ABC LTD     DLX        P    01-01-2018    John
ABC LTD     DLX        P    01-02-2018    John
ABC LTD     DLX        P    01-03-2018    John
XYZ LTD     SUP        P    01-01-2018    Alan
XYZ LTD     SUP        P    01-02-2018    Alan
XYZ LTD     SUP        P    01-03-2018    Alan
XYZ LTD     SUP        P    01-04-2018    Alan

如何使用 T-SQL 查询做到这一点?

【问题讨论】:

  • 使用日历表(例如Bones of SQL - The Calendar Table),然后使用DateToDateFromBETWEENJOIN 放到它上面。
  • 请阅读this,了解一些改进问题的技巧。诸如解释您想要完成什么以及您尝试过什么之类的事情很有帮助。

标签: sql sql-server tsql


【解决方案1】:

另一种选择是 CROSS APPLY 与 ad-hoc Tally Table

示例

Declare @YourTable Table ([Property] varchar(50),[Room] varchar(50),[Season] varchar(50),[Datefrom] varchar(50),[Dateto] date,[Name] varchar(50))
Insert Into @YourTable Values 
 ('ABC LTD','DLX','P','01-01-2018','01-03-2018','John')
,('XYZ LTD','SUP','P','01-01-2018','01-04-2018','Alan')


Select Property
      ,Room
      ,Season
      ,Date = B.D
      ,Name
 from @YourTable A
 Cross Apply ( 
                Select Top (DateDiff(DAY,Datefrom,IsNull([DateTo],DateFrom))+1) 
                       D=DateAdd(DAY,-1+Row_Number() Over (Order By (Select Null)),DateFrom) 
                 From  master..spt_values n1  --,master..spt_values << remove comment of span can be > 6 years 
             ) B

退货

【讨论】:

  • 约翰,这很及时,我真的很喜欢它,我只是用它来帮助另一个操作 stackoverflow.com/questions/48908324/…
  • 我投了赞成票,但似乎该线程上的操作与计数表和案例陈述答案一致。
  • @Random_User 对不起,我的小脑袋里有你的问题
【解决方案2】:

计数表或日历表是解决此问题的方法。递归解决方案通常速度较慢,并且可能导致其他错误。这个答案很像@JohnCappelletti 的答案,我包含了一个更可靠的计数表并拒绝了无效间隔。

DECLARE @YourTable TABLE
(
  [Property] varchar(50),
  [Room] varchar(50),
  [Season] char(1),
  [Datefrom] date,
  [Dateto] date,
  [Name] varchar(50)
)
INSERT @YourTable VALUES
 ('ABC LTD','DLX','P','01-01-2018','01-03-2018','John')
,('XYZ LTD','SUP','P','01-01-2018','01-04-2018','Alan')

;WITH N(N)AS 
(SELECT 1 FROM(VALUES(1),(1),(1),(1),(1),(1),(1),(1),(1),(1))M(N)),
tally(N)AS(SELECT 1 FROM N,N a,N b,N c,N d)
SELECT
  Property,
  Room,
  Season,
  b.Date,
  Name
FROM @YourTable a
CROSS APPLY
( 
  SELECT top(datediff(d,Datefrom,case when DateTo >= DateFrom
             then dateadd(d, 1, DateTo) else DateFrom end))
    DATEADD(d,row_number()over(order by 1/0)-1, DateFrom) Date
  FROM tally
) b

如果 dateto 在 datefrom 之前,则不会包含该行。

【讨论】:

  • 太棒了!在 33 秒内将大约 115,000 行转换为 2,830,000 行!
【解决方案3】:

如果不是很多天,那么这是学习递归 CTE 的好机会:

with cte as (
      select Property, Room, Season, datefrom as dte, Name, dateto
      from t
      union all
      select Property, Room, Season, dateadd(day, 1, dte), Name, dateto
      from cte 
      where dte < dateto
     )
select *
from cte;

默认情况下,这将工作长达 100 天。您可以使用option (maxrecursion 0),因此它适用于任何号码。

就性能而言,数字表更快,但递归 CTE 在许多情况下具有令人惊讶的合理性能。

【讨论】:

  • 谢谢。 'work for up to 100 days'是指每条记录的Datefrom和Dateto之间的差异不应超过100天吗?
  • @user3115933 。 . .是的,除非您使用最大递归选项。
【解决方案4】:

尝试使用递归 CTE

DECLARE @T TABLE
(
    Property    NVARCHAR(255),
    Room    NVARCHAR(255),
    Season   NVARCHAR(255),
    Datefrom    DATE,
    Dateto      DATE,
    Name NVARCHAR(255)
)

INSERT INTO @T
VALUES('ABC LTD','DLX','P','01-01-2018','01-03-2018','John'),
('XYZ LTD','SUP','P','01-01-2018','01-05-2018','Alan')

;WITH CTE
AS
(
    SELECT
       Property,
       Room,
       Season,
       MyDate = Datefrom,
       Name,
       Dateto
       FROM @T

    UNION ALL

    SELECT
       Property,
       Room,
       Season,
       MyDate = DATEADD(D,1,MyDate),
       Name,
       Dateto
       FROM CTE
          WHERE MyDate < Dateto

)
SELECT
    *
    FROM CTE
    ORDER BY Property
    OPTION(MAXRECURSION 0)

【讨论】:

    【解决方案5】:

    与其他答案相同或相似。我认为戈登是第一个。我正在练习递归 CTE。

    declare @t table (Property varchar(20), Room varchar(20), Season varchar(20), Datefrom datetime, Dateto datetime, Name varchar(20));
    insert into @t values 
           ('ABC LTD','DLX','P','01-01-2018','01-03-2018','John')
         , ('XYZ LTD','SUP','P','01-01-2018','01-05-2018','Alan');
    
    with cte as 
    ( select Property, Room, Season, Datefrom as [Date], Dateto, Name
      from @t 
      union all
      select Property, Room, Season, DATEADD(day, 1, [date]), Dateto, Name 
      from cte 
      where [date] < Dateto
    )
    
    select Property, Room, Season, casT([Date] as date) as [Date], Name 
    from cte 
    order by property, [date];
    

    【讨论】:

      【解决方案6】:

      使用日期范围表。

      declare @t table (Property varchar(20), Room varchar(20), Season varchar(20), DateFrom date, DateTo date, Name varchar(20));
      insert into @t values 
             ('ABC LTD','DLX','P','01-01-2018','01-03-2018','John')
           , ('XYZ LTD','SUP','P','01-01-2018','01-05-2018','Alan');
      
      declare @dateRange table (dt date primary key);
      declare @minDate date = (select min(datefrom) from @t);
      declare @maxDate date = (select max(dateto)   from @t);
      with dates as 
      (
           select @minDate as dt 
           union all 
           select DATEADD(day, 1, dt) 
           from dates 
           where dt < @maxDate
      )
      insert into @dateRange 
      select dt from dates option (maxrecursion 10000);
      
      select t.Property, t.Room, t.Season, d.dt as [Date], t.Name
        from @t t 
        join @dateRange d
          on d.dt >= t.DateFrom 
         and d.dt <= t.DateTo
       order by t.Property, t.Room, d.dt;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-14
        • 2021-07-01
        • 1970-01-01
        • 2021-10-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多