2009-03-02 10 views
8

Ho due tabelle con una relazione 1: n: "contenuto" e "versione-contenuto-dati" (ad esempio, un'entità articolo e tutte le versioni create di tale articolo) . Vorrei creare una vista che mostri la versione principale di ogni "contenuto".Come convertire una sottoquery SQL in un join

Attualmente io uso questa query (con una semplice sottoquery):

 
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 sottoquery è in realtà una query per la stessa tabella che estrae la versione più alto di un elemento specifico. Si noti che gli articoli con versione avranno lo stesso fk_idothertable.

In SQL Server Ho cercato di creare un indicizzato vista di questa query, ma sembra che io non sono in grado in quanto sottoquery non sono ammessi nella viste indicizzate. Quindi ... ecco la mia domanda ... Riesci a pensare a un modo per convertire questa query in una sorta di query con JOINs?

Sembra viste indicizzate non possono contenere:

  • subqueries
  • espressioni di tabella comuni
  • tabelle derivate
  • clausole con

Sono disperata. Altre idee sono benvenute :-)

Grazie mille!

+0

è la subquery corretta? Vedo solo una tabella referenziata –

+0

Sì, è una sottoquery della stessa tabella che estrae la versione massima di un elemento che condivide la stessa fk_idothertable – sachaa

+0

btw ... fk_idothertable è la chiave esterna della tabella master – sachaa

risposta

13

Questo probabilmente non lo farà aiuto se la tabella è già in produzione, ma il modo giusto per modellarlo è rendere la versione = 0 la versione permanente e incrementare sempre la versione del materiale OLDER.Così, quando si inserisce una nuova versione direste:

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

Poi questa query sarebbe solo

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

Nessun subqueries, alcuna aggregazione MAX. Sai sempre qual è la versione corrente. Non devi mai selezionare max (versione) per inserire il nuovo record.

+1

Idea molto intelligente. Grazie Joe! – sachaa

+0

Molto bello! Ben fatto. –

-1

In questo modo ... presumo che il "mytable" nella sottoquery fosse un diverso tavolo effettivo ... quindi l'ho chiamato mytable2. Se fosse la stessa tabella, funzionerebbe ancora, ma immagino che fk_idothertable sarà solo '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 

Spero che questo aiuti

+0

La sottoquery è in realtà una query alla stessa tabella che estrae la versione più alta di un elemento specifico. Si noti che gli stessi articoli con versione avranno lo stesso fk_idothertable. – sachaa

0

non so quanto sia efficiente questo sarebbe, ma:

 
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

Hai bisogno di un gruppo di. –

+0

Ma ancora ... anche con un GROUP BY questa query restituisce fondamentalmente tutti gli elementi della lista da mytable + la versione massima di ciascun elemento. Non restituisce solo gli articoli con la versione più alta. – sachaa

0

si potrebbe essere in grado di rendere il MAX un alias di tabella che fa il raggruppamento.

Potrebbe sembrare qualcosa di simile:

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

Forse qualcosa di simile?

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) 

Solo una supposizione selvaggia ...

+0

Grazie per la risposta. Mi è piaciuta molto l'eleganza della tua soluzione, ma quando la eseguo ottengo: La colonna 'mytable.id' non è valida nell'elenco di selezione perché non è contenuta in una funzione di aggregazione o nella clausola GROUP BY. Altre idee? – sachaa

+0

Basta aggiungere tutti i campi in SELECT a GROUP BY - AFAIK lo standard ha bisogno, ma ci sono DBMS che non lo richiedono. – jpalecek

+0

Se faccio questo, ogni volta che il titolo cambia tra le versioni questo elemento (con lo stesso fk_idothertable) verrà aggiunto due volte nei risultati (titolo diverso + versione diversa) – sachaa

0

Penso FerranB era vicino ma non abbastanza hanno il raggruppamento destra:

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

Grazie Mark. Questa query restituisce i risultati corretti ma poiché utilizza una "espressione di tabella comune" è non valido per me per creare una vista indicizzata in 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). 
Problemi correlati