Select Firebird 中最接近的最大值

Select closest maximal numeric value in Firebird

假设有 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. 的记录
  3. 如果是num_value > MAX( f_value ),应该是MAX( f_value )
  4. 的记录
  5. 否则应该是最近最大f_value
  6. 的记录

示例 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 查询?我尝试 "play" 解决方案 here and here 但失败了。

您应该能够使用相关子查询。假设 num_value 在 master table 中并且 f 值在 detail table:

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)

让我们称 num_value 您正在寻找的针(如 "needle in the haystack")。

首先,我们将标准化指针,使其不低于 MIN(f_value) 且不高于 MAX(f_value) 每个 master_id

然后我们将查找每个 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;