Используйте Oracle BITAND с параметром с несколькими значениями

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

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

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, но это синтаксически неверно.

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

** редактировать **

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

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
120
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

WHERE bitand(bits,:organ)=:organ

к

WHERE bitand(bits,:organ) != 0

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

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

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

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

@craig - я согласен - Sentinel опередил меня. Для дальнейшего уточнения: когда вы сравниваете два многобитовых кода (например, значения BITS для отдельных пациентов с кодом, например, 20 для проверки поджелудочной железы или почек), состояние bitand(bits, :organ) = :organ означает, что пациенту были трансплантаты для ВСЕХ кодов в :organ. . Напротив, условие bitand(bits, :organ) > 0 (как я бы предпочел его записать) означает, что эти два пересечения имеют непустое значение - пациенту была проведена по крайней мере ОДНА из трансплантатов органов, представленных в битовом коде :organ.

mathguy 19.12.2018 00:27

Спасибо за вотум доверия @mathguy. Я тоже изначально использовал > 0 , но решил переключиться на != 0, так как функция bitand работает с двоичными целыми числами с дополнением n-bit и двумя. Таким образом, они могут принимать отрицательные значения в дополнение к положительным. Использование != вместо этого позволяет использовать знаковый бит в дополнение ко всем остальным битам. В Oracle 12c n = 128 бит, поэтому вы можете иметь не более 128 различных элементов, идентифицируемых одной битовой маской.

Sentinel 19.12.2018 00:46

Думаю, из-за дополнительного числа до двух BITAND работает только до 127 бит. Если вам нужно работать с более чем 127 битами, используйте функцию UTL_RAW.BIT_AND.

Wernfried Domscheit 19.12.2018 08:37

Другие вопросы по теме