【问题标题】:SQL determine if a number of consecutive months have passedSQL 判断是否已经过了多个连续的月份
【发布时间】:2011-10-31 13:41:13
【问题描述】:

我有 3 列 1 表示代理 ID,一个表示他们的级别(初级、中级、高级),并表示他们达到该级别的月底日期。

如果样本数据看起来像这样

360123, Beginner, 1/22/2011
360123, Null, 2/22/2011
360123, Beginner, 3/22/2011
360123, Intermediate, 4/22/2011
360123, Beginner, 5/22/2011
360123, Beginner, 6/22/2011

如何设计一个查询来告诉我哪些代理从 3 月 22 日到 6 月 22 日连续 4 个月完成了所有初学者目标?

哇,谢谢大家的帮助!

在 6 个月期间的第 4 个月中,第一个月是您实现其中一个目标的任何一个月,怎么样?

【问题讨论】:

  • 在这种情况下,您正在寻找连续 4 个月,而您的 WHERE 子句仅涵盖 4 个月(3 月、4 月、5 月、6 月)。会一直这样吗?

标签: sql sql-server sql-server-2005 sql-server-2008


【解决方案1】:

示例数据和示例如下。基本上,按 ID 分组并搜索不同月份的计数。

DECLARE @T table (id int, lvl varchar(100), datefield smalldatetime)
INSERT INTO @T
VALUES
(360123, 'Beginner', '1/22/2011'),
(360123, Null, '2/22/2011'),
(360123, 'Beginner', '3/22/2011'),
(360123, 'Intermediate', '4/22/2011'),
(360123, 'Beginner', '5/22/2011'),
(360123, 'Beginner', '6/22/2011')

SELECT ID
FROM @T
WHERE Lvl = 'Beginner'
AND datefield BETWEEN '3/1/2011' AND '6/30/2011 23:59:59'
GROUP BY ID
HAVING COUNT(DISTINCT(MONTH(DateField))) = 4

【讨论】:

  • 似乎错过了连续要求(使用当前的WHERE子句)
  • @MartinSmith - 很好,我一半使用了他的数据,一半使用了他发布的逻辑。现在只使用逻辑!
  • 吹毛求疵:它可以工作 4 或 5 甚至 12 个月,但不能工作 13 或更多。
  • @ypercube - 非常有效的观点。这是这种方法的局限性。
【解决方案2】:
(Select id from table_name where date ='6/22/2011' and level ='Beginner')
intersect
(Select id from table_name where date ='5/22/2011' and level ='Beginner')
intersect

(Select id from table_name where date ='4/22/2011' and level ='Beginner')
intersect 

(Select id from table_name where date ='3/22/2011' and level ='Beginner')

【讨论】:

  • -1 - Intersects 不是有效关键字,您在每个查询中使用相同的日期(没有一个是有效日期),即使使用正确的语法,这也需要他键入每个手动日期,以及每个日期/月的子查询。
  • DV 已删除。尽管如此,这仍然是一种非常低效的方法。
【解决方案3】:
SELECT id
FROM TableX
WHERE level = 'Beginner'
  AND datefield >= '2011-03-22' 
  AND datefield < '2011-06-23'
GROUP BY id
HAVING COUNT(DISTINCT YEAR(datefield), MONTH(datefield)) = 4
   AND ( YEAR(MAX(datefield))*12+MONTH(MAX(datefield)) )
     - ( YEAR(MIN(datefield))*12+MONTH(MIN(datefield)) ) = 4 - 1

【讨论】:

    【解决方案4】:

    主要技巧是创建“人工” Id 以将其用作递归 CTE 中的锚点:

    声明@T 表 ( 编号 INT , Lvl VARCHAR (100), 日期字段 SMALLDATETIME);

    INSERT  INTO @T
    VALUES (360123, 'Beginner', '1/22/2011'),
    (360123, NULL, '2/22/2011'),
    (360123, 'Beginner', '3/22/2011'),
    (360123, 'Intermediate', '4/22/2011'),
    (360123, 'Beginner', '12/22/2011'),
    (360123, 'Beginner', '01/22/2012');
    
    DECLARE @BeginTime AS DATETIME = '1/22/2011';
    
    DECLARE @EndTime AS DATETIME = '6/22/2012';
    
    WITH   M
    AS     (SELECT DISTINCT Id,
                            MONTH(datefield) AS Mth,
                            YEAR(datefield) AS Yr,
                            ROW_NUMBER() OVER (PARTITION BY Id ORDER BY YEAR(datefield), MONTH(datefield)) AS RN
            FROM   @T AS T
            WHERE  Lvl = 'Beginner'
                   AND T.Datefield >= @BeginTime
                   AND T.Datefield <= @EndTime),
           C (Id, RN, MonthsInARow, Mth, Yr)
    AS     (SELECT M.Id,
                   RN,
                   CAST (1 AS INT),
                   Mth,
                   Yr
            FROM   M
            WHERE  RN = 1
            UNION ALL
            SELECT M.Id,
                   M.RN,
                   CASE 
                   WHEN M.Mth = C.Mth + 1
                             OR (M.Mth = 1
                                 AND C.Mth = 12
                                 AND M.Yr = C.yr + 1) THEN C.MonthsInARow + 1 ELSE 1 
                   END,
                   M.Mth,
                   M.Yr
            FROM   M
                   INNER JOIN
                   C
                   ON M.Id = C.Id
                      AND M.RN = C.RN + 1)
    SELECT [C].[Id],MAX([C].[MonthsInARow]) MAXMonth
    FROM   C
    GROUP BY [Id]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多