Предложение OR, возвращающее неверные результаты SQL Developer - PullRequest
0 голосов
/ 14 мая 2018

Я пишу запрос к таблице, которая содержит имена пользователей и идентификаторы пользователей. Я хочу игнорировать имена пользователей, которые содержат любую из следующих строк:

LOCKED, ДЕАКТИВИЗИРОВАНО, ЗАКРЫТО

Взяв в качестве примера запись с идентификатором пользователя 1234 и именем пользователя "myusername LOCKED"

Следующий запрос все еще возвращает эту запись

select username 
from table where (
instr(username),'LOCKED') = 0
or instr(username),'CLOSED') = 0
or instr(username),'DEACTIVATED') = 0
) and userid = '1234'

Я бы не ожидал, что результаты будут возвращены, поскольку, хотя идентификатор пользователя 1234 существует, он не существует без строки "LOCKED" в имени пользователя.

Почему возвращается запись?

Это потому, что другие условия верны? то есть. есть запись с идентификатором 1234 и имя пользователя не содержит "ЗАКРЫТО"? (потому что он содержит «LOCKED»

Ответы [ 2 ]

0 голосов
/ 15 мая 2018

Gordon_Linoff имеет правильный ответ, но мне нравится играть с общими табличными выражениями (CTE), поэтому я подумал, что добавлю этот дополнительный ответ на случай, если вы захотите указать имена исключений в виде CSV, а не жестко кодировать их вSQL.

   WITH
    user_table
    AS
        -- set up some usernames for testing
        (SELECT 'ARBY' AS username
           FROM DUAL
         UNION ALL
         SELECT 'CLOSED'
           FROM DUAL
         UNION ALL
         SELECT 'GOOFY'
           FROM DUAL),
    csv_value
    AS
        (SELECT 'LOCKED, CLOSED, DEACTIVATED' csv
           FROM DUAL),
    exclude_uservalues
    AS
        --  1) Bracket CSV value with commas
        --  2) Remove any spaces (presumes no embedded spaces within CSV values)
        (SELECT ',' || REPLACE (csv, ' ', NULL) || ',' AS csv_values
           FROM csv_value),
    exclude_userset (
                        exclude_user, csv_values
                    )
    AS
        -- This common table expression will split the CSV values into separate records
        -- It works by
        --   a) extracting the value between the first two commas
        --   b) dropping everything before the second comma
        --   c) terminating when there is no second comma
        (SELECT SUBSTR (csv_values, 2, INSTR (csv_values, ',', 2) - 2) AS exclude_user
              , SUBSTR (csv_values, INSTR (csv_values, ',', 2)) AS csv_values
           FROM exclude_uservalues
         UNION ALL
         SELECT SUBSTR (csv_values, 2, INSTR (csv_values, ',', 2) - 2) AS exclude_user
              , SUBSTR (csv_values, INSTR (csv_values, ',', 2)) AS csv_values
           FROM exclude_userset
          WHERE INSTR (csv_values, ',', 2) > 0)
SELECT username
  FROM user_table LEFT OUTER JOIN exclude_userset ON username = exclude_user
 WHERE exclude_user IS NULL;

Результат этой чепухи:

USERNAME
--------
GOOFY
ARBY
0 голосов
/ 14 мая 2018

Оператор or возвращает true, если какое-либо из условий true.То есть, даже если один из них равен false, а два других - true, результат будет true:

или:

false true true -> true
false false true -> true

Оператор and, с другой стороны, возвращает false, если ЛЮБОЕ из условий false:

и:

false true true -> false
false false true -> false

Я думаю,вместо этого вам нужен оператор AND.В своей голове подумайте "если Y истинно И если X истинно ___" .То, что у вас было раньше, это ", если Y истинно ИЛИ, если X истинно ___" , что, конечно, возвращало true.

Итак, решение:

select username 
from table
where (instr(username), 'LOCKED') = 0 and
      instr(username), 'CLOSED') = 0 and
      instr(username), 'DEACTIVATED') = 0
     ) and
    userid = '1234'
...