2009-03-02 6 views
8

J'ai deux tables avec une relation 1: n: "content" et "versioned-content-data" (par exemple, une entité article et toutes les versions créées de cet article) . Je voudrais créer une vue qui affiche la version supérieure de chaque "contenu".Comment convertir une sous-requête SQL en une jointure

Actuellement, j'utiliser cette requête (avec un sous-requête simple requise):

 
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 sous-requête est en fait une requête à la même table qui extrait la version la plus d'un élément spécifique. Notez que les éléments versionnés auront le même fk_idothertable.

Dans SQL Server j'ai essayé de créer une vue indexée de cette requête, mais il semble que je ne suis pas en mesure depuis les sous-requêtes ne sont pas autorisés à vues indexées. Alors ... voici ma question ... Pouvez-vous imaginer un moyen de convertir cette requête en une sorte de requête avec JOINs?

Il semble que les vues indexées ne peuvent pas contenir:

  • sous-requêtes
  • expressions de table commune
  • tables dérivées
  • de HAVING

Je suis désespéré. Toutes les autres idées sont les bienvenues :-)

Merci beaucoup!

+0

est votre sous-requête correcte? Je vois seulement une table référencée –

+0

Oui, est une sous-requête à la même table qui extrait la version max d'un élément qui partage le même fk_idothertable – sachaa

+0

btw ... fk_idothertable est la clé étrangère à la table principale – sachaa

Répondre

13

Ce ne sera probablement pas help si la table est déjà en production mais la bonne façon de modéliser ceci est de faire de version = 0 la version permanente et d'incrémenter toujours la version du matériel OLDER.Ainsi, lorsque vous insérez une nouvelle version que vous dites:

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

Ensuite, cette requête serait juste

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

Aucun sous-requêtes, aucune agrégation MAX. Vous savez toujours quelle est la version actuelle. Vous n'avez jamais besoin de sélectionner max (version) pour insérer le nouvel enregistrement.

+1

Très bonne idée.Merci Joe! – sachaa

+0

Très soigné! –

-1

Comme ça ... Je suppose que le 'mytable' dans la sous-requête était une table réelle différente ... donc je l'ai appelé mytable2. Si c'était la même table alors cela fonctionnera toujours, mais alors j'imagine que fk_idothertable sera juste '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 

Hope this helps

+0

La sous-requête est en fait une requête à la même table qui extrait la version la plus élevée d'un élément spécifique. Notez que les mêmes éléments versionnés auront le même fk_idothertable. – sachaa

0

Je ne sais pas comment cela serait efficace, mais:

 
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

Vous avez besoin d'un groupe par. –

+0

Mais encore ... même avec un GROUP BY cette requête retourne essentiellement tous les éléments de la liste de mytable + la version max de chaque élément. Il ne renvoie pas seulement les éléments avec la version la plus élevée. – sachaa

0

vous pourriez être en mesure de faire le MAX un alias de table qui fait groupe par.

Il pourrait ressembler à ceci:

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

Peut-être quelque chose comme ça?

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) 

Juste une supposition sauvage ...

+0

Merci pour votre réponse. J'ai vraiment aimé l'élégance de votre solution mais quand je l'exécute, j'obtiens: La colonne 'mytable.id' n'est pas valide dans la liste de sélection car elle n'est contenue ni dans une fonction d'agrégat ni dans la clause GROUP BY. D'autres idées? – sachaa

+0

Il suffit d'ajouter tous les champs de SELECT à GROUP BY - AFAIK la norme en a besoin, mais il y a des SGBD qui n'en ont pas besoin. – jpalecek

+0

Si je le fais, chaque fois que le titre change entre les versions, cet objet (avec le même fk_idothertable) apparaîtra deux fois dans les résultats (différent titre + version différente) – sachaa

0

Je pense que FerranB était proche, mais ne pas tout à fait le groupement droit:

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

Merci Mark Cette requête retourne les résultats corrects mais puisqu'elle utilise une "expression de table commune" non valide pour moi afin de créer une vue indexée dans 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). 
Questions connexes