Jma*_*ier 5 sql-server-2008 sql-server
我有一张做计件工作的人的桌子。表中的每个条目都有一个用户 ID、名称和他们输入的每个报告的日期。
我需要每周报告这些人从星期日开始每天做了什么
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)
这是一种方法。假设此表和示例数据:
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)
您可以在 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)
归档时间: |
|
查看次数: |
11371 次 |
最近记录: |