PL / SQL "Где" условное предложение "ГДЕ" - PullRequest
2 голосов
/ 22 марта 2019

Я пытаюсь сделать запрос к таблице и застрял при создании предложения WHERE. В таблице есть столбец «SUBCLASS», который имеет значение типа «UN» или «U *».

«U *» означает совпадение «U» и любого другого символа (например, UB, UC, ...).

Таблица выглядит так:

ID    ISC    SUBCLASS
---   ----   ---------
1     ABC        UN
2     DEF        UN
3     DEF        U*

С учетом строки UC12341001000012 или UN12341001000012, как бы я создал предложение WHERE.

Я пытался:

SELECT * FROM MYTABLE
WHERE x AND y AND
(SUBCLASS='UC' OR SUBSTR(SUBCLASS, 1, 1) = SUBSTR('UC',1,1))

но возвращает все строки. (Я использую здесь «UC», но на самом деле это параметр, передаваемый хранимой процедуре).

Итак, учитывая UC12341001000012, я должен получить третью запись, учитывая UN12341001000012, я должен получить первые две записи.

1 Ответ

1 голос
/ 22 марта 2019

(немного) хитрый бит не включает строку подстановки U*, когда есть точные совпадения.Существуют различные подходы с подзапросами и объединениями и т. Д .;этот использует встроенное представление для пометки и подсчета точных и подстановочных совпадений, а затем фильтрует это встроенное представление:

select id, isc, subclass, exact, wild
from (
  select id, isc, subclass,
    case when subclass = substr(:str, 1, 2) then 'Y' end as exact,
    case when subclass = substr(:str, 1, 1) || '*' then 'Y' end as wild,
    count(case when subclass = substr(:str, 1, 2) then subclass end) over () as exact_cnt
  from mytable
  where subclass like substr(:str, 1, 1) || '%' -- optional
)
where exact = 'Y' or (wild = 'Y' and exact_cnt = 0)

Я использовал переменную связывания :str вместо вашего более короткого литерала, частичнопотому что я думаю, что это более понятно, но также потому, что только с UC непонятно, почему я использовал substr() вызовов больше, чем вы;поскольку с вашими полными более длинными значениями вы все равно хотите смотреть только на первые два.

Вы можете немного изменить его, чтобы не повторять выражение регистра (с другим слоем встроенного представления / CTE, который вы затем посчитаете из), или измените внутренний фильтр, чтобы явно искать те же вещи, которые проверяет выражение case (или не включать его - зависит от объема, индексов ...) и т. д., но, надеюсь, дает вам идею.

С CTE для предоставления данных образца подкласса:

var str varchar2(20);

exec :str := 'UC12341001000012';

-- CTE for sample data
with mytable (id, isc, subclass) as (
  select 1, 'ABC', 'UN' from dual
  union all select 2, 'DEF', 'UN' from dual
  union all select 3, 'DEF', 'U*' from dual
)
-- actual query
select id, isc, subclass
from (
  select id, isc, subclass,
    case when subclass = substr(:str, 1, 2) then 'Y' end as exact,
    case when subclass = substr(:str, 1, 1) || '*' then 'Y' end as wild,
    count(case when subclass = substr(:str, 1, 2) then subclass end) over () as exact_cnt
  from mytable
  where subclass like substr(:str, 1, 1) || '%' -- optional
)
where exact = 'Y' or (wild = 'Y' and exact_cnt = 0);

        ID ISC SU
---------- --- --
         3 DEF U*

exec :str := 'UN12341001000012';

<same query>

        ID ISC SU
---------- --- --
         1 ABC UN
         2 DEF UN

в случае возвращения нескольких строк

Если вам нужна только одна из строк с точным соответствиемВы можете добавить вызов row_number() во встроенном представлении - с подходящим значением order by для того, чтобы разделить связи, а затем добавить его во внешний фильтр:

select id, isc, subclass
from (
  select id, isc, subclass,
    case when subclass = substr(:str, 1, 2) then 'Y' end as exact,
    case when subclass = substr(:str, 1, 1) || '*' then 'Y' end as wild,
    count(case when subclass = substr(:str, 1, 2) then subclass end) over () as exact_cnt,
    row_number() over (partition by subclass order by isc) as rn
  from mytable
  where subclass like substr(:str, 1, 1) || '%' -- optional
)
where (exact = 'Y' or (wild = 'Y' and exact_cnt = 0))
and rn =1

... илиВы можете изначально выбрать все три строки, но расположить их так, чтобы подстановочный знак был последним, прежде чем применять фильтр rownum:

select id, isc, subclass
from (
  select id, isc, subclass
  from mytable
  where subclass = substr(:str, 1, 2)
  or subclass = substr(:str, 1, 1) || '*'
  order by case when subclass like '_*' then 2 else 1 end,
    isc -- or however you want to split ties
)
where rownum = 1
...