Поиск PL/SQL Regex на основе как символов, так и цифр

У меня есть таблица TA, в которой есть столбец inv_ref с данными, как показано ниже.

inv_ref
----------
MX/3280/20
CT/3281/20
CT/3109/20
MX/3272/20
RF/3275/20

Мое требование состоит в том, чтобы получить, тогда как средние 4 цифры числа inv_ref между 3270 и 3299 также начинаются с MX и CT.

select * from TA where regexp_like(inv_ref, 'CT/32[7-9][0-9]/20')

Приведенный выше запрос возвращает только CT. Как вернуть значения, связанные с CT и MX, без RF?


person Dilhan Nakandala    schedule 10.07.2020    source источник
comment
Привет, это твой стол очень большой ?? Вам нужны только CT и MX? или у вас есть несколько комбинаций, и вы хотите пропустить только одну? . Я имею в виду, что если таблица невелика, вы можете использовать условие ИЛИ с двумя выражениями REGEXP_LIKE.   -  person Roberto Hernandez    schedule 10.07.2020
comment
Привет, сама таблица большая, и на данный момент мне нужны только значения CT и MX, значения в этом столбце могут начинаться только с CT, MX и RF.   -  person Dilhan Nakandala    schedule 10.07.2020


Ответы (4)


Вы можете использовать:

SELECT *
FROM   TA
WHERE  REGEXP_LIKE(inv_ref, '^(CT|MX)/32[7-9][0-9]/20$')

Затем, если у вас есть тестовые данные:

CREATE TABLE TA ( inv_ref, is_valid ) AS
SELECT 'MX/3280/20', 'Valid' FROM DUAL UNION ALL
SELECT 'CT/3281/20', 'Valid' FROM DUAL UNION ALL
SELECT 'CT/3109/20', 'Invalid, number too low' FROM DUAL UNION ALL
SELECT 'MX/3272/20', 'Valid' FROM DUAL UNION ALL
SELECT 'RF/3275/20', 'Invalid, wrong start' FROM DUAL UNION ALL
SELECT 'CX/3299/20', 'Invalid, wrong start' FROM DUAL UNION ALL
SELECT 'MT/3270/20', 'Invalid, wrong start' FROM DUAL UNION ALL
SELECT 'ACT/3270/20', 'Invalid, wrong start' FROM DUAL;

Это выводит:

INV_REF    | IS_VALID
:--------- | :-------
MX/3280/20 | Valid   
CT/3281/20 | Valid   
MX/3272/20 | Valid   

db‹›fiddle здесь

person MT0    schedule 10.07.2020

Было бы намного проще добавить виртуальные сгенерированные столбцы:

alter table your_table add (
  P1 generated always as (regexp_substr(inv_ref,'^[^/]+')),
  P2 generated always as (to_number(regexp_substr(inv_ref,'/(\d+)/',1,1,null,1))),
  P3 generated always as (regexp_substr(inv_ref,'[^/]+$'))
);

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

Полный тестовый пример:

CREATE TABLE YOUR_TABLE ( inv_ref ) AS
SELECT 'MX/3280/20' FROM DUAL UNION ALL
SELECT 'CT/3281/20' FROM DUAL UNION ALL
SELECT 'CT/3109/20' FROM DUAL UNION ALL
SELECT 'MX/3272/20' FROM DUAL UNION ALL
SELECT 'RF/3275/20' FROM DUAL UNION ALL
SELECT 'CX/3299/20' FROM DUAL UNION ALL
SELECT 'MT/3270/20' FROM DUAL UNION ALL
SELECT 'ACT/3270/20' FROM DUAL;

alter table your_table add (
  P1 generated always as (regexp_substr(inv_ref,'^[^/]+')),
  P2 generated always as (to_number(regexp_substr(inv_ref,'/(\d+)/',1,1,null,1))),
  P3 generated always as (regexp_substr(inv_ref,'[^/]+$'))
);

select * from your_table
where p1 IN ('CT', 'MX')
  and p2 BETWEEN 3270 and 3299;

Результат:

MX/3280/20  MX        3280 20
CT/3281/20  CT        3281 20
MX/3272/20  MX        3272 20
person Sayan Malakshinov    schedule 10.07.2020
comment
Чтобы ответить на вопрос, вам нужно предложение WHERE в последнем выборе, например WHERE p1 IN ('CT', 'MX') AND p2 BETWEEN 3270 and 3299. - person MT0; 10.07.2020
comment
@MT0 это было очевидно, но ладно, добавил :) - person Sayan Malakshinov; 10.07.2020

Вы могли бы использовать

SQL> create table my_test ( inv_ref varchar2(100) ) ;

SQL> insert into my_test values ( 'MX/3280/20') ;

SQL> insert into my_test values ( 'CD/3281/20') ;

SQL> insert into my_test values ( 'CD/3109/20') ;

SQL> insert into my_test values ( 'MX/3272/20') ;

SQL> insert into my_test values ( 'RF/3275/20') ;

Table created.

SQL> SQL> SQL>
1 row created.

SQL> SQL>
1 row created.

SQL> SQL>
1 row created.

SQL> SQL>
1 row created.

SQL> SQL>

1 row created.

SQL> commit ;

Commit complete.

Два варианта (в моем случае я использую CD вместо CT). Эта опция будет работать, пока строки ограничены примером. Если бы у вас была другая комбинация, она не будет, так как CD|MX означает C или D, или M, или X). Смотрите комментарии к ответу. @MTO, спасибо за ваши комментарии.

SQL> select * from my_test where regexp_like(inv_ref, '[CD|MX]/32[7-9][0-9]/20')

INV_REF
--------------------------------------------------------------------------------
MX/3280/20
CD/3281/20
MX/3272/20


SQL> select * from my_test where regexp_like(inv_ref, 'CD/32[7-9][0-9]/20') or 
regexp_like(inv_ref, 'MX/32[7-9][0-9]/20')

INV_REF
--------------------------------------------------------------------------------
MX/3280/20
CD/3281/20
MX/3272/20
person Roberto Hernandez    schedule 10.07.2020
comment
Первый вариант неверный. [CD|MX] будет соответствовать одному символу, который может быть либо C, либо D, либо |, либо M, либо X. Похоже, это удалось, потому что вы не привязали его к началу строки, и он может соответствовать D или X, но совпадает слишком много. db‹›fiddle - person MT0; 10.07.2020
comment
Если я вложу комбинации CD|MX, то должно работать, верно? - person Roberto Hernandez; 10.07.2020
comment
Нет, теперь он будет соответствовать одному символу ", или C, или D, или ", или |, или ", или M, или X, или ". - person MT0; 10.07.2020
comment
вы совершенно правы. Я отредактирую свой ответ, чтобы сказать, что он будет работать, пока комбинации ограничены. Спасибо @MT0 - person Roberto Hernandez; 10.07.2020

Вы можете использовать значения, разделенные | (или), следующим образом:

select * from TA where regexp_like(inv_ref, '^(CT|MX)/32[7-9][0-9]/20');

db‹›fiddle

person Popeye    schedule 10.07.2020
comment
Это не правильно. [CT|MX] будет соответствовать одному символу, который может быть либо C, либо T, либо |, либо M, либо X. Похоже, это удалось, потому что вы не привязали его к началу строки, и он может соответствовать T или X, но совпадает слишком много. db‹›fiddle - person MT0; 10.07.2020
comment
Слишком много совпадений с db‹›fiddle - person MT0; 10.07.2020
comment
Просто используйте ^(CT|MX). [abc] — это класс символов, который означает любой символ из a, b или c (класс символов может использовать диапазоны, например [a-d] = [abcd]) - person Sayan Malakshinov; 10.07.2020