Regole generali per la semplificazione di istruzioni SQL
-
21-08-2019 - |
Domanda
Sto cercando alcune "regole di inferenza" (simile al funzionamento del set di regole o le regole di logica) che posso usare per ridurre una query SQL di complessità o dimensione.Esiste qualcosa di simile?Tutte le carte, tutti gli strumenti?Qualsiasi valore che hai trovato sul tuo?È in qualche modo simile per l'ottimizzazione delle query, ma non in termini di prestazioni.
Stato diverso:Avere un (complesso) di query con Join, sub-select, Sindacati, è possibile (o non) a ridurlo a una semplice, equivalente istruzione SQL, che producendo lo stesso risultato, utilizzando alcune regole di trasformazione?
Così, sto cercando equivalente trasformazioni di istruzioni SQL come il fatto che la maggior parte dei sub-select può essere riscritto come un JOIN.
Soluzione
Per dirla diverso: Avere una query (complesso) con JOINs, selezioni, unioni è possibile (o meno) per ridurlo a, un'istruzione SQL equivalente semplice, che produce lo stesso risultato, utilizzando alcune regole di trasformazione ?
E 'esattamente quello ottimizzatori fai per vivere (non che io sto dicendo che da sempre fanno bene).
Dal SQL
è un linguaggio basato insieme, di solito ci sono più di un modo per trasformare una query ad altri.
Ti piace questa query:
SELECT *
FROM mytable
WHERE col1 > @value1 OR col2 < @value2
può essere trasformato in questo modo:
SELECT *
FROM mytable
WHERE col1 > @value1
UNION
SELECT *
FROM mytable
WHERE col2 < @value2
o questo:
SELECT mo.*
FROM (
SELECT id
FROM mytable
WHERE col1 > @value1
UNION
SELECT id
FROM mytable
WHERE col2 < @value2
) mi
JOIN mytable mo
ON mo.id = mi.id
, che sembrano più brutto, ma può produrre migliori piani di esecuzione.
Una delle cose più comuni da fare è sostituire questa query:
SELECT *
FROM mytable
WHERE col IN
(
SELECT othercol
FROM othertable
)
con questo:
SELECT *
FROM mytable mo
WHERE EXISTS
(
SELECT NULL
FROM othertable o
WHERE o.othercol = mo.col
)
In alcuni RDBMS
s '(come PostgreSQL
), DISTINCT
e GROUP BY
utilizzare i diversi piani di esecuzione, in modo a volte è meglio sostituire uno con l'altro:
SELECT mo.grouper,
(
SELECT SUM(col)
FROM mytable mi
WHERE mi.grouper = mo.grouper
)
FROM (
SELECT DISTINCT grouper
FROM mytable
) mo
vs.
SELECT mo.grouper, SUM(col)
FROM mytable
GROUP BY
mo.grouper
In MySQL
, FULL OUTER JOIN
tipi e Oracle
hash.
SQL Server
manca cursors
, in modo che può essere riscritta come folloing:
SELECT t1.col1, t2.col2
FROM table1 t1
LEFT OUTER JOIN
table2 t2
ON t1.id = t2.id
vs.
SELECT t1.col1, t2.col2
FROM table1 t1
LEFT JOIN
table2 t2
ON t1.id = t2.id
UNION ALL
SELECT NULL, t2.col2
FROM table1 t1
RIGHT JOIN
table2 t2
ON t1.id = t2.id
WHERE t1.id IS NULL
, ma si veda questo articolo nel mio blog su come fare questo in modo più efficiente in more efficiently
:
Questa query gerarchica in NESTED LOOPS
:
SELECT DISTINCT(animal_id) AS animal_id
FROM animal
START WITH
animal_id = :id
CONNECT BY
PRIOR animal_id IN (father, mother)
ORDER BY
animal_id
può essere trasformato a questo:
SELECT DISTINCT(animal_id) AS animal_id
FROM (
SELECT 0 AS gender, animal_id, father AS parent
FROM animal
UNION ALL
SELECT 1, animal_id, mother
FROM animal
)
START WITH
animal_id = :id
CONNECT BY
parent = PRIOR animal_id
ORDER BY
animal_id
, quest'ultima essendo più performante.
Si veda questo articolo nel mio blog per i dettagli del piano di esecuzione:
Per trovare tutte le gamme che si sovrappongono l'intervallo dato, è possibile utilizzare la seguente query:
SELECT *
FROM ranges
WHERE end_date >= @start
AND start_date <= @end
, ma in questo 6
più complesse le rese di query stessi risultati più rapidi:
SELECT *
FROM ranges
WHERE (start_date > @start AND start_date <= @end)
OR (@start BETWEEN start_date AND end_date)
, e che ci crediate o no, ho un articolo nel mio blog anche su questo:
<=
manca anche un modo efficace per fare aggregati cumulativi, quindi questa query:
SELECT mi.id, SUM(mo.value) AS running_sum
FROM mytable mi
JOIN mytable mo
ON mo.id <= mi.id
GROUP BY
mi.id
può essere riscritto in modo più efficiente utilizzando, Signore aiutami, cursori (mi avete sentito bene: =
, JOIN
e <=> in una frase).
Si veda questo articolo nel mio blog su come fare:
C'è un certo tipo di interrogazione comunemente incontrati in applicazioni finanziarie che cerca per il tasso effettivo di una moneta, come questo in <=>:
SELECT TO_CHAR(SUM(xac_amount * rte_rate), 'FM999G999G999G999G999G999D999999')
FROM t_transaction x
JOIN t_rate r
ON (rte_currency, rte_date) IN
(
SELECT xac_currency, MAX(rte_date)
FROM t_rate
WHERE rte_currency = xac_currency
AND rte_date <= xac_date
)
Questa query può essere pesantemente riscritto per utilizzare una condizione di uguaglianza che permette un <=> invece di <=>:
WITH v_rate AS
(
SELECT cur_id AS eff_currency, dte_date AS eff_date, rte_rate AS eff_rate
FROM (
SELECT cur_id, dte_date,
(
SELECT MAX(rte_date)
FROM t_rate ri
WHERE rte_currency = cur_id
AND rte_date <= dte_date
) AS rte_effdate
FROM (
SELECT (
SELECT MAX(rte_date)
FROM t_rate
) - level + 1 AS dte_date
FROM dual
CONNECT BY
level <=
(
SELECT MAX(rte_date) - MIN(rte_date)
FROM t_rate
)
) v_date,
(
SELECT 1 AS cur_id
FROM dual
UNION ALL
SELECT 2 AS cur_id
FROM dual
) v_currency
) v_eff
LEFT JOIN
t_rate
ON rte_currency = cur_id
AND rte_date = rte_effdate
)
SELECT TO_CHAR(SUM(xac_amount * eff_rate), 'FM999G999G999G999G999G999D999999')
FROM (
SELECT xac_currency, TRUNC(xac_date) AS xac_date, SUM(xac_amount) AS xac_amount, COUNT(*) AS cnt
FROM t_transaction x
GROUP BY
xac_currency, TRUNC(xac_date)
)
JOIN v_rate
ON eff_currency = xac_currency
AND eff_date = xac_date
Nonostante sia ingombrante come un inferno, la seconda interrogazione è <=> volte più veloce.
L'idea principale qui sostituisce <=> con <=>, che richiede la costruzione di un calendario tavolo in memoria. a <=> con.
Altri suggerimenti
Ecco alcuni di lavorare con Oracle 8 e 9 (naturalmente, a volte facendo l'opposto potrebbe rendere la query più semplice o più veloce):
parentesi possono essere rimossi se non sono utilizzati per sostituire precedenza degli operatori. Un semplice esempio è quando tutti gli operatori booleani nel where
clausola sono gli stessi: where ((a or b) or c)
è equivalente a where a or b or c
Un sub-query può spesso (se non sempre) essere si è fusa con l'interrogazione principale per semplificarlo. Nella mia esperienza, questo spesso migliora notevolmente le prestazioni:
select foo.a,
bar.a
from foomatic foo,
bartastic bar
where foo.id = bar.id and
bar.id = (
select ban.id
from bantabulous ban
where ban.bandana = 42
)
;
è equivalente a
select foo.a,
bar.a
from foomatic foo,
bartastic bar,
bantabulous ban
where foo.id = bar.id and
bar.id = ban.id and
ban.bandana = 42
;
Utilizzando ANSI unisce separa un sacco di logica "Code Monkey" dalle parti veramente interessanti della clausola where: La query precedente è equivalente a
select foo.a,
bar.a
from foomatic foo
join bartastic bar on bar.id = foo.id
join bantabulous ban on ban.id = bar.id
where ban.bandana = 42
;
Se si vuole verificare l'esistenza di una riga, non usare count (*) , anziché utilizzare uno rownum = 1
o mettere la query in una clausola where exists
per andare a prendere solo una fila invece di tutti.
- Suppongo che il più ovvio è quello di cercare tutti i Cursori, che può essere sostituito con un SQL 'Set' a base di operazione.
- Il prossimo sulla mia lista, è cercare eventuali correlati sub-query che possono essere riscritte come un-correlato query
- Nel lungo stored procedure, rompere separato SQL in loro stored procedure.Che modo si possa arrivare proprio nella cache del piano di query.
- Cerca le operazioni che possono avere il loro campo di applicazione ridotto.Ho regolarmente a trovare le istruzioni all'interno di una transazione che può tranquillamente essere al di fuori.
- Sub-seleziona, spesso, può essere riscritta come dritto in avanti join (moderno ottimizzatori sono bravi a cogliere le più semplici)
Come @Quassnoi accennato, l'Ottimizzatore spesso fa un buon lavoro.Un modo per aiutare è quello di garantire gli indici e le statistiche sono aggiornato, e gli indici appropriati esiste per il carico di lavoro della query.
mi piace di sostituire tutti i tipi di selezione secondaria per unirsi query.
Questo è ovvio:
SELECT *
FROM mytable mo
WHERE EXISTS
(
SELECT *
FROM othertable o
WHERE o.othercol = mo.col
)
da
SELECT mo.*
FROM mytable mo inner join othertable o on o.othercol = mo.col
E questo è in fase di stima:
SELECT *
FROM mytable mo
WHERE NOT EXISTS
(
SELECT *
FROM othertable o
WHERE o.othercol = mo.col
)
da
SELECT mo.*
FROM mytable mo left outer join othertable o on o.othercol = mo.col
WHERE o.othercol is null
Potrebbe aiutare i DBMS di scegliere il bene piano di esecuzione in una grande richiesta.
Mi piace tutti su una squadra per seguire una serie di norme per rendere il codice leggibile, gestibile, comprensibile, lavabile, ecc ..:)
- tutti usano lo stesso alias
- non cursori. nessun cicli
- perché anche pensare IN quando è possibile ESISTE
- TRATTINO
- La coerenza nella codifica stile
c'è un po 'di roba qui Quali sono alcuni dei vostri più utili gli standard di database?
Data la natura di SQL, è assolutamente necessario essere consapevoli delle implicazioni sulle prestazioni di qualsiasi refactoring. Rifattorizzare applicazioni SQL è una buona risorsa su refactoring con una forte enfasi sulle prestazioni ( vedere il Capitolo 5).
Anche se la semplificazione non può uguale ottimizzazione, semplificazione può essere importante per la scrittura di codice leggibile SQL, che è a sua volta fondamentale per essere in grado di controllare il codice SQL per concettuale correttezza (non correttezza sintattica, che il vostro ambiente di sviluppo dovrebbe controllare per voi) . Mi sembra che in un mondo ideale, vorremmo scrivere il codice più semplice SQL leggibile e quindi l'ottimizzatore potrebbe riscrivere il codice SQL per essere in qualsiasi forma (forse più verboso) correrebbe il più veloce.
Ho trovato che il pensiero delle istruzioni SQL come basato sulla logica set è molto utile, soprattutto se ho bisogno di combinare in cui le clausole o capire una negazione complessa di una clausola dove. Io uso le di Algebra di Boole in questo caso.
I più importanti per la semplificazione di una clausola in cui sono probabilmente le leggi di De Morgan (notare che "·" è "E" e "+" è "OR"):
- non (x · y) = x + NON NON y
- non (x + y) = NON x · NON y
Questo si traduce in SQL per:
NOT (expr1 AND expr2) -> NOT expr1 OR NOT expr2
NOT (expr1 OR expr2) -> NOT expr1 AND NOT expr2
Queste leggi possono essere molto utile per semplificare in cui le clausole con un sacco di annidati AND
e OR
parti.
E 'anche utile ricordare che la dichiarazione field1 IN (value1, value2, ...)
è equivalente a field1 = value1 OR field1 = value2 OR ...
. Ciò consente di negare la IN ()
uno dei due modi:
NOT field1 IN (value1, value2) -- for longer lists
NOT field1 = value1 AND NOT field1 = value2 -- for shorter lists
Un sub-query può essere pensato in questo modo anche. Ad esempio, questa negato clausola dove:
NOT (table1.field1 = value1 AND EXISTS (SELECT * FROM table2 WHERE table1.field1 = table2.field2))
può essere riscritto come:
NOT table1.field1 = value1 OR NOT EXISTS (SELECT * FROM table2 WHERE table1.field1 = table2.field2))
Queste leggi non vi dicono come trasformare una query SQL utilizzando una subquery in uno utilizzando un join, ma la logica booleana può aiutare a capire uniscono i tipi e ciò che la vostra query deve essere tornando. Ad esempio, con tavoli A
e B
, un INNER JOIN
è come A AND B
, un LEFT OUTER JOIN
è come (A AND NOT B) OR (A AND B)
che semplifica al A OR (A AND B)
, ed un FULL OUTER JOIN
è A OR (A AND B) OR B
che semplifica a A OR B
.
Il mio approccio è quello di imparare la teoria relazionale in algebra relazionale generale e in particolare. Poi imparare a individuare i costrutti utilizzati in SQL per implementare operatori del algebra relazionale (ad esempio universale quantificazione divisione pseudonimo) e di calcolo (per esempio quantificazione esistenziale). Il punto è che SQL ha caratteristiche non presenti nel modello relazionale esempio null, che sono probabilmente meglio refactoring via comunque. Letture consigliate: SQL e Teoria Relazionale: come scrivere SQL Accurate Codice da CJ Data .
In questo senso, non sono convinto "il fatto che la maggior parte subselect possono essere riscritti come JOIN" rappresenta una semplificazione.
Prendete questa query per esempio:
SELECT c
FROM T1
WHERE c NOT IN ( SELECT c FROM T2 );
Riscrivere utilizzando JOIN
SELECT DISTINCT T1.c
FROM T1 NATURAL LEFT OUTER JOIN T2
WHERE T2.c IS NULL;
Il join è più prolisso!
In alternativa, riconosce il costrutto attua un antijoin sulla proiezione di c
esempio pseudo algrbra
T1 { c } antijoin T2 { c }
La semplificazione utilizzando gli operatori relazionali:
SELECT c FROM T1 EXCEPT SELECT c FROM T2;