Selezionare con colonna calcolata che dipende una correlazione
-
18-09-2019 - |
Domanda
Non faccio un sacco di SQL, e la maggior parte del tempo, sto facendo operazioni CRUD. Di tanto in tanto vado a prendere qualcosa di un po 'più complicato. Quindi, questa domanda può essere una domanda newbie, ma io sono pronto. Ho appena cercato di capire questo fuori per ore, ed è stato inutile.
Quindi, Immaginate la seguente struttura della tabella:
> | ID | Col1 | Col2 | Col3 | .. | Col8 |
voglio selezionare ID e una colonna calcolata. La colonna calcolata ha una gamma di 0-8 e contiene il numero di partite alla query. Voglio anche per limitare il set di risultati per includere solo le righe che hanno un certo numero di partite.
Quindi, da questi dati di esempio:
> | 1 | 'a' | 'b' | 1 | 2 |
> | 2 | 'b' | 'c' | 1 | 2 |
> | 3 | 'b' | 'c' | 4 | 5 |
> | 4 | 'x' | 'x' | 9 | 9 |
Voglio interrogare sulla Col1 = 'A' o Col2 = 'C' o Col3 = 1 o Col4 = 5 dove il risultato calcolato> 1 e avere il set aspetto risultato come:
> | ID | Cal |
> | 1 | 2 |
> | 2 | 2 |
> | 3 | 2 |
Sto utilizzando T-SQL e SQL Server 2005, se è importante, e non posso cambiare il DB Schema.
Mi piacerebbe anche preferisco tenerlo come una query autonomo e non c'è bisogno di creare una stored procedure o una tabella temporanea.
Soluzione
Questa risposta funzionerà con SQL 2005, utilizzando un CTE per ripulire la tabella derivata un po '.
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
Altri suggerimenti
Ecco una soluzione che sfrutta il fatto che un confronto booleana restituisce i numeri interi 1 o 0:
SELECT * FROM (
SELECT ID, (Col1='a') + (Col2='c') + (Col3=1) + (Col4=5) AS calculated
FROM MyTable
) q
WHERE calculated > 1;
Si noti che si deve mettere tra parentesi i confronti booleane perché +
ha precedenza maggiore rispetto =
. Inoltre, bisogna mettere tutto in una sottoquery, perché normalmente non è possibile utilizzare un alias di colonna in una clausola WHERE
della stessa query.
Potrebbe sembrare si dovrebbe anche usare una clausola WHERE
nella subquery per limitare le sue file, ma con ogni probabilità si sta andando a finire con un tavolo pieno scansione comunque, quindi probabilmente non è una grande vittoria. D'altra parte, se si prevede che tale restrizione sarebbe molto di ridurre il numero di righe nel risultato subquery, quindi sarebbe utile.
Il commento di Re Quassnoi, se non è possibile trattare le espressioni booleane come valori interi, ci dovrebbe essere un modo per mappare le condizioni booleane a numeri interi, anche se è un po 'prolisso. Ad esempio:
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;
Questa query è più indice di amichevole:
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
Questo sarà solo efficace se tutti le colonne che si sta cercando per sono, in primo luogo, indicizzato e, dall'altro, hanno un alto cardinalità (molti valori distinti).
Si veda questo articolo nel mio blog per i dettagli delle prestazioni: