单一查询替代动态PIVOT

时间:2018-05-03 13:09:56

标签: sql-server azure-sql-database sql-server-2017

这对我来说已经有几天的脑筋急转弯了,我似乎无法解决它。

基本上我有一个项目,资源和分配表,我每天都会存储项目资源分配。您可以使用以下查询来构建表结构:

CREATE TABLE Projects ([ProjectID] INT IDENTITY PRIMARY KEY, [Name] VARCHAR(100));
CREATE TABLE Resources ([ResourceID] INT IDENTITY PRIMARY KEY,[Name] VARCHAR(100));

CREATE TABLE Allocation (
    [Resource] INT FOREIGN KEY REFERENCES Resources (ResourceID),
    [Project] INT FOREIGN KEY REFERENCES Projects (ProjectID),
    [Date] DATE);

此外,您可以使用下一个查询来生成虚拟数据:

DECLARE @seed INT = 65;

;WITH proj_cte
AS (
    SELECT @seed [seed]

    UNION ALL

    SELECT [seed] + 1
    FROM proj_cte
    WHERE [seed] < 90
    )
INSERT INTO Projects (name)
SELECT 'Project ' + CHAR([seed])
FROM proj_cte;

;WITH res_cte
AS (
    SELECT @seed [seed]

    UNION ALL

    SELECT [seed] + 1
    FROM res_cte
    WHERE [seed] < 90
    )
INSERT INTO Resources (name)
SELECT 'Resource ' + CHAR([seed])
FROM res_cte;

并使用随机虚拟数据填充Allocation表:

CREATE UNIQUE NONCLUSTERED INDEX ncu_resource_project_date ON Allocation (Resource, Project, DATE);

INSERT INTO Allocation (Resource, Project, DATE)
SELECT DISTINCT ResourceId, ProjectID, AllocationDate
FROM (
    SELECT ProjectID, abs(checksum(newid())) % 26 [ResourceId], cast(getdate() + (abs(checksum(newid())) % 26) AS DATE) [AllocationDate], abs(checksum(newid())) % 26 [filter]
    FROM Projects
    ) f
WHERE f.ResourceId > 0
    AND f.ProjectID > 0;
GO

INSERT INTO Allocation (Resource, Project, DATE)
SELECT DISTINCT ResourceId, ProjectID, AllocationDate
FROM (
    SELECT ProjectID, abs(checksum(newid())) % 26 [ResourceId], cast(getdate() + (abs(checksum(newid())) % 26) AS DATE) [AllocationDate], abs(checksum(newid())) % 26 [filter]
    FROM Projects
    ) f
INNER JOIN Allocation u
    ON u.Resource = f.ResourceId
        AND u.Project = f.ProjectID
        AND f.AllocationDate <> u.[Date]
        AND f.ResourceId > 0
        AND f.ProjectID > 0
WHERE NOT EXISTS (
        SELECT 1
        FROM Allocation uin
        WHERE uin.resource = u.resource
            AND uin.project = uin.project
            AND uin.DATE = f.AllocationDate;
        ) GO 250

现在,所有这一切的目标是编写一个可以参数化的单个查询(包括CTE),并返回类似于Allocation表的横截面的结果。在2个日期,@StartDate@EndDate(这些是参数)。

目标输出应如下所示:

enter image description here

我试图扩展一个CTE,它生成@StartDate@EndDate之间的日期范围,几乎可以通过以下查询找到一个有效的解决方案,但它有一些障碍:

  • 我仍然需要在PIVOT中手动输入日期范围
  • 我找不到符合我逻辑的PIVOT聚合函数(目前我有一个“bug”,因为我使用MIN(Project)为整个资源返回MIN(Project)日期范围)。

    DECLARE @srcdt DATE = '20180101', @enddt DATE = '20180116';
    
    ;WITH dates
    AS (
        SELECT @srcdt srcdt
    
        UNION ALL
    
    SELECT dateadd(day, 1, srcdt)
    FROM dates
    WHERE srcdt < @enddt
    )
    SELECT pvt.*
    FROM (
        SELECT r.Name AS Resource, p.Name AS Project, DATE
        FROM Allocation u
        INNER JOIN Projects p
            ON u.Project = p.ProjectID
        RIGHT JOIN Resources r
            ON u.resource = r.ResourceID
        ) pvt_src
    PIVOT (min(Project) FOR [Date] IN ([2018-05-01], [2018-05-02], [2018-05-03], [2018-05-04], [2018-05-05], [2018-05-06], [2018-05-07], [2018-05-08], [2018-05-09], [2018-05-10], [2018-05-11], [2018-05-12], [2018-05-13], [2018-05-14], [2018-05-15], [2018-05-16], [2018-05-17], [2018-05-18], [2018-05-19], [2018-05-20], [2018-05-21], [2018-05-22], [2018-05-23], [2018-05-24], [2018-05-25], [2018-05-26], [2018-05-27], [2018-05-28], [2018-05-29], [2018-05-30])) pvt
    

我可以使用动态PIVOT和动态SQL来生成我PIVOT的日期范围列,但这已经超过1个查询。

1 个答案:

答案 0 :(得分:0)

在不使用动态sql的情况下,无法生成未知(或数据驱动)列数的结果集。这不是一个脑筋急转弯,它是一堵砖墙。