Exemplo
Eu tenho uma mesa
ID myField
------------
1 someValue
2 NULL
3 someOtherValue
e uma expressão booleana T-SQL que pode ser avaliada como TRUE, FALSE ou (devido à lógica ternária do SQL) UNKNOWN:
SELECT * FROM myTable WHERE myField = 'someValue'
-- yields record 1
Se eu quiser obter todos os outros registros , não posso simplesmente negar a expressão
SELECT * FROM myTable WHERE NOT (myField = 'someValue')
-- yields only record 3
Eu sei como isso acontece (lógica ternária) e sei como resolver esse problema específico.
Eu sei que posso apenas usar myField = 'someValue' AND NOT myField IS NULL
e recebo uma expressão "invertível" que nunca produz UNKNOWN:
SELECT * FROM myTable WHERE NOT (myField = 'someValue' AND myField IS NOT NULL)
-- yields records 2 and 3, hooray!
Caso Geral
Agora, vamos falar sobre o caso geral. Digamos que em vez de myField = 'someValue'
eu tenho uma expressão complexa envolvendo muitos campos e condições, talvez subconsultas:
SELECT * FROM myTable WHERE ...some complex Boolean expression...
Existe uma maneira genérica de "inverter" essa expulsão? Pontos de bônus se funcionar para subexpressões:
SELECT * FROM myTable
WHERE ...some expression which stays...
AND ...some expression which I might want to invert...
Preciso dar suporte ao SQL Server 2008-2014, mas se houver uma solução elegante que exija uma versão mais recente que 2008, também estou interessado em saber sobre isso.
fonte
O primeiro pensamento que me ocorre:
Devoluções:
Devoluções:
Isso depende da maneira como
EXISTS
sempre retorna verdadeiro ou falso , nunca desconhecido .SELECT 1 WHERE
Infelizmente, a necessidade é necessária, mas pode ser viável para sua exigência, por exemplo:Consulte EXISTS (Transact-SQL)
Um exemplo trabalhado um pouco mais complexo, mostrando como um
EXISTS
ou outroCASE/IIF
métodos podem ser aplicados para inverter predicados individuais:Código:
fonte
Se você não se importa em reescrever as sub-expressões antecipadamente, pode usar
COALESCE
:Você deve se certificar de que
'notSomeValue'
é diferente de'someValue'
; de preferência, seria algum valor completamente ilegal para a coluna. (Também não pode serNULL
, é claro.) Isso é fácil negar, mesmo se você tiver uma lista longa:Mais limpo, mais simples e mais óbvio do que
CASE
ouIIF
, na minha opinião. A principal desvantagem é ter um segundo valor que você sabe que não é igual, mas isso só é realmente um problema se você não souber o valor real antecipadamente. Nesse caso, você pode fazer o que Hanno Binder sugere e usarCOALESCE(myField, CONCAT('not', 'someValue')) = 'someValue'
(onde'someValue'
seria realmente parametrizado).COALESCE
está documentado para estar disponível a partir do SQL Server 2005.Lembre-se de que mexer com sua consulta dessa maneira (usando qualquer um dos métodos recomendados aqui) pode dificultar a otimização do banco de dados. Para conjuntos de dados grandes,
IS NULL
é provável que a versão seja mais fácil de otimizar.fonte
COALESCE(myField, CONCAT('not', 'someValue')) = 'someValue'
deve funcionar para qualquer "someValue" e qualquer dado na tabela.Existe o operador EXCEPT set embutido que, efetivamente, remove os resultados de uma segunda consulta da primeira.
fonte
O COALESCE está disponível?
fonte
sql-server
, porém, nãomysql
oupostgresql
.BOOLEAN
tipo e o MySQL tem umBOOLEAN
tipo (falsificado) que pode ser parâmetros daCOALESCE()
função. Se a pergunta tivesse sido marcada comsql-agnostic
ousql-standard
, a resposta seria boa.