【问题标题】:Select closest maximal numeric value in Firebird在 Firebird 中选择最接近的最大数值
【发布时间】:2020-02-07 20:22:35
【问题描述】:

假设有 2 个表,我们称它们为“Master”和“Detail”:

Master
--------------------------------
| ID | field_1 | ... | field_n |
--------------------------------
Detail
--------------------------------------------
| ID | master_id | f_value | ... | field_n |
--------------------------------------------
| 1  |      1    |   0.03  | ... |   ...   |
--------------------------------------------
| 2  |      1    |   0.95  | ... |   ...   |
--------------------------------------------
| 3  |      1    |   1.22  | ... |   ...   |
--------------------------------------------
| 4  |      2    |   0.91  | ... |   ...   |
--------------------------------------------
| 5  |      2    |   0.93  | ... |   ...   |
--------------------------------------------
| 6  |      2    |   2.07  | ... |   ...   |
--------------------------------------------

有 2 个输入参数:Master ID 列表 (master_id_list) 和数值 (num_value)。

对于master_id_list 中的每个ID,我应该得到一个详细记录:

  1. 如果是num_value < MIN( f_value ),应该是MIN( f_value )的记录
  2. 如果是num_value > MAX( f_value ),应该是MAX( f_value )的记录
  3. 否则应该是最接近最大值的记录f_value

示例 1。 master_id_list = [ 1, 2 ]num_value = 0。结果:

--------------------------------------------
| 1  |      1    |   0.03  | ... |   ...   |
--------------------------------------------
| 4  |      2    |   0.91  | ... |   ...   |
--------------------------------------------

示例 2。 master_id_list = [ 1, 2 ]num_value = 50。结果:

--------------------------------------------
| 3  |      1    |   1.22  | ... |   ...   |
--------------------------------------------
| 6  |      2    |   2.07  | ... |   ...   |
--------------------------------------------

示例 3。 master_id_list = [ 1, 2 ]num_value = 0.94。结果:

--------------------------------------------
| 2  |      1    |   0.95  | ... |   ...   |
--------------------------------------------
| 6  |      2    |   2.07  | ... |   ...   |
--------------------------------------------

是否可以使用单个 SQL 查询?我试图“玩”解决方案 herehere 但失败了。

【问题讨论】:

  • 我很困惑。在您的示例数据中没有调用 num_value
  • @GordonLinoff, num_valuef_value 的输入参数。如果num_value 介于MIN( f_value )MAX( f_value ) 之间,我需要找到第一个f_value >= num_value。否则我需要得到MIN( f_value )MAX( f_value )

标签: sql firebird firebird-3.0


【解决方案1】:

让我们致电num_value 您正在寻找的针(如“大海捞针”)。

首先,我们将对针进行标准化,使其不低于 MIN(f_value) 且不高于每个 master_idMAX(f_value)

然后我们将查找每个Detail 行,其中最近的f_value 大于或等于我们的归一化针,按master_id 分组。 (这只是一个greatest-n-per-group sql 问题)。

WITH normalized AS (     -- First normalize the needle for each master_id
  SELECT hilo.master_id,
         MAXVALUE(hilo.lo, MINVALUE(hilo.hi, d.needle)) AS needle
    FROM (SELECT ? FROM rdb$database) d (needle) -- <- change this ? to your needle
         CROSS JOIN
         (SELECT master_id, MAX(f_value), MIN(f_value)
            FROM detail GROUP BY master_id) hilo (master_id, hi, lo)
),
     ranked AS (         -- Next order f_value >= needle by master_id
  SELECT detail.*,
         ROW_NUMBER() OVER (PARTITION BY detail.master_id ORDER BY f_value ASC)
           AS rk
    FROM detail
         LEFT JOIN
         normalized ON detail.master_id = normalized.master_id
   WHERE detail.f_value >= normalized.needle
)
                         -- Strip off the rank ordering and SELECT what you want
SELECT id, master_id, f_value, ...
  FROM ranked
 WHERE rk = 1;

【讨论】:

  • 谢谢。它似乎工作。但现在我想知道:rdb$database 的东西是否必要。我已经用SELECT "hilo"."master_id", MAXVALUE( "hilo"."lo", MINVALUE( "hilo"."hi", :needle ) ) AS "needle" FROM ( SELECT "master_id", MIN( "q_nom" ), MAX( "q_nom" ) FROM "detail" GROUP BY "master_id" ) "hilo" ( "master_id", "lo", "hi" ) 替换了它,它似乎也有效。
  • @OldSkull,在您发表评论之前,没有人知道“q_nom”的存在。无论如何,不​​要使用这个答案,请使用下面 Gordon Linoff 的答案。
  • q_nomf_value,它们来自测试和工作表
【解决方案2】:

您应该能够使用相关子查询。假设num_value在主表中,f值在明细表中:

select m.*,
       (select first 1 d.f_value
        from detail d
        where d.master_id = m.master_id
        order by abs(m.num_value - d.f_value)
       )
from master m;

编辑:

如果您希望获得更大的价值——如果存在的话——只需将order by 更改为:

order by (case when d.f_value >= m.num_value then 1 else 2 end),
         abs(d.f_value - m.num_value)

【讨论】:

  • order by abs(m.num_value - d.f_value) 会找到最接近但不是 maximal 最接近。例如。你有f_value = [ 0.76, 0.85, 0.93, 1,98, 50.23 ] 并且需要找到最接近0.94 的最大值。结果将是 0.93 而不是 1.98
  • @OldSkull 。 . .这就是“最大最接近”的意思吗?我认为这只是强调“最接近”的一种方式。您可以使用order by (case when d.f_value &gt;= m.num_value then 1 else 2 end), abs(m.num_value - d.f_value) asc。这仍然比替代答案简单得多。我不会将“最大”解释为大于特定值,所以我误解了问题的那一部分。
  • 谢谢。它确实看起来更简单。对不起,愚蠢的问题:我怎样才能得到整行(d.*),而不仅仅是f_value
  • @OldSkull 。 . .最简单的方法是JOIN回到details表。
  • 谢谢。我终于设法创建了工作sql。不确定其意识形态的正确性,但它似乎按预期工作。将其添加到我原来的问题中是否正确/必要?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-07
  • 1970-01-01
  • 1970-01-01
  • 2019-07-31
相关资源
最近更新 更多