如何使用T-SQL将数据从行转移到列

时间:2016-04-15 07:51:30

标签: sql-server tsql

我的SQL Server数据库中有这个数据结构:

User         FmDate          ToDate
------------------------------------
Andy         1/10            1/15
Baey         1/14            1/18
Christy      1/17            2/18

日期是自然日的范围,如何编写t-sql以像excel一样转动它们,如图所示:

Andy    1/10
Andy    1/11
Andy    1/12
Andy    1/13
Andy    1/14
Andy    1/15
Baey    1/14
Baey    1/15
Baey    1/16
Baey    1/17
Baey    1/18
Christy 1/17
Christy 1/18

1 个答案:

答案 0 :(得分:1)

您可以使用tally table生成从FmDateToDate的日期

DECLARE @range INT;
SELECT @range = MAX(DATEDIFF(DAY, FmDate, ToDate)) + 1 FROM tbl;

;WITH E1(N) AS( -- 10 ^ 1 = 10 rows
    SELECT 1 FROM(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t(N)
),
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b), -- 10 ^ 2 = 100 rows
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b), -- 10 ^ 4 = 10,000 rows
CteTally(N) AS(
    SELECT TOP(@Range) ROW_NUMBER() OVER(ORDER BY(SELECT NULL))
    FROM E4
),
CteDates([User], dt) AS(
    SELECT t.[User], DATEADD(DAY, N-1, FmDate)
    FROM tbl t
    CROSS JOIN CteTally ct
    WHERE DATEADD(DAY, N-1, FmDate) <= ToDate
)
SELECT * 
FROM CteDates
ORDER BY [User], dt;

SQL Fiddle

这假定您的日期为DATE格式。

在编辑之前,您的预期结果是一个透视版本。

要调整上述结果,请使用dynamic crosstab。将上述结果插入临时表进行处理:

IF OBJECT_ID('tempdb..##tembTbl') IS NOT NULL BEGIN
    DROP TABLE ##tempTbl
END

DECLARE @range INT;
SELECT @range = MAX(DATEDIFF(DAY, FmDate, ToDate)) + 1 FROM tbl;

;WITH E1(N) AS( -- 10 ^ 1 = 10 rows
    SELECT 1 FROM(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t(N)
),
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b), -- 10 ^ 2 = 100 rows
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b), -- 10 ^ 4 = 10,000 rows
CteTally(N) AS(
    SELECT TOP(@Range) ROW_NUMBER() OVER(ORDER BY(SELECT NULL))
    FROM E4
),
CteDates([User], dt) AS(
    SELECT t.[User], DATEADD(DAY, N-1, FmDate)
    FROM tbl t
    CROSS JOIN CteTally ct
    WHERE DATEADD(DAY, N-1, FmDate) <= ToDate
)
SELECT * 
INTO ##tempTbl
FROM CteDates;

DECLARE @sql NVARCHAR(MAX) = '';

SELECT @sql = 
'SELECT
    [User]' + CHAR(10)

SELECT @sql = @sql +
'   , MAX(CASE WHEN dt = ''' + CONVERT(VARCHAR(8), dt, 112) + ''' THEN ''Y'' ELSE '''' END) AS ' + QUOTENAME(CONVERT(VARCHAR(5), dt, 101)) + CHAR(10)
FROM (SELECT DISTINCT dt FROM ##tempTbl) t 
ORDER BY dt

SELECT @sql = @sql +
'FROM ##tempTbl
GROUP BY [User];'

PRINT (@sql);
EXEC (@sql);

SQL Fiddle

<强>结果:

|    User | 01/10 | 01/11 | 01/12 | 01/13 | 01/14 | 01/15 | 01/16 | 01/17 | 01/18 |
|---------|-------|-------|-------|-------|-------|-------|-------|-------|-------|
|    Andy |     Y |     Y |     Y |     Y |     Y |     Y |       |       |       |
|    Baey |       |       |       |       |     Y |     Y |     Y |     Y |     Y |
| Christy |       |       |       |       |       |       |       |     Y |     Y |