将Oracle的BITAND与多值参数一起使用

时间:2018-12-18 21:58:47

标签: oracle

使用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。此外,参数的值以数组或逗号分隔的字符串(很难说是哪个)的形式提供,而不是总计为单个值(如答案和注释中所建议)。这种架构使这变得困难。

1 个答案:

答案 0 :(得分:2)

如果我正确理解了您的问题,那么您想查询汇总数据,以了解至少移植了器官列表之一的患者。您只需通过以下操作即可更改谓词:

WHERE bitand(bits,:organ)=:organ

WHERE bitand(bits,:organ) != 0

然后您可以提供您感兴趣的器官的位掩码(例如Pancreas [4]Kidney [16]的位掩码为20,而{{1}的位掩码}和Pancreas [4]Liver [8])。这将起作用,因为只要掩码中的一位与该位匹配,结果将为非零;如果与位不匹配,则结果将为零。

要使用多值参数,您只需将其转换为位掩码,然后将:organ绑定值替换为派生的位掩码,如下所示:

12

在这种情况下,我将像您一样模拟多值参数,然后在针对器官表的标量值子查询中将其转换为位掩码。