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.

È stato utile?

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;
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top