Mysql
 sql >> Database >  >> RDS >> Mysql

Prestazioni MySQL di VIEW per tabelle combinate con UNION ALL

Concordo con tutti i punti dell'eccellente risposta di Bill Karwin.

D: È pratica normale creare una vista per la query di unione discussa e usarla nei miei join, sottoselezioni ecc?

R: Con MySQL la pratica più normale è evitare di usare l'istruzione "CREATE VIEW".

D: In termini di prestazioni, sarà peggiore, uguale o migliore rispetto al semplice inserimento in join, sottoselezioni ecc?

R: Fare riferimento a un oggetto vista avrà le stesse prestazioni di una vista in linea equivalente.

(Potrebbe esserci un po' di lavoro in più per cercare l'oggetto della vista, controllare i privilegi e quindi sostituire il riferimento alla vista con l'SQL archiviato, anziché inviare un'istruzione che è solo un po' più lunga. Ma nessuno di questi le differenze sono insignificanti.)

D: Ci sono degli svantaggi nell'avere una vista in questo caso?

R: Il più grande svantaggio è nel modo in cui MySQL elabora una vista, sia che sia archiviata o inline. MySQL eseguirà sempre la query di visualizzazione e materializzerà i risultati di tale query come tabella MyISAM temporanea. Ma non c'è differenza se la definizione della vista è archiviata o se è inclusa in linea. (Altri RDBMS elaborano viste in modo molto diverso da MySQL).

Un grande svantaggio di una vista è che i predicati della query esterna NON vengono MAI inseriti nella query della vista. Ogni volta che fai riferimento a quella vista, anche con una query per un singolo valore id, MySQL eseguirà la query di visualizzazione e creerà una tabella MyISAM temporanea (senza indici su di essa) e ALLORA MySQL eseguirà la query esterna su quella temporanea La mia tabella ISAM.

Quindi, in termini di prestazioni, pensa a un riferimento a una vista alla pari con "CREATE TEMPORARY TABLE t (cols) ENGINE=MyISAM " e "INSERT INTO t (cols) SELECT ... ".

MySQL in realtà si riferisce a una vista in linea come a una "tabella derivata" e quel nome ha molto senso, quando capiamo cosa sta facendo MySQL con essa.

La mia preferenza personale è di non utilizzare l'istruzione "CREATE VIEW". Il più grande svantaggio (come lo vedo io) è che "nasconde" l'SQL che viene eseguito. Per il futuro lettore, il riferimento alla vista appare come una tabella. E poi, quando scriverà un'istruzione SQL, farà riferimento alla vista come se fosse una tabella, quindi molto conveniente. Quindi decide che unirà quel tavolo a se stesso, con un altro riferimento ad esso. (Per il secondo riferimento, anche MySQL esegue di nuovo quella query e crea un'altra tabella MyISAM temporanea (e non indicizzata). E ora c'è un'operazione JOIN su questo. E poi viene aggiunto un predicato "WHERE view.column ='foo'" sulla query esterna.

Finisce per "nascondere" il miglioramento delle prestazioni più ovvio, facendo scorrere quel predicato nella query di visualizzazione.

E poi, qualcuno arriva e decide che creerà una nuova vista, che fa riferimento alla vecchia vista. Ha solo bisogno di un sottoinsieme di righe e non può modificare la vista esistente perché potrebbe interrompere qualcosa, quindi crea una nuova vista... CREATE VIEW myview FROM publicview p WHERE p.col ='foo'.

E, ora, un riferimento a myview eseguirà prima la query publicview, creerà una tabella MyISAM temporanea, quindi la query myview verrà eseguita su quella, creando un'altra tabella MyISAM temporanea, su cui verrà eseguita la query esterna.

Fondamentalmente, la comodità della vista ha il potenziale per problemi di prestazioni non intenzionali. Con la definizione della vista disponibile sul database che chiunque può utilizzarla, qualcuno la utilizzerà, anche dove non è la soluzione più appropriata.

Almeno con una vista in linea, la persona che scrive l'istruzione SQL è più consapevole dell'effettiva esecuzione dell'SQL e avere tutto quell'SQL disposto offre l'opportunità di modificarlo per le prestazioni.

I miei due centesimi.

ADDOMARE L'SQL BEASTLY

Trovo che l'applicazione di regole di formattazione regolari (che i miei strumenti eseguono automaticamente) possa trasformare un SQL mostruoso in qualcosa con cui posso leggere e lavorare.

SELECT row.col1
     , row.col2
     , person.*
  FROM some_table row
  LEFT
  JOIN ( SELECT 'person'  AS `person_type`
              , p.id      AS `id`
              , CONCAT(p.first_name,' ',p.surname) AS `name`
           FROM person p
          UNION ALL
         SELECT 'company' AS `person_type`
              , c.id      AS `id`
              , c.name    AS `name`
           FROM company c
       ) person
    ON person.id = row.person_id
   AND person.person_type = row.person_type

Sarebbe altrettanto probabile che eviterei del tutto la visualizzazione in linea e userei espressioni condizionali nell'elenco SELECT, anche se questo diventa più ingombrante per molte colonne.

SELECT row.col1
     , row.col2
     , row.person_type AS ref_person_type
     , row.person_id   AS ref_person_id
     , CASE
       WHEN row.person_type = 'person'  THEN p.id 
       WHEN row.person_type = 'company' THEN c.id
       END AS `person_id`
     , CASE
       WHEN row.person_type = 'person'  THEN CONCAT(p.first_name,' ',p.surname)
       WHEN row.person_type = 'company' THEN c.name
       END AS `name`
  FROM some_table row
  LEFT
  JOIN person p
    ON row.person_type = 'person'
   AND p.id = row.person_id
  LEFT
  JOIN company c
    ON row.person_type = 'company'
   AND c.id = row.person_id