【问题标题】:Selecting multiple rows, where a difference in value is greater than x%选择多行,其中值的差异大于 x%
【发布时间】:2020-10-10 19:26:11
【问题描述】:

我面临以下问题... 鉴于此数据: 表:投票

=========
  value
=========
   10
   25
   38
   90
   92
   93
   98
   100
   120

如果下一个和之前接受的值之间的差异大于第一个值的 10%,我只想返回该值:

if abs(int(a)-int(b))*100/int(a) < 10:
  return True

所以最终列表应该是(我在方括号中添加了 % 差异):

==========
  result
==========
   10 ()
   25 (150%)
   38 (52%)
   90 (136%)
   100 (11%)
   120 (20%)

查询还应该首先对这些值进行排序。

我可以使用代码(如上所示)来完成,但没有任何机会接近直接查询。

MySQL v.8.0.19

【问题讨论】:

  • 在 MySQL 8.x 中很容易。您使用的是 MySQL 5.x 还是 8.x?
  • 在提供示例时,包含不符合条件的行有时会更有帮助。
  • 8.x,我已经在原问题中添加了。

标签: mysql sql select window-functions recursive-query


【解决方案1】:

在 MySQL 8.0 中,您可以使用 lag() 执行此操作。假设您想按value 对行进行排序,则为:

select value
from (
    select
        value,
        lag(value, 1, 0) over(order by value) lag_value
    from mytable t
) t
where value > lag_value * 1.10

如果您想使用不同的排序列,则可以更改order by 子句以使用相关列。

在早期版本中,一个选项是相关子查询:

select value
from mytable t
where value > 1.10 * coalesce(
    (
        select t1.value 
        from mytable t1 
        where t1.value < t.value
        order by t1.value desc
        limit 1
    ),
    0
)

要在此处使用另一个排序列,您需要更改子查询的where 子句和order by 子句。


另一方面,如果您想根据与先前选择的行的比率选择下一行,那么这是一个不同的问题。您需要某种迭代过程:在 SQL 中,一种方法是递归查询:

with 
    data as (
        select value, row_number() over(order by value) rn
        from mytable t
    ) d,
    cte as (
        select 1 is_valid, value, rn from data where rn = 1
        union all 
        select 
            (d.value > 1.1 * c.value),
            case when d.value > 1.1 * c.value then d.value else c.value end,
            d.rn
        from cte c
        inner join data d on d.rn = c.rn + 1
    )
    select value from cte where is_valid order by value

查询枚举值,然后按顺序遍历数据集,同时跟踪最后选择的值,并为应该出现在最终结果集中的记录设置标志。

【讨论】:

  • 现在这几乎是我所需要的,但如果我们的序列如下:100,101,102,103,104,105,106,107,108,109,110,111,它将不起作用。它应该返回 100 和 111(因为差异超过 10%),但如果我正确理解查询 - 它会将每个值与以前的值进行比较,永远不会检测到更改发生的位置。
  • @MarcinBobowski:好的,这是一个不同的问题......它也更有趣。我根据您的要求更新了我的答案。
【解决方案2】:

您没有提及您使用的是什么版本的 MySQL,所以我假设它是现代版本 (8.x)。您可以使用LAG()。例如:

select
  concat('', value,
    case when prev_value is null then '' 
       else concat('', 100 * (value - prev_value) / prev_value, '%')
    end
  ) as result
from (
  select
    value,
    lag(value) over (order by value) as prev_value
  from t
) x
where prev_value is null or value > prev_value * 1.1
order by value

【讨论】:

    猜你喜欢
    • 2013-01-23
    • 1970-01-01
    • 2019-04-07
    • 1970-01-01
    • 2020-10-15
    • 1970-01-01
    • 2022-08-04
    • 2023-03-26
    • 2012-10-09
    相关资源
    最近更新 更多