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
我应该得到一个详细记录:
- 如果是
num_value < MIN( f_value )
,应该是MIN( f_value )
的记录
- 如果是
num_value > MAX( f_value )
,应该是MAX( f_value )
的记录
- 否则应该是最近最大
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 查询?我尝试 "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;
假设有 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
我应该得到一个详细记录:
- 如果是
num_value < MIN( f_value )
,应该是MIN( f_value )
的记录
- 如果是
num_value > MAX( f_value )
,应该是MAX( f_value )
的记录
- 否则应该是最近最大
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 查询?我尝试 "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;