Como converter uma subconsulta SQL para uma junção
-
03-07-2019 - |
Pergunta
Eu tenho duas tabelas com uma relação 1: n: "conteúdo" e "-content versionadas-data" (por exemplo, uma entidade artigo e todas as versões criadas do mesmo artigo). Eu gostaria de criar uma exibição que exibe a versão topo de cada "conteúdo".
Atualmente eu usar essa consulta (com uma subconsulta simples):
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)))
A subconsulta é realmente uma consulta para a mesma mesa que extrai a versão mais alto de um item específico. Observe que os itens de versão terá o mesmo fk_idothertable.
No SQL Server eu tentei criar uma visão indexada desta consulta, mas parece que eu não sou capaz desde subconsultas não são permitidos em exibições indexadas . Então ... aqui está a minha pergunta ... Você pode pensar em uma maneira de converter essa consulta para algum tipo de consulta com junções?
Parece que exibições indexadas não pode conter:
- subqueries
- expressões de tabela comum
- tabelas derivadas
- TENDO cláusulas
Estou desesperada. Quaisquer outras ideias são bem-vindas: -)
Muito obrigado!
Solução
Isso provavelmente não vai ajudar se a tabela já está em produção, mas o caminho certo para modelar isto é fazer version = 0 a versão permanente e sempre incrementar a versão do material mais antigo. Então, quando você inserir uma nova versão que você diria:
UPDATE thetable SET version = version + 1 WHERE id = :id
INSERT INTO thetable (id, version, title, ...) VALUES (:id, 0, :title, ...)
Em seguida, essa consulta seria apenas
SELECT id, title, ... FROM thetable WHERE version = 0
Não subqueries, nenhuma agregação MAX. Você sempre sabe o que a versão atual. Você nunca tem que selecionar max (versão), a fim de inserir o novo registro.
Outras dicas
Talvez algo assim?
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)
Apenas um palpite ...
Você pode ser capaz de fazer o MAX uma mesa alias que faz GROUP BY.
Pode parecer algo como isto:
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
Eu acho FerranB estava perto, mas não chegou a ter o agrupamento de direita:
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).
Eu não sei o quão eficiente este seria, mas:
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
Assim ... Presumo que o 'mytable' na subconsulta foi uma tabela real diferente ... então eu o chamei mytable2. Se fosse a mesma tabela, então este será ainda trabalho, mas então eu imagino que fk_idothertable será apenas '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 isso ajude