Как преобразовать подзапрос SQL в соединение
-
03-07-2019 - |
Вопрос
У меня есть две таблицы с отношением 1:n:«content» и «versioned-content-data» (например, объект статьи и все версии, созданные для этой статьи).Я хотел бы создать представление, которое отображает верхнюю версию каждого «контента».
В настоящее время я использую этот запрос (с простым подзапросом):
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)))
Подзапрос на самом деле представляет собой запрос к той же таблице, который извлекает самую старшую версию определенного элемента.Обратите внимание, что элементы с версиями будут иметь одну и ту же таблицу fk_idothertable.
В SQL Server я пытался создать индексированное представление этого запроса, но, похоже, я не могу, поскольку подзапросы не разрешены индексированные просмотры.Так...вот мой вопрос...Можете ли вы придумать способ преобразовать этот запрос в какой-то запрос с помощью JOIN?
Кажется, что индексированные представления не могут содержать:
- подзапросы
- общие табличные выражения
- производные таблицы
- HAVING предложения
Я в отчаянии.Любые другие идеи приветствуются :-)
Большое спасибо!
Решение
Вероятно, это не поможет, если таблица уже находится в производстве, но правильный способ смоделировать это — сделать версию = 0 постоянной версией и всегда увеличивать версию СТАРОГО материала.Поэтому, когда вы вставляете новую версию, вы говорите:
UPDATE thetable SET version = version + 1 WHERE id = :id
INSERT INTO thetable (id, version, title, ...) VALUES (:id, 0, :title, ...)
Тогда этот запрос будет просто
SELECT id, title, ... FROM thetable WHERE version = 0
Никаких подзапросов, никакой агрегации MAX.Вы всегда знаете, какая текущая версия.Вам никогда не придется выбирать max(version), чтобы вставить новую запись.
Другие советы
Может быть, что-то вроде этого?
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)
Просто дикая догадка...
Возможно, вы сможете сделать MAX псевдонимом таблицы, который группируется.
Это может выглядеть примерно так:
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
Я думаю, что FerranB был близок к этому, но не совсем правильно сгруппировал:
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);
М
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).
Не знаю, насколько это будет эффективно, но:
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
Вот так... Я предполагаю, что "mytable" в подзапросе была другой реальной таблицей... поэтому я назвал ее mytable2.Если это была та же таблица, это все равно будет работать, но тогда я думаю, что fk_idothertable будет просто «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
Надеюсь это поможет