使用SQL语句将行拆分为多个

时间:2015-09-21 07:07:42

标签: sql sql-server tsql

我在以下表单中的数据库表中有一行:

ID | Amount | From       | To
5  | 5439   | 01.01.2014 | 05.01.2014

我想使用SQL / T-SQL将它分成一行pr月:

 Amount | From       
 5439   | 01.01.2014 
 5439   | 02.01.2014 
 5439   | 03.01.2014 
 5439   | 04.01.2014
 5439   | 05.01.2014

遗憾的是,我无法更改数据库源代码,我希望在SQL中更好地执行此操作,因为我尝试使用Powerpivot中的其他表来查询此查询。

编辑:根据我的代码请求,我尝试了以下内容:

declare @counter int
set @counter = 0
WHILE  @counter < 6
begin
    set @counter = @counter +1
    select amount, DATEADD(month, @counter, [From]) as Dato
    FROM [database].[dbo].[table]
end

然而,这会返回多个数据库集。

5 个答案:

答案 0 :(得分:4)

您可以使用tally table生成所有日期。

SQL Fiddle

;WITH E1(N) AS(
    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
),
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b),
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b),
Tally(N) AS(
    SELECT TOP(SELECT MAX(DATEDIFF(DAY, [From], [To])) + 1 FROM yourTable)
        ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
    FROM E4
)
SELECT 
    yt.Id,
    yt.Amount,
    [From] = DATEADD(DAY, N-1, yt.[From])
FROM yourTable yt
CROSS JOIN Tally t
WHERE
    DATEADD(DAY, N-1, yt.[From]) <= yt.[To]

Simplified explanation on Tally Table

答案 1 :(得分:2)

您需要一个带有“正在运行的号码”的计数表。这可能是一个函数(我在这里很快发布了一个:https://stackoverflow.com/a/32096945/5089204)或一个物理表(我在这里发布了一个示例:https://stackoverflow.com/a/32474751/5089204)或CTE来“动态”执行此操作(表格示例)是这样做的。)

如果你使用发布的功能,它可能是这样的:

declare @startDate DATETIME={d'2015-09-01'};
declare @EndDate DATETIME={d'2015-09-10'};
select DATEADD(DAY, Nmbr,@startDate) 
from dbo.GetRunningNumbers(DATEDIFF(DAY,@startDate,@endDate)+1,0);

答案 2 :(得分:2)

select * INTO #TEMP1 from 
(values
(5  , 5439   , '01.01.2014', '05.01.2014'))t(id,amount,fromd,tod)

WITH CTE
AS
(
    SELECT CAST(FROMD AS DATE) AS FROMD,amount,1 AS RN,ID FROM #TEMP1
    UNION ALL
    SELECT DATEADD(M,1,C.FROMD),C.amount,C.RN+1,C.ID 
    FROM CTE C 
            INNER JOIN #TEMP1 T ON T.id = C.ID AND DATEADD(M,1,c.FROMD)<=T.tod

)

SELECT * FROM CTE

答案 3 :(得分:2)

create table t (fd date, td date)
insert into t values ('2015-01-01','2015-01-05')

WITH DATES (fd, td, Level)
AS
(
    SELECT fd, td, 0 AS Level
    FROM t
    UNION ALL
-- Recursive member definition
    SELECT DATEADD(day,level+1,e.fd),e.td,Level + 1
    FROM t AS e
    INNER JOIN Dates AS d ON DATEADD(day,-d.level,d.fd) = e.fd AND d.fd < d.td

)
-- Statement that executes the CTE
SELECT fd,td,level
from DATES

答案 4 :(得分:1)

使用递归cte

变体

--variable table for data sample
DECLARE @tbl AS TABLE
    (
      ID INT ,
      Amount FLOAT ,
      [From] DATE ,
      [To] DATE
    )
INSERT  INTO @tbl
        ( ID, Amount, [From], [To] )
VALUES  ( 5, 5439, '2014-01-01', '2014-01-05' )

--final query using recursive cte
;
WITH    cte
          AS ( SELECT   T.ID ,
                        T.Amount ,
                        T.[From] ,
                        T.[To] ,
                        CONVERT(DATE, NULL) AS Dt ,
                        n = 0
               FROM     @tbl AS T
               UNION ALL
               SELECT   cte.ID ,
                        cte.Amount ,
                        cte.[From] ,
                        cte.[To] ,
                        DATEADD(DAY, n, cte.[From]) ,
                        cte.n + 1
               FROM     cte
               WHERE    n <= DATEDIFF(day, cte.[From], cte.[To])
             )
    SELECT  cte.ID ,
            cte.Amount ,
            dt AS [From]
    FROM    cte
    WHERE   cte.Dt IS NOT NULL


SQL Fiddle