2013-12-03 4 views
1

Возможно ли пропустить динамическое предложение where?Пропустить Где предложение динамически

Я пытаюсь добиться этого.

select count(*) 
     into user_count 
from mstuser a 
where ((gender_compare is not null or gender_compare != '') 
    and upper(a.gender)=upper(gender_compare)) 
    and (age_compare_group is not null or age_compare_group != '') 
    and (MONTHS_BETWEEN(sysdate, a.dob)/12 
     between substr(age_compare_group, 0, INSTR(age_compare_group, '-') - 1) 
      and substr(age_compare_group, INSTR(age_compare_group, '-') + 1))) 

Но, похоже, он не работает.

Что я хочу:

Это должно дать все результаты, если оба значения gender_compare и age_compare_group равно нулю. Если какой-либо из них не имеет значения null, он должен отображать результат на основе той части запроса.

Сообщите мне, если я могу сделать что-то еще для этого же.

+0

Вы можете использовать динамический SQL –

ответ

4

Похоже, что вам нужно сделать, это разделить свое состояние на две группы

WHERE (gender_compare IS NULL OR gender_compare = '') 
     OR (<the rest of condition>) 

Если genter_compare IS NULL вычисляет TRUE, все условие будет TRUE, поэтому WHERE пункт будет эквивалентна WHERE TRUE, что эквивалентно отсутствию инструкции WHERE.

В противном случае будет выполнено второе условие.

+1

'gender_compare =«» 'является излишним, поскольку это всегда будет' false'. Это связано с тем, что oracle хранит пустую строку как значение «NULL», а тестирование «NULL» для равенства на что-либо еще всегда возвращает «FALSE». – MT0

+0

ах, есть «оракул» в тегах! –

2

Я завернул SQL в функцию, чтобы сделать его проще, чтобы показать некоторые тесты:

SQL Fiddle

Oracle 11g R2 Настройка схемы:

CREATE TABLE mstuser (id, gender, dob) AS 
      SELECT 1, 'M', SYSDATE - INTERVAL '1' YEAR FROM DUAL 
UNION ALL SELECT 2, 'M', SYSDATE - INTERVAL '2' YEAR FROM DUAL 
UNION ALL SELECT 3, 'M', SYSDATE - INTERVAL '3' YEAR FROM DUAL 
UNION ALL SELECT 4, 'F', SYSDATE - INTERVAL '1' YEAR FROM DUAL 
UNION ALL SELECT 5, 'F', SYSDATE - INTERVAL '2' YEAR FROM DUAL 
UNION ALL SELECT 6, 'F', SYSDATE - INTERVAL '3' YEAR FROM DUAL 
UNION ALL SELECT 7, 'F', SYSDATE - INTERVAL '4' YEAR FROM DUAL 
UNION ALL SELECT 8, 'F', SYSDATE - INTERVAL '5' YEAR FROM DUAL 
/

CREATE OR REPLACE FUNCTION count_by_compare (
    gender_compare mstuser.gender%TYPE, 
    age_compare_group VARCHAR2 
) RETURN NUMBER 
AS 
    user_count NUMBER; 
BEGIN 
    SELECT COUNT(1) 
    INTO user_count 
    FROM mstuser a 
    WHERE ( gender_compare IS NULL 
      OR UPPER(a.gender) = UPPER(gender_compare) 
     ) 
    AND ( age_compare_group IS NULL 
      OR MONTHS_BETWEEN(sysdate, a.dob)/12 
      BETWEEN TO_NUMBER(SUBSTR(age_compare_group, 1, INSTR(age_compare_group, '-') - 1)) 
       AND TO_NUMBER(SUBSTR(age_compare_group, INSTR(age_compare_group, '-') + 1)) 
     ); 
    return user_count; 
END; 
/

запроса 1 :

WITH Tests AS (
      SELECT 'M' AS gender, '0-1' AS age FROM DUAL 
    UNION ALL SELECT 'F', '0-6' FROM DUAL 
    UNION ALL SELECT 'F', '0-1' FROM DUAL 
    UNION ALL SELECT 'M', '0-6' FROM DUAL 
    UNION ALL SELECT NULL, '0-2' FROM DUAL 
    UNION ALL SELECT 'M', NULL FROM DUAL 
    UNION ALL SELECT 'F', NULL FROM DUAL 
    UNION ALL SELECT NULL, NULL FROM DUAL 
    UNION ALL SELECT '', '' FROM DUAL 
) 
SELECT gender, 
     age, 
     SUBSTR(age, 1, INSTR(age, '-') - 1), 
     SUBSTR(age, INSTR(age, '-') + 1), 
     count_by_compare(gender, age) 
FROM Tests 

Results:

| GENDER | AGE | SUBSTR(AGE,1,INSTR(AGE,'-')-1) | SUBSTR(AGE,INSTR(AGE,'-')+1) | COUNT_BY_COMPARE(GENDER,AGE) | 
|--------|--------|--------------------------------|------------------------------|------------------------------| 
|  M | 0-1 |        0 |       1 |       1 | 
|  F | 0-6 |        0 |       6 |       5 | 
|  F | 0-1 |        0 |       1 |       1 | 
|  M | 0-6 |        0 |       6 |       3 | 
| (null) | 0-2 |        0 |       2 |       4 | 
|  M | (null) |       (null) |      (null) |       3 | 
|  F | (null) |       (null) |      (null) |       5 | 
| (null) | (null) |       (null) |      (null) |       8 | 
| (null) | (null) |       (null) |      (null) |       8 | 

Кроме того, вам не нужно, чтобы проверить, как != '' оракул представляет собой пустую строку в качестве NULL - увидеть окончательный тест выше или пустые сравнения строк ниже:

SELECT CASE WHEN '' = '' THEN 1 ELSE 0 END AS equal, 
     CASE WHEN '' != '' THEN 1 ELSE 0 END AS not_equal, 
     CASE WHEN '' IS NULL THEN 1 ELSE 0 END AS is_null, 
     CASE WHEN '' IS NOT NULL THEN 1 ELSE 0 END AS is_not_null 
FROM DUAL 

дает результат:

| EQUAL | NOT_EQUAL | IS_NULL | IS_NOT_NULL | 
|-------|-----------|---------|-------------| 
|  0 |   0 |  1 |   0 | 
+0

Спасибо за такой хороший ответ с хорошим и чистым объяснением. –

0

Обычно вы сравниваете с gender_compare, когда gender_compare не является нулевым и сравнивается с age_compare_group, когда age_compare_group не равно null. Вы также можете указать его так: «либо gender_compare имеет значение null, либо я сравниваю его», и «age_compare_group имеет значение null или я сравниваю его». И это, как вы бы записать оператор выбора соответственно:

select count(*) 
    into user_count 
from mstuser a 
where 
( 
    gender_compare is null 
or 
    upper(a.gender) = upper(gender_compare) 
) -- compare gender if given 
and 
( 
    age_compare_group is null 
or 
    months_between(sysdate, a.dob)/12 
    between substr(age_compare_group, 0, instr(age_compare_group, '-') - 1) 
    and substr(age_compare_group, instr(age_compare_group, '-') + 1) 
); 

Если, однако, вы действительно хотите только сравнить с gender_compare и age_compare_group, когда оба дают и не сравнить вообще, если только один или ни один из они даются (т.е.по крайней мере, один из них равно нулю), то сделать это следующим образом:

select count(*) 
    into user_count 
from mstuser a 
where 
( 
    (
    gender_compare is null 
    or 
    age_compare_group is null 
) 
or 
    (
    upper(a.gender) = upper(gender_compare) 
    and 
    months_between(sysdate, a.dob)/12 
     between substr(age_compare_group, 0, instr(age_compare_group, '-') - 1) 
     and substr(age_compare_group, instr(age_compare_group, '-') + 1) 
    ) 
); 
Смежные вопросы