Используйте Oracle BITAND с параметром с несколькими значениями - PullRequest
0 голосов
/ 19 декабря 2018

Попытка найти элегантный способ фильтрации запроса, используя BITAND, где значения and ed предоставляются параметром с несколькими значениями.

Данные тестирования:

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, но это синтаксически недопустимо.

Есть ли другой непроцедурный подход?

**edit **

Чтобы уточнить, я ссылаюсь на этот SQL в инструменте отчетности (Crystal Reports).Инструмент позволяет выбрать одно или несколько значений параметров: вы видели organ_name, но поставляется organ_id.Кроме того, значения параметра предоставляются в виде массива или строки, разделенной запятыми (сложно сказать, какой), а не суммируются в одно значение (как предлагается в ответе и комментариях).Именно эта архитектура делает это трудным.

1 Ответ

0 голосов
/ 19 декабря 2018

Если я правильно понимаю ваш вопрос, вы хотите запросить ваши обобщенные данные о пациентах, у которых был хотя бы один из перечисленных органов.Вы должны быть в состоянии сделать это, просто изменив предикат в первой попытке с:

WHERE bitand(bits,:organ)=:organ

на

WHERE bitand(bits,:organ) != 0

Затем вы можете указать немного маски интересующих вас органов.в (например, битовая маска для Pancreas [4] и Kidney [16] будет 20, а битовая маска для Pancreas [4] и Liver [8] будет 12).Это будет работать, поскольку пока один бит в маске совпадает с битом, а результат будет отличным от нуля, если ни один из них не совпадает с битом, а результат будет равен нулю.

Чтобы использовать параметр с несколькими значениями, вам просто нужно преобразовать егов битовую маску и замените значение: organ bind на производную битовую маску следующим образом:

WHERE bitand(bits,(select sum(distinct organ_id) from organ where organ_id in (&organs))) !=0

В этом случае я симулирую многозначный параметр, как вы это сделали, и преобразую его в битовую маску вскалярный подзапрос к органной таблице.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...