【问题标题】:How to get number of days of specific type per week如何获取每周特定类型的天数
【发布时间】:2016-04-21 07:55:19
【问题描述】:

如果我有这样的表:

emp_num trans_date  day_type
5667    2016-03-01  1
5667    2016-03-02  1
5667    2016-03-03  1
5667    2016-03-04  3
5667    2016-03-05  3
5667    2016-03-06  1
5667    2016-03-07  1
5667    2016-03-08  1
5667    2016-03-09  1
5667    2016-03-10  1
5667    2016-03-11  3
5667    2016-03-12  3
5667    2016-03-13  1
5667    2016-03-14  1
5667    2016-03-15  1
5667    2016-03-16  1
5667    2016-03-17  1
5667    2016-03-18  3
5667    2016-03-19  3
5667    2016-03-20  1
5667    2016-03-21  1
5667    2016-03-22  1
5667    2016-03-23  1
5667    2016-03-24  1
5667    2016-03-25  3
5667    2016-03-26  3
5667    2016-03-27  1
5667    2016-03-28  1
5667    2016-03-29  1
5667    2016-03-30  1
5667    2016-03-31  1

鉴于每个员工都必须在trans_date 中拥有所有月日。

如何获取所有超过两个day_type =3的员工在一年中的某一个月至少一周内

【问题讨论】:

  • 这真的很广泛,当一周在两个不同的月份内时会发生什么。你想要什么样的一周-iso 周?同一个 ISO 周可以跨越 2 个不同的年份
  • @t-clausen.dk 如果在两个不同的月份中的一周是相同的规则,请检查特定员工的一周中包含两个以上(day_type =3)的部分
  • 再次,你如何定义一周?美国周、ISO 周还是其他?
  • @t-clausen.dk :对不起,我不知道这些类型之间的确切区别,但我们使用标准。
  • 一周是从周一还是周日开始,一年中的第一周可以有上一年的日子吗?

标签: sql sql-server date sql-server-2012


【解决方案1】:

鉴于您提供的数据:

declare  @table1 table (emp_num int, trans_date datetime, day_type int)
insert into @table1
VALUES (5667,'2016-03-01',1),(5667,'2016-03-02',1),(5667,'2016-03-03',1),
(5667,'2016-03-04',3),(5667,'2016-03-05',3),(5667,'2016-03-06',1),
(5667,'2016-03-07',1),(5667,'2016-03-08',1),(5667,'2016-03-09',1),
(5667,'2016-03-10',1),(5667,'2016-03-11',3),(5667,'2016-03-12',3),
(5667,'2016-03-13',1),(5667,'2016-03-14',1),(5667,'2016-03-15',1),
(5667,'2016-03-16',1),(5667,'2016-03-17',1),(5667,'2016-03-18',3),
(5667,'2016-03-19',3),(5667,'2016-03-20',1),(5667,'2016-03-21',1),
(5667,'2016-03-22',1),(5667,'2016-03-23',1),(5667,'2016-03-24',1),
(5667,'2016-03-25',3),(5667,'2016-03-26',3),(5667,'2016-03-27',1),
(5667,'2016-03-28',1),(5667,'2016-03-29',1),(5667,'2016-03-30',1),
(5667,'2016-03-31',1),(4275,'2016-03-01',3),(4275,'2016-03-02',1),
(4275,'2016-03-03',1 ),(4275,'2016-03-04',3 ),(4275,'2016-03-05',1 ),
(4275,'2016-03-06',1 ),(4275,'2016-03-07',3 ),(4275,'2016-03-08',3 ),
(4275,'2016-03-09',1 ),(4275,'2016-03-10',1 ),(4275,'2016-03-11',3 ),
(4275,'2016-03-12',1 ),(4275,'2016-03-13',1 ),(4275,'2016-03-14',3 ),
(4275,'2016-03-15',3 ),(4275,'2016-03-16',1 ),(4275,'2016-03-17',1 ),
(4275,'2016-03-18',3 ),(4275,'2016-03-19',1 ),(4275,'2016-03-20',1 ),
(4275,'2016-03-21',3 ),(4275,'2016-03-22',3 ),(4275,'2016-03-23',1 ),
(4275,'2016-03-24',1),(4275,'2016-03-25',3 ),(4275,'2016-03-26',1 ),
(4275,'2016-03-27',1 ),(4275,'2016-03-28',3 ),(4275,'2016-03-29',3 ),
(4275,'2016-03-30',1 ),(4275,'2016-03-31',1)

这将为您提供所需的内容(emp_num 5667 未返回,emp_num 4275 已返回),但请记住,有些月份会有几周跨越两个月,因此您可能需要根据您的要求对其进行调整因为这更微妙:

 declare @year int = 2016,
 @month int = 3

 ;with emp_cte (emp_num, weeknum, day_type_count)
 as
 (
     select emp_num, 
         datepart(week, trans_date), 
         sum(case when day_type = 3 then 1 else 0 end)
     from @table1 t
     where year(trans_date) = @year
     and month(trans_date) = @month
     group by emp_num, datepart(week, trans_date)
 )

 select emp_num
 from emp_cte
 group by emp_num
 having min(day_type_count) >= 2

【讨论】:

  • 重新阅读您最初的帖子后,我是否正确地说,您只希望在给定月份每周都有超过 2 个 day_type=3 条目的员工返回 - 而不是在给定的某个特定时间里只有一次月(这是我当前的查询给你的)?
  • 没错,这就是我的意思
  • 我已经相应地修改了我的答案@Anyname Donotcare。
  • 太慢了,永远跑!!
  • 是的,结果是正确的,但是NOT EXISTSEXCEPT 在检查时确实导致了很多表扫描!重新设计了应该执行得更好的查询 - 并给出了相同的结果。
【解决方案2】:
DECLARE @month int = 3,
        @year int = 2016

;WITH cte AS (
SELECT *
FROM (VALUES
                        (5667, '2016-03-01', 1),(5667, '2016-03-02', 1),(5667, '2016-03-03', 1),(5667, '2016-03-04', 3),(5667, '2016-03-05', 3),(5667, '2016-03-06', 1), --2
(5667, '2016-03-07', 1),(5667, '2016-03-08', 1),(5667, '2016-03-09', 1),(5667, '2016-03-10', 1),(5667, '2016-03-11', 3),(5667, '2016-03-12', 3),(5667, '2016-03-13', 1), --2
(5667, '2016-03-14', 1),(5667, '2016-03-15', 1),(5667, '2016-03-16', 1),(5667, '2016-03-17', 1),(5667, '2016-03-18', 3),(5667, '2016-03-19', 3),(5667, '2016-03-20', 1), --2
(5667, '2016-03-21', 1),(5667, '2016-03-22', 1),(5667, '2016-03-23', 1),(5667, '2016-03-24', 1),(5667, '2016-03-25', 3),(5667, '2016-03-26', 3),(5667, '2016-03-27', 1), --2
(5667, '2016-03-28', 1),(5667, '2016-03-29', 1),(5667, '2016-03-30', 1),(5667, '2016-03-31', 1),                                                                         --0

                        (4275, '2016-03-01', 3),(4275, '2016-03-02', 1),(4275, '2016-03-03', 1),(4275, '2016-03-04', 3),(4275, '2016-03-05', 1),(4275, '2016-03-06', 3), --3
(4275, '2016-03-07', 3),(4275, '2016-03-08', 3),(4275, '2016-03-09', 1),(4275, '2016-03-10', 1),(4275, '2016-03-11', 3),(4275, '2016-03-12', 1),(4275, '2016-03-13', 1), --3
(4275, '2016-03-14', 3),(4275, '2016-03-15', 3),(4275, '2016-03-16', 1),(4275, '2016-03-17', 1),(4275, '2016-03-18', 3),(4275, '2016-03-19', 1),(4275, '2016-03-20', 1), --3
(4275, '2016-03-21', 3),(4275, '2016-03-22', 3),(4275, '2016-03-23', 1),(4275, '2016-03-24', 1),(4275, '2016-03-25', 3),(4275, '2016-03-26', 1),(4275, '2016-03-27', 1), --3
(4275, '2016-03-28', 3),(4275, '2016-03-29', 3),(4275, '2016-03-30', 1),(4275, '2016-03-31', 1),                                                                         --2

                        (9922, '2016-03-01', 1),(9922, '2016-03-02', 1),(9922, '2016-03-03', 1),(9922, '2016-03-04', 3),(9922, '2016-03-05', 3),(9922, '2016-03-06', 1), --2
(9922, '2016-03-07', 1),(9922, '2016-03-08', 1),(9922, '2016-03-09', 1),(9922, '2016-03-10', 1),(9922, '2016-03-11', 3),(9922, '2016-03-12', 3),(9922, '2016-03-13', 1), --2
(9922, '2016-03-14', 1),(9922, '2016-03-15', 1),(9922, '2016-03-16', 1),(9922, '2016-03-17', 1),(9922, '2016-03-18', 3),(9922, '2016-03-19', 3),(9922, '2016-03-20', 1), --2
(9922, '2016-03-21', 3),(9922, '2016-03-22', 3),(9922, '2016-03-23', 1),(9922, '2016-03-24', 1),(9922, '2016-03-25', 1),(9922, '2016-03-26', 1),(9922, '2016-03-27', 1), --2
(9922, '2016-03-28', 3),(9922, '2016-03-29', 1),(9922, '2016-03-30', 3),(9922, '2016-03-31', 1)                                                                          --2
) AS t (emp_num, trans_date, day_type)
)
,final AS (
SELECT  DATEPART(week,c.trans_date) as week_num,
        emp_num,
        COUNT(c.trans_date) as coun
FROM cte c 
WHERE day_type = 3 
    AND DATEPART(month,trans_date) = @month
    AND DATEPART(YEAR,trans_date) = @year
GROUP BY emp_num,
        DATEPART(week,c.trans_date)
HAVING COUNT(c.trans_date) > 2
)

SELECT f.emp_num
FROM final f
GROUP BY emp_num

输出:

emp_num
-----------
4275

(1 row(s) affected)

【讨论】:

  • 这是我开始的。但是,要求是仅返回当月每周的 day_type = 3 行数超过或等于 2 行的员工。这只会获取一个月内一周内拥有超过或等于 2 个day_type = 3 行的员工。只有员工 4275 应该返回而不是 5667。
  • 谢谢,但结果错误,例如:我得到了9922 9922 2016-03-01 1 9922 2016-03-02 1 9922 2016-03-03 1 9922 2016-03-04 3 9922 2016-03-05 3 9922 2016-03-06 1 9922 2016-03-07 1 9922 2016-03-08 1 9922 2016-03-09 1 9922 2016-03-10 1 9922 2016-03-11 3 9922 2016-03-12 3 9922 2016-03-13 1 9922 2016-03-14 1 9922 2016-03-15 1 9922 2016-03-16 1 9922 2016-03-17 1 9922 2016-03-18 3 9922 2016-03-19 3 9922 2016-03-20 1 9922 2016-03-21 3 9922 2016-03-22 3 9922 2016-03-23 1 9922 2016-03-24 1 9922 2016-03-25 1 9922 2016-03-26 1 9922 2016-03-27 1 9922 2016-03-28 3 9922 2016-03-29 1 9922 2016-03-30 3 9922 2016-03-31 1
  • 该员工不应该出现在结果中,因为它整个月每周只有 2 个 day_type = 3
  • @AnynameDonotcare 超过 2 个?然后将HAVING COUNT(c.trans_date) >= 2 更改为HAVING COUNT(c.trans_date) > 2
  • 当我进行此更改时,我根本没有得到任何结果,尽管例如 4275 应该在那里
【解决方案3】:

类似于以下内容。 DATEADD(month, DATEDIFF(month, 0, trans_date), 0) 将日期转换为“月初”,然后EOMONTH 将执行“月底”,有效地限制了您的“给定月份”(@GIVEN_MONTH_DATE)。

DECLARE @GIVEN_MONTH_DATE DATE = '2016-04-01'
DECLARE @WEEKS_IN_MONTH = DATEDIFF(week, DATEADD(month, DATEDIFF(month, 0, @GIVEN_MONTH_DATE), 0), EOMONTH(@GIVEN_MONTH_DATE))

WITH results AS (
    SELECT
        emp_num AS EmployeeNumber,
        DATEPART(week, trans_date) AS Week,
        day_type AS DayType,
        COUNT(emp_num) AS Total
    FROM
        table
    WHERE
        day_type = 3
        AND trans_date BETWEEN DATEADD(month, DATEDIFF(month, 0, @GIVEN_MONTH_DATE), 0) AND EOMONTH(@GIVEN_MONTH_DATE)
    GROUP BY
        emp_num,
        DATEPART(week, trans_date),
        day_type
    HAVING
        COUNT(emp_num) > 2
)

SELECT
    EmployeeNumber,
    SUM(Total) AS Transactions
FROM
    results
GROUP BY
    EmployeeNumber
HAVING
    COUNT(EmployeeNumber) = @WEEKS_IN_MONTH

【讨论】:

  • 这将为您提供每个员工每周拥有超过 2 个day_type = 3 实例。它不会只返回每月每周拥有超过 2 个day_type = 3 实例的员工,这是要求。因此,员工 5667 和 4275 都将被退回 - 不正确。
  • 现在应该可以满足他的要求了。考虑到我实际上并没有运行它,这是伪代码式的,但它应该足够接近让他开始
  • 这是一个灰色区域,需要计算哪些周,但 DATEDIFF 只计算跨越的那些周边界。因此,在这种情况下它返回 4 周。然而,数据集包含 5 个不同的周,第一个和最后一个有 5 天。我认为可能需要对确切要求进行一些澄清,但在这种情况下,对(现已删除的)答案的评论提到应该返回员工 4275 而不是 5667(请参阅我的数据以获取这些记录)。您的查询目前正好相反。
猜你喜欢
  • 2013-02-09
  • 1970-01-01
  • 2019-04-24
  • 1970-01-01
  • 2022-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-09
相关资源
最近更新 更多