【问题标题】:T SQL - Count People with Visits in 3 consecutive monthsT SQL - 统计连续 3 个月访问的人数
【发布时间】:2018-10-05 14:36:59
【问题描述】:

有以下数据:

Declare @t Table 
(
Name        Varchar(1),
VisitDate   Date
)

Insert Into @t select 'A','2017-01-05'
Insert Into @t select 'A','2017-03-05'
Insert Into @t select 'A','2017-04-05'
Insert Into @t select 'A','2017-05-05'
Insert Into @t select 'A','2017-08-05'
Insert Into @t select 'B','2017-03-05'
Insert Into @t select 'C','2017-01-05'
Insert Into @t select 'C','2017-02-05'
Insert Into @t select 'C','2017-04-05'
Insert Into @t select 'D','2017-01-05'
Insert Into @t select 'D','2017-02-05'
Insert Into @t select 'D','2017-03-05'
Insert Into @t select 'D','2017-06-05'
Insert Into @t select 'B','2018-01-05'
Insert Into @t select 'B','2018-02-05'
Insert Into @t select 'B','2018-03-05'
Insert Into @t select 'E','2018-01-05'
Insert Into @t select 'E','2018-02-05'
Insert Into @t select 'E','2018-03-05'
Insert Into @t select 'E','2018-06-05'

我需要编写一个查询,该查询将返回在任何一年的任何连续三个月中具有 VisitDates 的年份和名称。

根据数据,我希望看到:

2017 A
2017 D
2018 B
2018 E

说实话,我不知道从哪里开始使用 SQL。

如果能得到任何帮助,我将不胜感激。

谢谢!!

【问题讨论】:

  • 12 月后计数器会重置吗?
  • @Salman:是的。确实如此。谢谢。
  • 序列1st Feb, 31st Mar, 30th Apr 算不算?您实际上只是在寻找连续的 3 个日历月,即使两个事件相隔两个月还有 1 天?
  • @MatBalie:是的。即使他们相隔两个月只有一天——即使他们相隔一天 [2 月 28 日,3 月 1 日]。只要他们在连续三个日历月内。

标签: sql-server tsql date datetime


【解决方案1】:

您可以使用gaps-and-islands 中使用的相同方法来避免连接或多次解析整个数据集。

http://rextester.com/SYHJ40676

WITH
  sequenced AS
(
  SELECT
    Name,
    YEAR(VisitDate)         AS VisitYear,
    MONTH(VisitDate)        AS VisitMonth,
    ROW_NUMBER()
      OVER (PARTITION BY Name, YEAR(VisitDate)
                ORDER BY MONTH(VisitDate)
           )
                            AS MonthSequenceID
  FROM
    @t
  GROUP BY
    Name,
    YEAR(VisitDate),
    MONTH(VisitDate)
)
SELECT DISTINCT
  Name,
  VisitYear
FROM
  sequenced
GROUP BY
  Name,
  VisitYear,
  VisitMonth - MonthSequenceID
HAVING
  COUNT(*) >= 3

【讨论】:

    【解决方案2】:

    只需将接下来的两个月加入数据,看看它的去向:

    SELECT DATEPART(year, m1.VisitDate), m1.Name
    FROM @t m1
      JOIN @t m2 on m2.Name = m1.Name AND DATEPART(month, m2.VisitDate) = DATEPART(month, m1.VisitDate) + 1
      JOIN @t m3 on m3.Name = m1.Name AND DATEPART(month, m3.VisitDate) = DATEPART(month, m1.VisitDate) + 2
    

    既然在评论中被问到,如何解决这个问题与一年重叠,这应该可以:

    SELECT DATEPART(year, m1.VisitDate), m1.Name
    FROM @t m1
      JOIN @t m2 on m2.Name = m1.Name AND EOMONTH(m1.VisitDate,1) = EOMONTH(m2.VisitDate)
      JOIN @t m3 on m3.Name = m1.Name AND EOMONTH(m1.VisitDate,2) = EOMONTH(m3.VisitDate)
    

    EOMONTH 文档:https://docs.microsoft.com/en-us/sql/t-sql/functions/eomonth-transact-sql?view=sql-server-2017

    编辑:我的回答只是一个快速破解并且性能非常低下,并且在每月有多个实例时会出现错误。 我建议使用这个答案:https://stackoverflow.com/a/52669713/4903754

    【讨论】:

    • 如果有 2017 年 12 月、2018 年 1 月、2018 年 2 月,那会怎样?
    • 最初的问题是:我需要编写一个查询,该查询将返回任何一年中任何连续三个月中具有 VisitDates 的年份和名称。因此无需检查明年的日期
    • @Isitar 是正确的。访问日期必须是连续 3 个月且在同一日历年。
    • @Isitar:我使用了你的第二个建议——但两者都有效。注:我摆弄了我的数据,发现如果我连续 4 个月有名字,那么 Year-Name 组合会出现两次 - 如果我连续 5 个月有名字,则会出现 3 次......因为我的测试数据没有显示这种可能性。谢谢!!
    • 当一个月内有多个事件时会发疯 (dbfiddle.uk/…),并且在连接谓词中使用函数会阻止使用索引。连接 + 不使用索引 + 大量重复结果 = 严重低效。
    【解决方案3】:

    按照 postgres SQL 9.5.0 的语法编写我的代码

    首先我连续几个月创建了标志,并使用该标志检索了所需的data.lag(),lead()

    我们需要比较它们是否连续的日期天气,因为我正在使用lag(),lead() 函数。

    with temp as (
    
    select name,visitdate, 
    
    coalesce(lag(visitdate) over (partition by name order by visitdate),lead(visitdate) over (partition by name order by visitdate))check1,
    
    coalesce(lead(visitdate) over (partition by name order by visitdate),lag(visitdate) over (partition by name order by visitdate)) check2
    
    from TT 
    
    order by 1
    ),
    
     t2 as (
    
    select name,
    
    case
    
    when
     (DATE_PART('year', visitdate::date) - DATE_PART('year', check1::date)) * 12 +
                  (DATE_PART('month', visitdate::date) - DATE_PART('month', check1::date))=1 
    
    or 
    
    (DATE_PART('year', check2::date) - DATE_PART('year', visitdate::date)) * 12 +
                  (DATE_PART('month', check2::date) - DATE_PART('month', visitdate::date))=1 
    
    then 1 else 0
    
     end as flag
    
     from temp)
    
    select name ,count(1) from t2 where flag=1  group by name having count(1)>=3
    

    【讨论】:

      猜你喜欢
      • 2012-12-20
      • 1970-01-01
      • 1970-01-01
      • 2013-03-04
      • 2015-01-06
      • 2011-02-16
      • 2018-02-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多