SELECT com coluna calculado que é dependente de uma correlação
-
18-09-2019 - |
Pergunta
Eu não faço um monte de SQL, e na maioria das vezes, eu estou fazendo operações CRUD. Ocasionalmente Vou pegar algo um pouco mais complicado. Assim, esta questão pode ser uma pergunta novato, mas eu estou pronto. Eu apenas fui tentando descobrir isso por horas, e tem sido inútil.
Então, imagine a seguinte estrutura de tabela:
> | ID | Col1 | Col2 | Col3 | .. | Col8 |
Eu quero selecionar ID e uma coluna calculada. A coluna calculada tem um intervalo de 0 - 8 e que contém o número de correspondências para a consulta. Eu também quero restringir o conjunto de resultados para incluir apenas linhas que têm um determinado número de partidas.
Assim, a partir destes dados de amostra:
> | 1 | 'a' | 'b' | 1 | 2 |
> | 2 | 'b' | 'c' | 1 | 2 |
> | 3 | 'b' | 'c' | 4 | 5 |
> | 4 | 'x' | 'x' | 9 | 9 |
Eu quero consulta Col1 = 'a' OR Col2 = 'c' OR Col3 = 1 ou Col4 = 5, onde o resultado calculado> 1 e têm o olhar conjunto de resultados como:
> | ID | Cal |
> | 1 | 2 |
> | 2 | 2 |
> | 3 | 2 |
Eu estou usando T-SQL e SQL Server 2005, se isso importa, e eu não posso mudar o esquema DB.
Eu também prefiro mantê-lo como uma consulta auto-suficiente e não para criar um procedimento armazenado ou tabela temporária.
Solução
Esta resposta vai trabalhar com SQL 2005, usando uma CTE para limpar a tabela derivada um pouco.
WITH Matches AS
(
SELECT ID, CASE WHEN Col1 = 'a' THEN 1 ELSE 0 END +
CASE WHEN Col2 = 'c' THEN 1 ELSE 0 END +
CASE WHEN Col3 = 1 THEN 1 ELSE 0 END +
CASE WHEN Col4 = 5 THEN 1 ELSE 0 END AS Result
FROM Table1
WHERE Col1 = 'a' OR Col2 = 'c' OR Col3 = 1 OR Col4 = 5
)
SELECT ID, Result
FROM Matches
WHERE Result > 1
Outras dicas
Aqui está uma solução que aproveita o fato de que uma comparação boolean retorna os números inteiros 1 ou 0:
SELECT * FROM (
SELECT ID, (Col1='a') + (Col2='c') + (Col3=1) + (Col4=5) AS calculated
FROM MyTable
) q
WHERE calculated > 1;
Note que você tem que entre parênteses as comparações booleanas porque +
tem precedência maior do que =
. Além disso, você tem que colocar tudo em uma subconsulta, porque você normalmente não pode usar um alias de coluna em uma cláusula WHERE
da mesma consulta.
Pode parecer que você também deve usar uma cláusula WHERE
na subconsulta para restringir suas linhas, mas com toda a probabilidade que você vai acabar com um de qualquer maneira varredura completa da tabela, por isso provavelmente não é uma grande vitória. Por outro lado, se você espera que tal restrição seria um muito reduzir o número de linhas no resultado da subconsulta, então seria vale a pena.
O comentário de Re Quassnoi, se você não pode expressões booleanas tratar como valores inteiros, deve haver uma forma de mapear as condições booleano para números inteiros, mesmo que seja um pouco detalhado. Por exemplo:
SELECT * FROM (
SELECT ID,
CASE WHEN Col1='a' THEN 1 ELSE 0 END
+ CASE WHEN Col2='c' THEN 1 ELSE 0 END
+ CASE WHEN Col3=1 THEN 1 ELSE 0 END
+ CASE WHEN Col4=5 THEN 1 ELSE 0 END AS calculated
FROM MyTable
) q
WHERE calculated > 1;
Esta consulta é mais índice amigável:
SELECT id, SUM(match)
FROM (
SELECT id, 1 AS match
FROM mytable
WHERE col1 = 'a'
UNION ALL
SELECT id, 1 AS match
FROM mytable
WHERE col2 = 'c'
UNION ALL
SELECT id, 1 AS match
FROM mytable
WHERE col3 = 1
UNION ALL
SELECT id, 1 AS match
FROM mytable
WHERE col4 = 5
) q
GROUP BY
id
HAVING SUM(match) > 1
Isso só será eficiente se todas as colunas você esta procurando são, primeiro, indexados e, em segundo, tem alta cardinalidade (muitos valores distintos).
Veja este artigo no meu blog para mais detalhes de desempenho: