如何在SQL Server视图中获得用户明智的分配任务?

时间:2016-07-31 18:40:24

标签: sql-server

我有一个employee表,其中包含列:

empid, empname

然后是一个projectplan表,其中包含列:

planid, empid, task, start, end

如何创建如下所示的视图:

Date     Emp1    Emp2           Emp3           Emp4 .. 
------------------------------------------------------------
01-Jun   task1   task2,task3    task2,task4    task1,task5
02-Jun   task1   task2,task3    task6          task7

感谢任何帮助。

2 个答案:

答案 0 :(得分:0)

您对任务的解释并不是那么清楚。但我知道您无法使用可变列数创建视图。您可以使用生成所需结果的动态SQL创建存储过程。在这里我的消化:

CREATE TABLE #employee (
  empid int, 
  empname varchar(100), 
  CONSTRAINT PK_employee PRIMARY KEY (empid)
);

CREATE TABLE #projectplan (
  planid int, 
  empid int, 
  task varchar(100), 
  start date, 
  [end] date, 
  CONSTRAINT PK_projectplan PRIMARY KEY (planid)
);

INSERT INTO #employee 
VALUES 
  (1, 'John'),
  (2, 'Mary')

INSERT INTO #projectplan
VALUES 
  (1, 1, 'Task 1', '20160110', '20160112'),
  (2, 1, 'Task 2', '20160113', '20160228'),
  (3, 1, 'Task 3', '20160315', '20160328'),
  (4, 1, 'Task 4', '20160315', '20160328'),
  (5, 2, 'Task 3', '20160315', '20160319'),
  (6, 2, 'Task 4', '20160320', '20160520')

DECLARE @cols nvarchar(MAX) = 
  STUFF((
    SELECT ',' + QUOTENAME(empname + LTRIM(STR(empid))) 
    FROM #employee 
    FOR XML PATH('')
  ), 1, 1, '')

EXEC ('
  SELECT *
  FROM (
    SELECT 
      p.start AS [Date],
      e.empname + LTRIM(STR(e.empid)) AS [Emp],
      STUFF((
        SELECT '','' + task
        FROM #projectplan
        WHERE empid = p.empid
          AND start = p.start
        FOR XML PATH('''')
      ), 1, 1, '''') AS TaskList
    FROM (
      SELECT DISTINCT
        start,
        empid
      FROM #projectplan
    ) AS p
    INNER JOIN #employee AS e ON e.empid = p.empid
  ) AS it
  PIVOT (
    MAX(TaskList) FOR Emp IN (' + @cols + ')
  ) AS pt')

答案 1 :(得分:0)

在视图中无法执行此操作,如果添加新员工,则需要重写它。这就是你需要动态SQL的原因。

首先我创建了带有虚拟数据的表:

SELECT * INTO #employee
FROM (VALUES
(1, 'John Smith'),
(2, 'Richard Morris'),
(3, 'Jason Campbell'),
(4, 'Anthony Watson'),
(5, 'Jeff Butler')
) as t(empid, empname)

SELECT * INTO #projectplan 
FROM (VALUES
(1, 1, 'task1','2016-06-01','2016-06-07'),
(2, 2, 'task2','2016-06-01','2016-06-05'),
(3, 3, 'task3','2016-06-01','2016-06-05'),
(4, 4, 'task4','2016-06-01','2016-06-03'),
(5, 5, 'task5','2016-06-01','2016-06-04'),
(6, 2, 'task6','2016-06-02','2016-06-09'),
(7, 3, 'task7','2016-06-03','2016-06-17'),
(8, 5, 'task8','2016-06-02','2016-06-19')
) as t(planid, empid, task, [start], [end])

然后我使用CTE获取MIN([start])MAX([end])之间的所有日期,以及所有员工的另一个CTE以及每个日期的任务:

;WITH cte AS (
    SELECT  CAST(MIN([start]) as datetime) as dateStart,
            CAST(MAX([end]) as datetime) as dateEnd
    FROM #projectplan
    UNION ALL
    SELECT  DATEADD(day,1,dateStart),
            dateEnd
    FROM cte
    WHERE dateStart < dateEnd
), res AS (
    SELECT  empname,
            task,
            c.dateStart as [date] 
    FROM cte c
    INNER JOIN #projectplan p
        ON c.dateStart between [start] and [end]
    INNER JOIN #employee e
        ON e.empid = p.empid
)
--And put them into `#final` temporary table
SELECT  r.empname,
        CAST(r.[date] as date) [date],
        STUFF((SELECT ','+task
        FROM res
        WHERE empname = r.empname AND [date] = r.[date]
        FOR XML PATH('')
        ),1,1,'') as tasks
INTO #final
FROM res r

所以在#final表中我有:

empname         date        tasks
John Smith      2016-06-01  task1
Richard Morris  2016-06-01  task2
Jason Campbell  2016-06-01  task3
Anthony Watson  2016-06-01  task4
Jeff Butler     2016-06-01  task5
John Smith      2016-06-02  task1
Richard Morris  2016-06-02  task2,task6
Jason Campbell  2016-06-02  task3
Anthony Watson  2016-06-02  task4
Jeff Butler     2016-06-02  task5,task8
...etc

如您所见,所有跨越日期任务的员工都以逗号分隔。然后我们用动态SQL PIVOT:

DECLARE @columns nvarchar(max),
        @query nvarchar(max)

SELECT @columns = STUFF(
(SELECT ',' + QUOTENAME(empname)
FROM #employee
FOR XML PATH('')),1,1,'')

SELECT @query = N'
SELECT *
FROM #final
PIVOT (
    MAX(tasks) FOR empname IN ('+@columns+')
) AS pvt'

EXEC sp_executesql @query

输出:

date        John Smith  Richard Morris  Jason Campbell  Anthony Watson  Jeff Butler
2016-06-01  task1       task2           task3           task4           task5
2016-06-02  task1       task2,task6     task3           task4           task5,task8
2016-06-03  task1       task2,task6     task3,task7     task4           task5,task8
2016-06-04  task1       task2,task6     task3,task7     NULL            task5,task8
2016-06-05  task1       task2,task6     task3,task7     NULL            task8
2016-06-06  task1       task6           task7           NULL            task8
2016-06-07  task1       task6           task7           NULL            task8
2016-06-08  NULL        task6           task7           NULL            task8
2016-06-09  NULL        task6           task7           NULL            task8
2016-06-10  NULL        NULL            task7           NULL            task8
2016-06-11  NULL        NULL            task7           NULL            task8
2016-06-12  NULL        NULL            task7           NULL            task8
2016-06-13  NULL        NULL            task7           NULL            task8
2016-06-14  NULL        NULL            task7           NULL            task8
2016-06-15  NULL        NULL            task7           NULL            task8
2016-06-16  NULL        NULL            task7           NULL            task8
2016-06-17  NULL        NULL            task7           NULL            task8
2016-06-18  NULL        NULL            NULL            NULL            task8
2016-06-19  NULL        NULL            NULL            NULL            task8

正如您所看到的,Richard Morris得到了2个任务task2,日期介于2016-06-012016-06-05以及task6之间,时间间隔介于2016-06-02之间2016-06-09。因此,间隔正在交叉,task2,task62016-06-02之间显示2016-06-05

希望这会对你有帮助!