如何使用SQL在表中查找N个连续记录

时间:2010-04-27 23:17:42

标签: sql-server-2005 sql

我有以下表定义和样本数据。在下表中,Customer Product&日期是关键字段

Table One
Customer   Product    Date         SALE
   X          A       01/01/2010    YES
   X          A       02/01/2010    YES
   X          A       03/01/2010    NO
   X          A       04/01/2010    NO
   X          A       05/01/2010    YES
   X          A       06/01/2010    NO
   X          A       07/01/2010    NO
   X          A       08/01/2010    NO
   X          A       09/01/2010    YES
   X          A       10/01/2010    YES
   X          A       11/01/2010    NO
   X          A       12/01/2010    YES

在上表中,我需要找到N或>没有销售的N个连续记录,销售价值为'否' 例如,如果N为2,则结果集将返回以下

     Customer   Product    Date         SALE
       X          A       03/01/2010    NO
       X          A       04/01/2010    NO
       X          A       06/01/2010    NO
       X          A       07/01/2010    NO
       X          A       08/01/2010    NO

有人可以帮我处理SQL查询以获得所需的结果。我正在使用SQL Server 2005.我开始使用ROW_NUMBER()AND PARTITION子句但没有运气。 谢谢你的帮助

4 个答案:

答案 0 :(得分:4)

您需要将表格与自身匹配,就好像有2个表格一样。所以你使用两个别名o1和o2来引用你的表:

SELECT DISTINCT o1.customer, o1.product, o1.datum, o1.sale
  FROM one o1, one o2
  WHERE (o1.datum = o2.datum-1 OR o1.datum = o2.datum +1)
  AND o1.sale = 'NO' 
  AND o2.sale = 'NO'; 
 customer | product |   datum    | sale 
----------+---------+------------+------
 X        | A       | 2010-01-03 | NO
 X        | A       | 2010-01-04 | NO
 X        | A       | 2010-01-06 | NO
 X        | A       | 2010-01-07 | NO
 X        | A       | 2010-01-08 | NO

请注意,我在postgresql数据库上执行了查询 - 也许ms-sql-server上的语法不同,也许在别名'FROM AS AS o1'上,也许你不能以这种方式添加/减少。

答案 1 :(得分:1)

一种不同的方法,受到最后一行的启发。

获取 - 给定日期的第一个日期晚于YES,最后一个日期早于YES。这些形成了我们的日期适合的边界。

SELECT (o1.datum),
    MAX (o3.datum) - MIN (o2.datum) AS diff
FROM one o1, one o2, one o3 
WHERE o1.sale = 'NO'
AND o3.datum <
    (SELECT MIN (datum) 
    FROM one 
    WHERE datum >= o1.datum 
    AND SALE = 'YES') 
AND o2.datum > 
    (SELECT MAX (datum) 
    FROM one 
    WHERE datum <= o1.datum 
    AND SALE = 'YES') 
GROUP BY o1.datum 
HAVING MAX (o3.datum) - MIN (o2.datum) >= 2
ORDER BY o1.datum;

也许它需要某种优化,因为第一表是查询中涉及的5倍。 :)

答案 2 :(得分:0)

好的,我们需要一个可变的答案。我们搜索一个日期,我们有N个以下日期,所有销售区域都是NO。

SELECT d1.datum
FROM one d1, one d2, i 
WHERE d1.sale = 'NO' AND d2.sale = 'NO'
  AND d1.datum = (d2.datum - i) 
  AND i > 0 AND i < 4 
GROUP BY d1.datum 
HAVING COUNT (*) = 3; 

这将为我们提供日期,我们将其用于子查询。

注意:

  • 我使用'datum'而不是date,因为date是postgresql上的保留关键字。

  • 在Oracle中,您可以使用虚拟表格虚拟,其中包含您要求的任何内容,例如“(1,2,3)中的SELCT foo FROM dual WHERE foo”;如果我没记错的话会给你1,2,3。根据供应商的不同,可能还有其他技巧可以获得序列1到N.我创建了一个带有列i的表i,并用值1到100填充它,我希望N不超过100;由于有几个版本,postgresql包含一个函数'generate_series(from,to),它也可以解决问题,并且可能与特定数据库的解决方案有相似之处。但是我应该独立于供应商。

  • 如果N == 17,则必须修改3到3的3个位置。

最终查询将是:

SELECT o4.* 
FROM one o3, one o4 
WHERE o3.datum = (
    SELECT d1.datum
    FROM one d1, one d2, i 
    WHERE d1.sale = 'NO' AND d2.sale = 'NO'
      AND d1.datum = (d2.datum - i) 
      AND i > 0 AND i <= 3 
    GROUP BY d1.datum 
    HAVING COUNT (*) = 3) 
AND o4.datum <= o3.datum + 3 
AND o4.datum >= o3.datum; 
 customer | product |   datum    | sale 
----------+---------+------------+------
 X        | A       | 2010-02-06 | NO
 X        | A       | 2010-02-07 | NO
 X        | A       | 2010-02-08 | NO
 X        | A       | 2010-02-09 | NO

答案 3 :(得分:0)

感谢大家发布您的解决方案。想了想,我也会和大家分享我的解决方案。就像一个FYI,我从另一个SQL Server Central论坛成员那里收到了这个解决方案。我绝对不会赞成这个解决方案。

DECLARE @CNT INT
SELECT @CNT = 3

SELECT * FROM
(
  SELECT
    [Customer], [Product], [Date], [Sale], groupID, 
    COUNT(*) OVER (PARTITION BY [Customer], [Product], [Sale], groupID) AS groupCnt
  FROM
  (
    SELECT
      [Customer], [Product], [Date], [Sale],
      ROW_NUMBER() OVER (PARTITION BY [Customer], [Product] ORDER BY [Date])
      - ROW_NUMBER() OVER (PARTITION BY [Customer], [Product], [Sale] ORDER BY [Date]) AS groupID
    FROM
      [TableSales]
  ) T1
) T2
WHERE
  T2.[Sale] = 'NO' AND T2.[groupCnt] >= @CNT