计算一年中有记录的天数

时间:2014-12-15 19:34:26

标签: sql-server

我有一个名为 AgentLog 的SQL Server表,我在其中为每个代理存储他的每日销售数量。

+-----------+------------+-------------+
| AgentName |    Date    | SalesNumber |
+-----------+------------+-------------+
| John      | 01.01.2014 |          45 |
| Terry     | 01.01.2014 |          30 |
| John      | 02.01.2014 |          20 |
| Terry     | 02.01.2014 |          15 |
| Terry     | 03.01.2014 |          52 |
| Terry     | 04.01.2014 |          24 |
| Terry     | 05.01.2014 |          12 |
| Terry     | 06.01.2014 |          10 |
| Terry     | 07.01.2014 |          23 |
| John      | 08.01.2014 |          48 |
| Terry     | 08.01.2014 |          35 |
| John      | 09.01.2014 |          37 |
| Terry     | 10.01.2014 |          35 |
+-----------+------------+-------------+

如果代理人在某一天没有工作,则该日期没有他的销售记录。

我想在给定的日期间隔(例如:01.01.2014 - 10.01.2014)生成报告(查询),该日期间隔计算代理人没有工作的天数(例如:John - 6天),工作(约翰 - 4天),并返回它不存在的日期间隔(例如:约翰03.01.2014 - 07.01.2014,10.01.2014)(可能有多个间隔)。< / p>

5 个答案:

答案 0 :(得分:0)

您需要创建一个自定义表格,并在其中填写您所需的每个日期的记录(您可以根据自己的需要随意回溯过去和未来。)您可以非常轻松地在Excel中执行此操作并导入它。

Select *
from Custom.DateListTable dlt
left outer join agentlog ag
on dlt.Date = ag.Date

答案 1 :(得分:0)

我会通过获取间隔中的日期数量以及代理人工作的日期数来实现此目的,然后您就拥有了所需的一切。

获取可以使用DATEDIFF的天数:

SELECT DATEDIFF(day, '2014-01-01', '2014-10-01') AS totalDays;

要获取代理工作的天数,您可以使用COUNT(*)聚合函数:

SELECT agentName, COUNT(*) AS daysWorked
FROM myTable
GROUP BY agentName;

然后,你可以添加到该查询以通过减去totalDays来获得没有工作的日子 - daysWorked:

SELECT agentName, COUNT(*) AS daysWorked, (DATEDIFF(day, '2014-01-01', '2014-10-01') - COUNT(*)) AS daysMissed
FROM myTable
GROUP BY agentName;

以下是SQL Fiddle示例。

答案 2 :(得分:0)

我能想到解决这个问题的唯一方法是创建一个只有一列(datetime)的临时表,并保存所选范围内的所有日期。您可以使用带有间隔中所有日期的游标创建一个填充该临时表的存储过程。然后在表和临时表之间进行LEFT连接,以在表中查找空值(该人没有去工作的日子)

答案 3 :(得分:0)

注意:以下答案主要解决问题中最棘手的部分,即如何获得“缺勤” 间隔

将这些值指定为 Interval Start - End 日期:

DECLARE @IntervalStart DATE = '2013-12-30'
DECLARE @IntervalEnd DATE = '2014-01-10'

以下查询为您提供“缺勤” 间隔

SELECT AgentName, 
       DATEADD(d, 1, t.[Date]) As OffWorkStart,  
       DATEADD(d, -1, t.NextDate) As OffWorkEnd
FROM (
   SELECT AgentName, [Date], LEAD([Date]) OVER (PARTITION BY AgentName ORDER BY [Date] ASC) As NextDate,
          DATEDIFF(DAY, [Date], LEAD([Date]) OVER (PARTITION BY AgentName ORDER BY [Date] ASC)) As NextMinusCurrent
   FROM #AgentLog) t
WHERE t.NextMinusCurrent > 1

-- Get marginal beginning interval (in case such an interval exists)
UNION ALL

SELECT AgentName, @IntervalStart AS OffWorkStart, DATEADD(DAY, -1, MIN([Date])) AS OffWorkEnd 
FROM #AgentLog
GROUP BY AgentName 
HAVING MIN([Date]) > @IntervalStart

-- Get marginal ending interval (in case such an interval exists)
UNION ALL

SELECT AgentName, DATEADD(DAY, 1, MAX([Date])) AS OffWorkStart, @IntervalEnd
FROM #AgentLog
GROUP BY AgentName 
HAVING MAX([Date]) < @IntervalEnd

ORDER By AgentName, OffWorkStart

使用您提供的输入数据,上述查询将为您提供以下输出:

AgentName   OffWorkStart    OffWorkEnd
---------------------------------------
John        2013-12-30      2013-12-31
John        2014-01-03      2014-01-07
John        2014-01-10      2014-01-10
Terry       2013-12-30      2013-12-31
Terry       2014-01-09      2014-01-09

查询基本部分背后的想法是使用以下嵌套查询:

SELECT AgentName, 
       [Date], 
       LEAD([Date]) OVER (PARTITION BY AgentName ORDER BY [Date] ASC) As NextDate,
       DATEDIFF(DAY, [Date], LEAD([Date]) OVER (PARTITION BY AgentName ORDER BY [Date] ASC)) As NextMinusCurrent
FROM #AgentLog

为了在某个代理人工作的日子之间找到任何现有的差距。值NextMinusCurrent > 1表示存在这样的差距。

如果您有上述查询,计算天数是微不足道的。例如。将上述查询放在CTE中,您可以计算总缺席天数,例如:

;WITH cte (

... query goes here

)
SELECT AgentName, SUM(DATEDIFF(DAY, OffWorkStart, OffWorkEnd) + 1) AS AbsenceDays
FROM cte
GROUP By AgentName

P.S。以上查询使用SQL Server LEAD函数,该函数可从SQL SERVER 2012开始提供。

SQL Fiddle here

修改

CTEsROW_NUMBER()一起可用于模拟LEAD功能。查询的第一部分变为:

;WITH cte1 AS (
   SELECT AgentName, 
          [Date], 
          ROW_NUMBER() OVER (PARTITION BY AgentName ORDER BY [Date] ASC) As rn
   FROM #AgentLog
),
cte2 AS (
   SELECT cte1.AgentName, cte1.[Date], 
          cteLead.[Date] AS NextDate,
          DATEDIFF(DAY, cte1.[Date], cteLead.[Date])  As NextMinusCurrent
   FROM cte1 
   LEFT OUTER JOIN cte1 AS cteLead 
      ON (cte1.rn = cteLead.rn - 1) AND (cte1.AgentName = cteLead.AgentName)
)
SELECT AgentName, 
       DATEADD(d, 1, cte2.[Date]) As OffWorkStart,  
       DATEADD(d, -1, cte2.NextDate) As OffWorkEnd
FROM cte2
WHERE NextMinusCurrent > 1

SQL Fiddle for SQL Server 2008 here。我希望它也在SQL Server 2005中执行!

答案 4 :(得分:0)

试试这个......

SET DATEFIRST 1; --Monday

DECLARE @StartDate  DATETIME = '2014-01.01',
        @EndDate    DATETIME = '2014-01.10';

WITH data as (
    select 0 as i, DATEADD(DAY, 0, @StartDate) as TheDate
    union all
    select i + 1, DATEADD(DAY, i + 1, @StartDate) as TheDate
    from data
    where i < (@EndDate - @StartDate)
) 

SELECT a.AgentName, 
SUM(CASE WHEN c.Date IS NULL THEN 1 ELSE 0 END) AS  Missing, 
SUM(CASE WHEN c.Date IS NOT NULL THEN 1 ELSE 0 END) AS  Working
FROM Agent a
JOIN data b ON NOT EXISTS(SELECT NULL FROM SpecialDate s WHERE s.date = b.TheDate)
LEFT JOIN AgentLog c ON
    c.AgentName = a.AgentName
AND c.Date = b.TheDate
WHERE DATEPART(weekday, b.TheDate) <= 5
GROUP BY a.AgentName
OPTION (MAXRECURSION 10000);

它包括周末检查,以及对#34; SpecialDate&#34;的引用。可以保留非工作日列表,并从支票中排除。

再次阅读你的问题,我意识到这只会解决你问题的一半。