Cómo convertir una subconsulta SQL a una unión
-
03-07-2019 - |
Pregunta
Tengo dos tablas con una relación 1: n: " contenido " y " datos de contenido versionados " (por ejemplo, una entidad de artículo y todas las versiones creadas de ese artículo). Me gustaría crear una vista que muestre la versión superior de cada " contenido " ;.
Actualmente utilizo esta consulta (con una subconsulta simple):
SELECT t1.id, t1.title, t1.contenttext, t1.fk_idothertable t1.version FROM mytable as t1 WHERE (version = (SELECT MAX(version) AS topversion FROM mytable WHERE (fk_idothertable = t1.fk_idothertable)))
La subconsulta es en realidad una consulta a la misma tabla que extrae la versión más alta de un elemento específico. Tenga en cuenta que los elementos versionados tendrán el mismo fk_idothertable.
En SQL Server traté de crear una vista indizada de esta consulta, pero parece que no puedo, ya que las subconsultas no están permitidas en vistas indizadas . Entonces ... aquí está mi pregunta ... ¿Puedes pensar en una forma de convertir esta consulta en algún tipo de consulta con JOINs?
Parece que las vistas indizadas no pueden contener:
- subconsultas
- expresiones de tabla comunes
- tablas derivadas
- CLAVES QUE TIENEN
Estoy desesperado. Cualquier otra idea es bienvenida :-)
¡Muchas gracias!
Solución
Esto probablemente no ayudará si la tabla ya está en producción, pero la forma correcta de modelar esto es hacer que la versión = 0 sea la versión permanente y siempre incrementar la versión del material OLDER. Entonces, cuando insertas una nueva versión, dirías:
UPDATE thetable SET version = version + 1 WHERE id = :id
INSERT INTO thetable (id, version, title, ...) VALUES (:id, 0, :title, ...)
Entonces esta consulta simplemente sería
SELECT id, title, ... FROM thetable WHERE version = 0
No hay subconsultas, no hay agregación MAX. Siempre sabes cuál es la versión actual. Nunca tiene que seleccionar max (versión) para insertar el nuevo registro.
Otros consejos
¿Quizás algo como esto?
SELECT
t2.id,
t2.title,
t2.contenttext,
t2.fk_idothertable,
t2.version
FROM mytable t1, mytable t2
WHERE t1.fk_idothertable == t2.fk_idothertable
GROUP BY t2.fk_idothertable, t2.version
HAVING t2.version=MAX(t1.version)
Sólo una suposición salvaje ...
Es posible que puedas hacer de MAX un alias de tabla que se agrupe por.
Podría verse algo como esto:
SELECT
t1.id,
t1.title,
t1.contenttext,
t1.fk_idothertable
t1.version
FROM mytable as t1 JOIN
(SELECT fk_idothertable, MAX(version) AS topversion
FROM mytable
GROUP BY fk_idothertable) as t2
ON t1.version = t2.topversion
Creo que FerranB estaba cerca pero no tenía el derecho de agrupación:
with
latest_versions as (
select
max(version) as latest_version,
fk_idothertable
from
mytable
group by
fk_idothertable
)
select
t1.id,
t1.title,
t1.contenttext,
t1.fk_idothertable,
t1.version
from
mytable as t1
join latest_versions on (t1.version = latest_versions.latest_version
and t1.fk_idothertable = latest_versions.fk_idothertable);
M
If SQL Server accepts LIMIT clause, I think the following should work:
SELECT
t1.id,
t1.title,
t1.contenttext,
t1.fk_idothertable
t1.version
FROM mytable as t1 ordery by t1.version DESC LIMIT 1;
(DESC - For descending sort; LIMIT 1 chooses only the first row and
DBMS usually does good optimization on seeing LIMIT).
No sé cuán eficiente sería esto, pero:
SELECT t1.*, t2.version FROM mytable AS t1 JOIN ( SElECT mytable.fk_idothertable, MAX(mytable.version) AS version FROM mytable ) t2 ON t1.fk_idothertable = t2.fk_idothertable
De esta manera ... Supongo que el 'mytable' en la subconsulta era una tabla real diferente ... así que lo llamé mytable2. Si fuera la misma tabla, esto seguirá funcionando, pero luego imagino que fk_idothertable simplemente será 'id'.
SELECT
t1.id,
t1.title,
t1.contenttext,
t1.fk_idothertable
t1.version
FROM mytable as t1
INNER JOIN (SELECT MAX(Version) AS topversion,fk_idothertable FROM mytable2 GROUP BY fk_idothertable) t2
ON t1.id = t2.fk_idothertable AND t1.version = t2.topversion
Espero que esto ayude