使用BITAND
试图找到一种能够过滤查询的优雅方法,其中and
的值由多值参数提供。
测试数据:
WITH
patient as
(
select 1 patient_id, 'foo' patient_name from dual
union all
select 2 patient_id, 'bar' patient_name from dual
union all
select 3 patient_id, 'baz' patient_name from dual
union all
select 4 patient_id, 'zoo' patient_name from dual
)
,
-- each organ is a power of 2
organ as
(
select 2 organ_id, 'Lung' organ_name from dual
union all
select 4 organ_id, 'Pancreas' organ_name from dual
union all
select 8 organ_id, 'Liver' organ_name from dual
union all
select 16 organ_id, 'Kidney' organ_name from dual
)
,
patient_organ as
(
-- patient with a multi-organ transplant
select 1 patient_id, 4 organ_id from dual
union all
select 1 patient_id, 16 organ_id from dual
union all
-- patient with a single-organ transplant
select 2 patient_id, 4 organ_id from dual
union all
-- patient with a multi-organ transplant
select 3 patient_id, 8 organ_id from dual
union all
select 3 patient_id, 16 organ_id from dual
union all
-- patient with a single-organ transplant
select 4 patient_id, 2 organ_id from dual
)
此查询:
select p.patient_id, p.patient_name
,po.bits,po.organs
from patient p
inner join (
SELECT patient_id, sum(organ_id) AS BITS
,listagg(organ, '; ') within group (order by organ_id) ORGANS
FROM (
SELECT patient_id, po.organ_id, o.organ_name || ' [' || o.organ_id || ']' organ
FROM patient_organ po
INNER JOIN organ o ON po.organ_id = o.organ_id
)
GROUP BY patient_id
) po on p.patient_id=po.patient_id
生成所需的数据集;显示多个器官(例如Pancreas [4]; Kidney [16]
)
PATIENT_ID, PATIENT_NAME, BITS, ORGANS
1 foo 20 Pancreas [4]; Kidney [16]
2 bar 4 Pancreas [4]
3 baz 24 Liver [8]; Kidney [16]
4 zoo 2 Lung [2]
我希望能够提供参数值4,8
并获得以下结果:
PATIENT_ID, PATIENT_NAME, BITS, ORGANS
1 foo 20 Pancreas [4]; Kidney [16]
2 bar 4 Pancreas [4]
3 baz 24 Liver [8]; Kidney [16]
如果我有一个值(用:organ
= 4模拟),则可以使用BITAND
并获取多器官值:
select p.patient_id, p.patient_name
,po.bits,po.organs
from patient p
inner join (
SELECT patient_id, sum(organ_id) AS BITS
,listagg(organ, '; ') within group (order by organ_id) ORGANS
FROM (
SELECT patient_id, po.organ_id, o.organ_name || ' [' || o.organ_id || ']' organ
FROM patient_organ po
INNER JOIN organ o ON po.organ_id = o.organ_id
)
GROUP BY patient_id
) po on p.patient_id=po.patient_id
WHERE bitand(bits,:organ)=:organ
保存了多种器官:
PATIENT_ID, PATIENT_NAME, BITS, ORGANS
1 foo 20 Pancreas [4]; Kidney [16]
2 bar 4 Pancreas [4]
我可以使用多值参数(由&organs
= 4,8模拟):
select p.patient_id, p.patient_name
,po.bits,po.organs
from patient p
inner join (
SELECT patient_id, sum(organ_id) AS BITS
,listagg(organ, '; ') within group (order by organ_id) ORGANS
FROM (
SELECT patient_id, po.organ_id, o.organ_name || ' [' || o.organ_id || ']' organ
FROM patient_organ po
INNER JOIN organ o ON po.organ_id = o.organ_id
WHERE po.organ_id IN (&organs)
)
GROUP BY patient_id
) po on p.patient_id=po.patient_id
但这会丢失多器官功能的结果:
PATIENT_ID, PATIENT_NAME, BITS, ORGANS
1 foo 4 Pancreas [4]
2 bar 4 Pancreas [4]
3 baz 8 Liver [8]
理想情况下,我可以在BITAND
语句中使用IN
函数,但这在语法上无效。
还有另一种非程序性方法吗?
**编辑**
为澄清起见,我在报表工具(Crystal Reports)中引用此SQL。该工具允许您选择一个或多个参数值:您看到了organ_name
,但是提供了organ_id
。此外,参数的值以数组或逗号分隔的字符串(很难说是哪个)的形式提供,而不是总计为单个值(如答案和注释中所建议)。这种架构使这变得困难。
答案 0 :(得分:2)
如果我正确理解了您的问题,那么您想查询汇总数据,以了解至少移植了器官列表之一的患者。您只需通过以下操作即可更改谓词:
WHERE bitand(bits,:organ)=:organ
到
WHERE bitand(bits,:organ) != 0
然后您可以提供您感兴趣的器官的位掩码(例如Pancreas [4]
和Kidney [16]
的位掩码为20
,而{{1}的位掩码}和Pancreas [4]
为Liver [8]
)。这将起作用,因为只要掩码中的一位与该位匹配,结果将为非零;如果与位不匹配,则结果将为零。
要使用多值参数,您只需将其转换为位掩码,然后将:organ绑定值替换为派生的位掩码,如下所示:
12
在这种情况下,我将像您一样模拟多值参数,然后在针对器官表的标量值子查询中将其转换为位掩码。