2009-03-02 9 views
8

Tengo dos tablas con una relación 1: n: "contenido" y "contenido-contenido-versionado" (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".Cómo convertir una subconsulta SQL a una combinación

Actualmente uso de esta consulta (con un simple subconsulta):

 
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)))

El sub consulta es en realidad una consulta a la misma mesa 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 que trataron de crear una vista indizada de esta consulta, pero parece que no soy capaz de subconsultas ya no están permitidos en vistas indizadas. Entonces ... esta es 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 común
  • tablas derivadas
  • HAVING

estoy desesperado. Cualquier otra idea es bienvenida :-)

¡Muchas gracias!

+0

es su subconsulta correcta? Solo veo una tabla referenciada –

+0

Sí, es una subconsulta a la misma tabla que extrae la versión máxima de un artículo que comparte el mismo fk_idothertable – sachaa

+0

btw ... fk_idothertable es la clave externa de la tabla maestra – sachaa

Respuesta

13

Esto probablemente no lo hará help si la tabla ya está en producción, pero la forma correcta de modelar esto es hacer que version = 0 sea la versión permanente y siempre incrementar la versión del material OLDER.Por eso, cuando se inserta una nueva versión a la que dice:

UPDATE thetable SET version = version + 1 WHERE id = :id 
INSERT INTO thetable (id, version, title, ...) VALUES (:id, 0, :title, ...) 

Entonces esta consulta sería más que

SELECT id, title, ... FROM thetable WHERE version = 0 

No subconsultas, ninguna agregación MAX. Usted siempre sabe cuál es la versión actual. Nunca tiene que seleccionar max (versión) para insertar el nuevo registro.

+1

idea muy ingeniosa. ¡Gracias Joe! – sachaa

+0

¡Muy ordenado! Bien hecho. –

-1

Me gusta esto ... Supongo que el 'mitabla' en la subconsulta era una tabla real diferente ... así que lo llamé mytable2. Si fuera la misma mesa, esto funcionará, pero entonces me imagino que fk_idothertable será simplemente '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 

Esperanza esto ayuda

+0

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 mismos elementos versionados tendrán el mismo fk_idothertable. – sachaa

0

no sé qué tan eficiente sería, 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 
+0

Necesita un grupo de. –

+0

Pero aun así ... incluso con un GROUP BY, esta consulta básicamente devuelve todos los elementos de la lista de mytable + la versión máxima de cada elemento. No solo devuelve los artículos con la versión más alta. – sachaa

0

Usted puede ser capaz de hacer el máximo de una tabla de alias que hace agrupar.

Podría ser 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 
3

Tal vez 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 conjetura salvaje ...

+0

Gracias por su respuesta. Me gustó mucho la elegancia de su solución, pero cuando la ejecuto obtengo: La columna 'mytable.id' no es válida en la lista de selección porque no está contenida ni en una función de agregado ni en la cláusula GROUP BY. ¿Alguna otra idea? – sachaa

+0

Simplemente agregue todos los campos en SELECCIONAR a GROUP BY - AFAIK el estándar lo necesita, pero hay DBMS que no lo requieren. – jpalecek

+0

Si hago esto, cada vez que el título cambie entre versiones, este elemento (con el mismo fk_idothertable) aparecerá dos veces en los resultados (título diferente + versión diferente) – sachaa

0

Creo FerranB estaba cerca, pero no acababa de tener la agrupación de derecha:

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

+0

Gracias Marcar. Esta consulta devuelve los resultados correctos, pero dado que usa una "expresión de tabla común" es no es válido para mí para crear una vista indexada en SQL Server. – sachaa

0
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). 
Cuestiones relacionadas