【问题标题】:How to find N Consecutive records in a table using SQL如何使用 SQL 查找表中的 N 个连续记录
【发布时间】:2010-04-27 23:17:41
【问题描述】:

我有以下带有示例数据的表定义。在下表中,客户产品和日期是关键字段

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个连续没有销售的记录,销售值为'NO' 例如,如果 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 子句,但没有运气。 感谢您的帮助

【问题讨论】:

  • 您是否有权更改表架构?
  • 嗨,我可以访问表架构。
  • 示例中的日期似乎是本月的 1 日。这是数据的实际排列方式,还是只是样本数据?如果不是,那么“连续”在这种情况下意味着什么。
  • 是的,日期始终是当月的第一个日期。

标签: sql-server-2005 sql


【解决方案1】:

您需要将您的表与自身进行匹配,就好像那里有 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 one AS o1' 上,也许你不能以这种方式添加/减去。

【讨论】:

  • 嗨 Stefan,您的建议非常有效。 N个连续的情况如何处理?
  • 恐怕我现在就得到了你问题的 N 部分。嗯。棘手。您想指定 N=16 或 N=375 并获得至少 16 或 375 个日期的模拟结果?我们所说的N是什么?我需要更多空间并添加一个额外的答案。
  • 嗨 Stefan,N 是可变的。此 N 将由用户在查询运行时指定。我能够为此提出一个解决方案,我将很快发布它。感谢你的帮助。我很感激。贾维德
  • 为什么不使用 INNER JOIN 而不是 CROSS JOIN,它可能会更快...?
  • @VojtěchDohnal:可能是,也可能不是。如果您喜欢它或者您可以证明它更快并且性能很重要,请使用内部连接。我从未见过性能损失,但我没有经常测试(oracle、postgresql、informix)。
【解决方案2】:

一种不同的方法,灵感来自 munchs 最后一行。

Get - 对于给定日期,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 次。 :)

【讨论】:

  • 你只是不喜欢它,还是你有证据反对它?
  • A.这很令人困惑,B. 你无法分辨哪些是连接条件,哪些是过滤条件,C. 它很容易出错并且你有一个交叉连接,D. 它不允许左连接。 E. 不一致,您使用隐式语法进行内连接,但必须使用显式语法进行外连接 F. 它不遵循最佳实践 G. 它使查询优化器更难生成高效代码(尤其是在 SQL 服务器上)。
  • a) 让您感到困惑?传闻。 b) 当然,加入标准也是一个标准。这就是联接的性质。 c) 无可争辩:如果我犯了错误,我就有一个错误 - 与 a) 相同的论点用不同的词。 d) 我们需要左连接吗? e) 我不一致的外连接在哪里? f) 哪种最佳做法?轶事。 g) 也许它已经比 QO 使用显式连接更快了?你量过吗?为什么不使用显式连接发布您的解决方案?我的查询有效;您没有发现错误,但将其标记为无用。
【解决方案3】:

感谢大家发布您的解决方案。想了想,我也会和大家分享我的解决方案。仅供参考,我从另一个 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

【讨论】:

  • 这个查询有可能非常非常慢(如果数据库无法以任何方式优化它,N^3 次查询)
【解决方案4】:

好的,我们需要一个可变的答案。我们搜索一个日期,其中我们有 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 中,您可以使用虚拟表 dummy,其中包含您要求的任何内容,例如 'SELCT foo FROM dual WHERE foo in (1, 2, 3);'如果我没记错的话,这会给你1、2、3。根据供应商的不同,可能还有其他技巧来获取序列 1 到 N。我创建了一个包含列 i 的表 i,并用值 1 到 100 填充它,我希望 N 不超过 100;由于有几个版本,postgresql 包含一个函数 'generate_series (from, to),它也可以解决问题,并且可能与特定数据库的解决方案有相似之处。但是表我应该独立于供应商工作。

  • 如果 N == 17,则必须将 3 个位置从 3 修改为 17。

最终查询将是:

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

【讨论】:

  • 哦,我提到了,这只会返回 N=3 的完全匹配,而不是 N>=3。
猜你喜欢
  • 2019-02-16
  • 2017-12-03
  • 1970-01-01
  • 2021-02-20
  • 1970-01-01
  • 1970-01-01
  • 2015-10-23
  • 1970-01-01
  • 2015-12-06
相关资源
最近更新 更多