为一周中的一天中的每个计数(*)创建一列

Jma*_*ier 5 sql-server-2008 sql-server

我有一张做计件工作的人的桌子。表中的每个条目都有一个用户 ID、名称和他们输入的每个报告的日期。

我需要每周报告这些人从星期日开始每天做了什么

  1. 从周日开始,你如何按一周分组?
  2. 您如何为每天的每个 COUNT(*) 制作一列?

t0001, Tod, 2015-6-29
t0001, Tod, 2015-6-29
t0001, Tod, 2015-6-29
t0001, Tod, 2015-6-29
t0001, Tod, 2015-6-29
t0001, Tod, 2015-6-28
t0001, Tod, 2015-6-28
b0002, Ben, 2015-6-29
b0002, Ben, 2015-6-29
b0002, Ben, 2015-6-28
Run Code Online (Sandbox Code Playgroud)

这就是我要找的。

NAME | S | M | T | W | R | F | S | TOTAL
----------------------------------------
TOD  | 2 | 5 | - | - | - | - | - | 7
BEN  | 1 | 2 | - | - | - | - | - | 3
Run Code Online (Sandbox Code Playgroud)

Aar*_*and 5

这是一种方法。假设此表和示例数据:

USE tempdb;
GO
CREATE TABLE dbo.splunge(UserID CHAR(5), Name VARCHAR(32), Date DATE);

INSERT dbo.splunge(UserID, Name, Date) VALUES('t0001','Tod','20150629'),
  ('t0001','Tod','20150629'),('t0001','Tod','20150629'),('t0001','Tod','20150629'),
  ('t0001','Tod','20150629'),('t0001','Tod','20150628'),('t0001','Tod','20150628'),
  ('b0002','Ben','20150629'),('b0002','Ben','20150629'),('b0002','Ben','20150628');
Run Code Online (Sandbox Code Playgroud)

然后用一个变量或参数来决定你关心的一周:

DECLARE @dt DATE = '20150702';

-- roll it back to Sunday
SET @dt = DATEADD(WEEK, DATEDIFF(WEEK, '19050101', @dt), '19050101');

;WITH dt(dt) AS
(
  SELECT TOP (7) dt = DATEADD(DAY, ROW_NUMBER() OVER (ORDER BY [object_id])-1, @dt)
  FROM sys.all_objects
  ORDER BY [object_id]
),
x AS 
(
  SELECT 
    s.Name, dt.dt, 
    dd = DATEDIFF(DAY, @dt, dt.dt), 
    c = CONVERT(VARCHAR(11), COUNT(*))
  FROM dt
  LEFT OUTER JOIN dbo.splunge AS s 
  ON s.Date = dt.dt
  GROUP BY Grouping SETS((s.Name),(s.Name,dt.dt))
)
SELECT 
  Name, 
  [S] = COALESCE(MAX(CASE dd WHEN 0 THEN c END), '-'),
  [M] = COALESCE(MAX(CASE dd WHEN 1 THEN c END), '-'),
  [T] = COALESCE(MAX(CASE dd WHEN 2 THEN c END), '-'),
  [W] = COALESCE(MAX(CASE dd WHEN 3 THEN c END), '-'),
  [T] = COALESCE(MAX(CASE dd WHEN 4 THEN c END), '-'),
  [F] = COALESCE(MAX(CASE dd WHEN 5 THEN c END), '-'),
  [S] = COALESCE(MAX(CASE dd WHEN 6 THEN c END), '-'),
  TOTAL = MAX(CASE WHEN dt IS NULL THEN c END)
FROM x
WHERE Name IS NOT NULL
GROUP BY Name;
Run Code Online (Sandbox Code Playgroud)

结果:

Name    S   M   T   W   T   F   S   TOTAL
------  --- --- --- --- --- --- --- -----
Ben     1   2   -   -   -   -   -   3
Tod     2   5   -   -   -   -   -   7
Run Code Online (Sandbox Code Playgroud)

其它的办法:

DECLARE @dt DATE = '20150702';

-- make sure Sunday is the "beginning" of the week:
SET DATEFIRST 7;

-- roll it back to Sunday
SET @dt = DATEADD(WEEK, DATEDIFF(WEEK, '19050101', @dt), '19050101');

;WITH dt AS
(
  SELECT Name, dw = DATEPART(WEEKDAY, Date)
  FROM dbo.splunge
  WHERE Date >= @dt AND Date < DATEADD(DAY, 7, @dt)
),
x AS
(
  SELECT Name, dw = COALESCE(dw, 8),  
    c = CONVERT(VARCHAR(11), COUNT(*))
  FROM dt 
  GROUP BY GROUPING SETS((Name),(Name,dw))
)
SELECT Name, 
  [S] = COALESCE([1],'-'),
  [M] = COALESCE([2],'-'),
  [T] = COALESCE([3],'-'),
  [W] = COALESCE([4],'-'),
  [T] = COALESCE([5],'-'),
  [F] = COALESCE([6],'-'),
  [S] = COALESCE([7],'-'),
  TOTAL = [8]
FROM x 
PIVOT (MAX(c) FOR dw IN ([1],[2],[3],[4],[5],[6],[7],[8])) AS pvt;
Run Code Online (Sandbox Code Playgroud)

不要忘记清理:

DROP TABLE dbo.splunge;
Run Code Online (Sandbox Code Playgroud)


Geo*_*son 5

您可以在 SQL Server 中使用以下方法将数据转换为您要查找的格式。您也可以使用 PIVOT 运算符,但我倾向于写出 CASE 语句,因为我发现这种语法更清晰(在我检查过的所有情况下,它们总是解析为相同的查询计划)。

根据 Aaron 指出 DATENAME 的缺点更新了答案。感谢您指出这一点,亚伦!我们主要使用基于英语的数据库,但这显然是一个更广泛的愚蠢假设。

-- Load test data
CREATE TABLE #reportDates (employeeName VARCHAR(100) NOT NULL, reportDate DATE NOT NULL)
GO
INSERT INTO #reportDates (employeeName, reportDate)
VALUES
    ('Tod', '2015-6-29'),
    ('Tod', '2015-6-29'),
    ('Tod', '2015-6-29'),
    ('Tod', '2015-6-29'),
    ('Tod', '2015-6-29'),
    ('Tod', '2015-6-28'),
    ('Tod', '2015-6-28'),
    ('Ben', '2015-6-29'),
    ('Ben', '2015-6-29'),
    ('Ben', '2015-6-28')

-- Build the report for each employee
DECLARE @reportStartDate DATE = '2015-6-28' /* Sunday, the start date of the report */
SELECT employeeName,
    COUNT(CASE WHEN DATEDIFF(dd, @reportStartDate, reportDate) = 0 THEN 1 END) AS S,
    COUNT(CASE WHEN DATEDIFF(dd, @reportStartDate, reportDate) = 1 THEN 1 END) AS M,
    COUNT(CASE WHEN DATEDIFF(dd, @reportStartDate, reportDate) = 2 THEN 1 END) AS T,
    COUNT(CASE WHEN DATEDIFF(dd, @reportStartDate, reportDate) = 3 THEN 1 END) AS W,
    COUNT(CASE WHEN DATEDIFF(dd, @reportStartDate, reportDate) = 4 THEN 1 END) AS R,
    COUNT(CASE WHEN DATEDIFF(dd, @reportStartDate, reportDate) = 5 THEN 1 END) AS F,
    COUNT(CASE WHEN DATEDIFF(dd, @reportStartDate, reportDate) = 6 THEN 1 END) AS S,
    COUNT(*) AS TOTAL
FROM #reportDates
WHERE reportDate BETWEEN @reportStartDate/*Sunday, report start date*/ AND DATEADD(dd, 6, @reportStartDate)/* Saturday, report end date */
GROUP BY employeeName
Run Code Online (Sandbox Code Playgroud)

作为参考,这是原始回复(如果不使用英语,则不正确!):

-- Load test data
CREATE TABLE #reportDates (employeeName VARCHAR(100) NOT NULL, reportDate DATE NOT NULL)
GO
INSERT INTO #reportDates (employeeName, reportDate)
VALUES
    ('Tod', '2015-6-29'),
    ('Tod', '2015-6-29'),
    ('Tod', '2015-6-29'),
    ('Tod', '2015-6-29'),
    ('Tod', '2015-6-29'),
    ('Tod', '2015-6-28'),
    ('Tod', '2015-6-28'),
    ('Ben', '2015-6-29'),
    ('Ben', '2015-6-29'),
    ('Ben', '2015-6-28')

-- Build the report for each employee
SELECT employeeName,
    COUNT(CASE WHEN DATENAME(dw, reportDate) = 'Sunday' THEN 1 END) AS S,
    COUNT(CASE WHEN DATENAME(dw, reportDate) = 'Monday' THEN 1 END) AS M,
    COUNT(CASE WHEN DATENAME(dw, reportDate) = 'Tuesday' THEN 1 END) AS T,
    COUNT(CASE WHEN DATENAME(dw, reportDate) = 'Wednesday' THEN 1 END) AS W,
    COUNT(CASE WHEN DATENAME(dw, reportDate) = 'Thursday' THEN 1 END) AS R,
    COUNT(CASE WHEN DATENAME(dw, reportDate) = 'Friday' THEN 1 END) AS F,
    COUNT(CASE WHEN DATENAME(dw, reportDate) = 'Saturday' THEN 1 END) AS S,
    COUNT(*) AS TOTAL
FROM #reportDates
WHERE reportDate BETWEEN '2015-6-28'/*Sunday, report start date*/ AND '2015-7-4'/* Saturday, report end date */
GROUP BY employeeName
Run Code Online (Sandbox Code Playgroud)

您也可以考虑使用该DATEPART函数,但请注意,它取决于DATEFIRST配置方式,因此可能不会在不同的数据库或配置更改时始终返回相同的结果:

-- Compare DATENAME vs. DATEPART for day of the week
SELECT DATENAME(dw, '2015-6-29')
-- Monday
GO
SELECT DATEPART(dw,'2015-6-29')
-- 2
GO

-- Reconfigure the "first" day of the week to be Thursday
SET DATEFIRST 4
GO

-- Compare DATENAME vs. DATEPART for day of the week after setting DATEFIRST
SELECT DATENAME(dw,'2015-6-29')
-- Monday, which remains consistent
GO
SELECT DATEPART(dw,'2015-6-29')
-- 5, which means that DATEPART is not guaranteed for this usage!
GO

-- Reconfigure the "first" day of the week to Sunday (the default for US English)
SET DATEFIRST 7
GO
Run Code Online (Sandbox Code Playgroud)