Как преобразовать подзапрос SQL в объединение - PullRequest
8 голосов
/ 02 марта 2009

У меня есть две таблицы с отношением 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 пункты

Я в отчаянии. Любые другие идеи приветствуются: -)

Большое спасибо!

Ответы [ 7 ]

13 голосов
/ 02 марта 2009

Это, вероятно, не поможет, если таблица уже находится в производстве, но правильный способ моделирования - сделать версию = 0 постоянной версией и всегда увеличивать версию материала OLDER. Поэтому, когда вы вставляете новую версию, вы говорите:

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 (версию), чтобы вставить новую запись.

3 голосов
/ 02 марта 2009

Может как то так?

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)

Просто дикая догадка ...

0 голосов
/ 23 мая 2012
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).
0 голосов
/ 02 марта 2009

Я думаю, что 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);

M

0 голосов
/ 02 марта 2009

Вы могли бы сделать 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
0 голосов
/ 02 марта 2009

Я не знаю, насколько это будет эффективно, но:

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 голосов
/ 02 марта 2009

Вот так ... Я предполагаю, что '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

Надеюсь, это поможет

...