Come convertire una sottoquery SQL in un join
-
03-07-2019 - |
Domanda
Ho due tabelle con una relazione 1: n: " content " e "quoted-content-data "" (ad esempio un'entità articolo e tutte le versioni create di quell'articolo). Vorrei creare una vista che mostri la versione superiore di ogni "contenuto".
Attualmente utilizzo questa query (con una semplice subquery):
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 subquery è in realtà una query nella stessa tabella che estrae la versione più alta di un elemento specifico. Si noti che gli elementi con versione avranno la stessa fk_idothertable.
In SQL Server ho provato a creare una vista indicizzata di questa query ma sembra che io non sia in grado poiché le sottoquery non sono consentite nelle viste indicizzate . Quindi ... ecco la mia domanda ... Riesci a pensare a un modo per convertire questa query in una sorta di query con JOIN?
Sembra che le viste indicizzate non possano contenere:
- subqueries
- espressioni di tabella comuni
- tabelle derivate
- Clausole HAVING
Sono disperato. Altre idee sono benvenute :-)
Grazie mille!
Soluzione
Questo probabilmente non aiuterà se la tabella è già in produzione ma il modo giusto per modellarlo è rendere version = 0 la versione permanente e incrementare sempre la versione del materiale OLDER. Quindi, quando si inserisce una nuova versione, si direbbe:
UPDATE thetable SET version = version + 1 WHERE id = :id
INSERT INTO thetable (id, version, title, ...) VALUES (:id, 0, :title, ...)
Quindi questa query sarebbe solo
SELECT id, title, ... FROM thetable WHERE version = 0
Nessuna subquery, nessuna aggregazione MAX. Sai sempre qual è la versione attuale. Non devi mai selezionare max (versione) per inserire il nuovo record.
Altri suggerimenti
Forse qualcosa del genere?
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 un'ipotesi selvaggia ...
Potresti essere in grado di trasformare MAX in un alias di tabella che raggruppa per
Potrebbe assomigliare a questo:
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
Penso che FerranB fosse vicino ma non aveva il giusto raggruppamento:
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).
Non so quanto sia efficiente, 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
In questo modo ... Suppongo che il 'mytable' nella sottoquery fosse una tabella effettiva diversa ... così l'ho chiamato mytable2. Se fosse la stessa tabella allora funzionerà comunque, ma immagino che fk_idothertable sarà semplicemente "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