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.

Foi útil?

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:

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top