sql >> Base de Datos >  >> RDS >> Mysql

Rendimiento de MySQL de VIEW para tablas combinado con UNION ALL

Estoy de acuerdo con todos los puntos de la excelente respuesta de Bill Karwin.

P: ¿Es una práctica normal crear una vista para la consulta de unión discutida y usarla en mis uniones, subselecciones, etc.?

R: Con MySQL, la práctica más normal es evitar el uso de la instrucción "CREATE VIEW".

P: En términos de rendimiento, ¿será peor, igual o mejor en comparación con simplemente insertarlo en uniones, subselecciones, etc.?

R: Hacer referencia a un objeto de vista tendrá el mismo rendimiento que una vista en línea equivalente.

(Puede haber un poquito más de trabajo para buscar el objeto de la vista, verificar los privilegios y luego reemplazar la referencia de la vista con el SQL almacenado, en lugar de enviar una declaración que es solo un poquito más larga. Pero cualquiera de esos las diferencias son insignificantes.)

P: ¿Hay algún inconveniente de tener una vista en este caso?

R: El mayor inconveniente está en cómo MySQL procesa una vista, ya sea almacenada o en línea. MySQL siempre ejecutará la consulta de vista y materializará los resultados de esa consulta como una tabla MyISAM temporal. Pero no hay diferencia si la definición de la vista se almacena o si se incluye en línea. (Otros RDBMS procesan vistas de manera muy diferente a MySQL).

Un gran inconveniente de una vista es que los predicados de la consulta externa NUNCA se insertan en la consulta de la vista. Cada vez que haga referencia a esa vista, incluso con una consulta para un solo valor de identificación, MySQL ejecutará la consulta de vista y creará una tabla MyISAM temporal (sin índices), y LUEGO MySQL ejecutará la consulta externa contra ese temporal. Tabla MyISAM.

Entonces, en términos de rendimiento, piense en una referencia a una vista a la par con "CREATE TEMPORARY TABLE t (cols) ENGINE=MyISAM " y "INSERT INTO t (cols) SELECT ... ".

MySQL en realidad se refiere a una vista en línea como una "tabla derivada", y ese nombre tiene mucho sentido, cuando entendemos qué está haciendo MySQL con ella.

Mi preferencia personal es no usar la instrucción "CREATE VIEW". El mayor inconveniente (tal como lo veo) es que "oculta" el SQL que se está ejecutando. Para el futuro lector, la referencia a la vista parece una tabla. Y luego, cuando vaya a escribir una instrucción SQL, hará referencia a la vista como si fuera una tabla, muy conveniente. Luego decide que va a unir esa tabla a sí misma, con otra referencia a ella. (Para la segunda referencia, MySQL también ejecuta esa consulta nuevamente y crea otra tabla MyISAM temporal (y no indexada). Y ahora hay una operación JOIN en eso. Y luego se agrega un predicado "WHERE view.column ='foo'" en la consulta externa.

Termina "ocultando" la mejora de rendimiento más obvia, deslizando ese predicado en la consulta de vista.

Y luego, alguien llega y decide que va a crear una nueva vista, que hace referencia a la vista anterior. Solo necesita un subconjunto de filas y no puede modificar la vista existente porque eso podría romper algo, por lo que crea una nueva vista... CREATE VIEW myview FROM publicview p WHERE p.col ='foo'.

Y, ahora, una referencia a myview primero ejecutará la consulta publicview, creará una tabla MyISAM temporal, luego la consulta myview se ejecutará contra eso, creando otra tabla MyISAM temporal, contra la cual se ejecutará la consulta externa.

Básicamente, la conveniencia de la vista tiene el potencial de problemas de rendimiento no intencionales. Con la definición de vista disponible en la base de datos para que cualquiera la use, alguien la usará, incluso cuando no sea la solución más adecuada.

Al menos con una vista en línea, la persona que escribe la instrucción SQL es más consciente del SQL real que se está ejecutando, y tener todo ese SQL dispuesto brinda la oportunidad de ajustarlo para mejorar el rendimiento.

Mis dos centavos.

DOMINAR A LA BESTIA SQL

Encuentro que aplicar reglas de formato regulares (que mis herramientas hacen automáticamente) puede convertir un SQL monstruoso en algo que pueda leer y trabajar.

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

Es igualmente probable que evite la vista en línea y use expresiones condicionales en la lista SELECT, aunque esto se vuelve más difícil de manejar para muchas columnas.

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