【问题标题】:How to Find First Valid Row in SQL Based on Difference of Column Values如何根据列值的差异查找 SQL 中的第一个有效行
【发布时间】:2017-07-29 18:39:07
【问题描述】:

我正在尝试找到一个可靠的查询,它返回可接受的插入范围的第一个实例。

研究:

目标查询函数:

  • InsertRange(1) = (StartRange(i) - EndRange(i-1)) > 新值

其中 InsertRange(1) 是查询应返回的值。换句话说,这将是满足上述条件的第一个实例。

表结构:

  • 主键:StartRange
  • StartRange(i-1)
  • StartRange(i-1) + EndRange(i-1)

示例数据集

下面是一个示例用户表(3 列),具有设定的范围分布。 StartRanges 总是严格按升序排列,UserID 是任意字符串,只有 StartRange 和 EndRange 的序列重要:

开始范围结束范围用户 ID 312 6896 用户0 7134 16268 用户1 16877 22451 用户2 23137 25142 用户3 25955 28272 用户4 28313 35172 用户5 35593 38007 用户6 38319 38495 用户7 38565 45200 用户8 46136 48007 用户9

我当前的查询

我目前正在尝试使用此查询:

SELECT t2.StartRange, t2.EndRange 
FROM user AS t1, user AS t2 
WHERE (t1.StartRange - t2.StartRange+1) > NewValue 
ORDER BY t1.EndRange 
LIMIT 1

示例案例

给定表格,如果 NewValue = 800,那么返回的答案应该是 23137。这意味着,第一个可用插槽将在 user3 和 user4 之间(实际插槽大小 = 813):

InsertRange(1) = (StartRange(i) - EndRange(i-1)) > NewValue
InsertRange    = (StartRange(6) - EndRange(5))   > NewValue
   23137       =    25955       -     25142      >   800

更多评论

  • 我上面的查询似乎适用于 StartRange 紧密排列的特殊情况(即 StartRange(i) = StartRange(i-1) + EndRange(i-1) + 1)。这不再适用于不太紧密的 StartRanges 集

【问题讨论】:

  • 你期望的结果应该是什么?

标签: mysql


【解决方案1】:

请记住,SQL 表没有隐式行顺序。不过,按 StartRange 值对表进行排序似乎是公平的。

我们可以通过编写一个查询来获取与它前面的行配对的每一行来解决这个问题。在 MySQL 中,很难完美地做到这一点,因为它缺少行编号功能。

这可行 (http://sqlfiddle.com/#!9/4437c0/7/0)。它的性能可能很差,因为它会生成 O(n^2) 中间行。 user0 没有行;它不能与任何前一行配对,因为没有。

   select MAX(a.StartRange) SA, MAX(a.EndRange) EA, 
          b.StartRange SB, b.EndRange EB , b.UserID
     from user a
     join user b ON a.EndRange <= b.StartRange
    group by b.StartRange, b.EndRange, b.UserID

然后,您可以将其用作子查询,并应用您的条件,即

  1. 差距>= 800
  2. 第一个匹配行(最低 StartRange 值)ORDER BY SB
  3. 只有一个LIMIT 1

这是查询 (http://sqlfiddle.com/#!9/4437c0/11/0)

SELECT SB-EA Gap, 
       EA+1 Beginning_of_gap, SB-1 Ending_of_gap, 
       UserId UserID_after_gap
  FROM (
       select MAX(a.StartRange) SA, MAX(a.EndRange) EA, 
              b.StartRange SB, b.EndRange EB , b.UserID
         from user a
         join user b ON a.EndRange <= b.StartRange
        group by b.StartRange, b.EndRange, b.UserID
        ) pairs
  WHERE SB-EA >= 800
  ORDER BY SB
  LIMIT 1

请注意,您实际上可能想要最小的匹配间隙而不是第一个匹配间隙。这称为最佳拟合,而不是第一次拟合。要做到这一点,请改用ORDER BY SB-EA

编辑:还有另一种使用 MySQL 连接相邻行的方法,它没有 O(n^2) 性能问题。它涉及使用用户变量来模拟row_number() 函数。所涉及的查询是一个毛球(这是一个技术术语)。在此问题的答案的第三个备选方案中对此进行了描述。 How do I pair rows together in MYSQL?

【讨论】:

  • @ O. Jones:谢谢,很好的回答,准点!由于操作按 O(n^2) 扩展,您认为我应该使用表架构吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-17
相关资源
最近更新 更多