«Умное» условие where для фильтрации значений (Oracle 10g) - PullRequest
0 голосов
/ 11 августа 2009

Представьте себе, что таблица t1 имеет 2 столбца:

col1      col2
--------------
test 900     1 
test 901     2
test 902     3
user 901     4
test 909     5

Я хочу найти только одну строку на 2 параметра (имя, код), где указано ее имя (test, user ..etc) и код - (901, 902, null, ... и т. Д.). Не может быть более одной строки (код, имя) - я имею в виду, что нет двух строк, которые имеют "test 901" в Col1.

Код:

declare 
 name varchar2(30);
 code varchar(10);
 col_val varchar2(30);
 col2_val numeric;
begin
 name:= 'test';
 code := '900';

  select col1, col2 into col_val, col2_val 
  from t1 
  where 

   ( REGEXP_LIKE(col1, name||'\s+'||code) -- (3)
   or (
        not   REGEXP_LIKE(col1, name||'\s+'||code) -- (1) 
        and REGEXP_LIKE(col1, name) -- (2)
      )
    )
  order by col1;
  DBMS_OUTPUT.PUT_LINE('val:'||col_val||' id:'||col2_val);
end;

1) для тестовых значений name: = 'test' code: = '900' результат должен быть "val: test 900 id: 1" - все нормально.

2) НО для имени: = 'test' code: = '909' результат должен быть "val: test 909 id: 5 ", но я получил "val : test 900 id: 1 "(первая строка с именем = 'Test') - НЕ хочу, чтобы я хотел.

3) и в случае name: = 'test' code: = '999' результат должен быть "val: test 900 id: 1" (кода НЕТ 999, поэтому мне нужно просто любая строка с именем = «test» внутри).

Главный вопрос: ПОЧЕМУ оракул игнорирует (1) предложение для 2) примера? Возможно, я делаю что-то не так - было бы здорово, если бы Ты мог показать мою ошибку!

Ответы [ 3 ]

0 голосов
/ 11 августа 2009

Это условие:

(REGEXP_LIKE(col1, :name || '\s+' || :code)
OR (NOT REGEXP_LIKE(col1, :name || '\s+' || :code) AND REGEXP_LIKE(col1, :name))

будет соответствовать всем строкам в вашем примере.

При поиске test 909 вы соответствуете 'test 909' OR (NOT 'test 909' AND 'test'). Это будет соответствовать каждой строке, начиная с 'test' (либо за первое условие, либо за секунду).

Поскольку вы используете rownum = 1, первый столбец соответствует возвращенному, в вашем случае 'test 900'

Если вы хотите резервное совпадение (верните 'test 909', если оно существует, любое 'test' в противном случае), используйте это:

SELECT  *
FROM    (
        SELECT  *
        FROM    t1
        WHERE   REGEXP_LIKE(col1, name || '\s+' || code)
        UNION ALL
        SELECT  *
        FROM    t1
        WHERE   REGEXP_LIKE(col1, name)
        )
WHERE   rownum = 1

или, если вам не нравится UNION с:

SELECT  *
FROM    (
        SELECT  *
        FROM    t1
        WHERE   REGEXP_LIKE(col1, name)
        ORDER BY
                CASE WHEN REGEXP_LIKE(col1, name || '\s+' || code) THEN 0 ELSE 1 END
        )
WHERE   rownum = 1

Последний, однако, менее эффективен, поскольку должен сортировать.

0 голосов
/ 11 августа 2009

Было бы гораздо лучше хранить части col1 в отдельных столбцах. В любом случае, вот еще что, кроме того, что сказал Тило.

Игнорируя часть rownum, четыре строки вашей таблицы соответствуют предложению WHERE в вашем примере. Ваше предложение WHERE на английском языке «имя и код совпадают, иначе они не совпадают, но совпадают имена».

Это сложный способ сказать "совпадение имен", и нет необходимости в таком сложном предложении WHERE, если это действительно то, что вы имеете в виду.

(Если \ s означает пробел, в котором я не уверен, это «имя совпадает, а после имени есть пробел».) Если вы хотите увидеть совпадение «оба» перед совпадением только по имени, выберите первую строку в этом порядке «скоринга»:

ORDER BY 
  CASE WHEN REGEXP_LIKE(col1, name||'\s+'||code) THEN 2 
       WHEN REGEXP_LIKE(col1, name||'\s+) THEN 1
  ELSE 0 END DESC

Ответ на комментарий:

Ваше предложение WHERE не может выразить НО, если нет, ... аспект вашего требования. Предложение WHERE просто выражает Если в строке есть и CODE, и NAME, или строка, содержащая только NAME, покажите ее , и это не ваше требование.

Чтобы выразить ГДЕ A, НО если не A, то B , это не сработает для записи

WHERE <condition A> OR <condition B>

Вы должны написать

WHERE <condition A> OR (NOT <condition A> AND <condition B>)

или сделайте что-нибудь, как я сделал с моим выражением CASE.

Это помогает?

0 голосов
/ 11 августа 2009

Вы не можете комбинировать ROWNUM и ORDER BY следующим образом, потому что ROWNUM - это номер строки до , в которой она сортируется.

Тебе нужно быть более многословным и написать что-то вроде

 select a.* from (
     select * from table order by col1 ) a
  where rownum = 1;

Проверьте документацию на ROWNUM и подкачку.

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