9

I have two tables with a 1:n relationship: "content" and "versioned-content-data" (for example, an article entity and all the versions created of that article). I would like to create a view that displays the top version of each "content".

Currently I use this query (with a simple 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)))

The subquery is actually a query to the same table that extracts the highest version of a specific item. Notice that the versioned items will have the same fk_idothertable.

In SQL Server I tried to create an indexed view of this query but it seems I'm not able since subqueries are not allowed in indexed views. So... here's my question... Can you think of a way to convert this query to some sort of query with JOINs?

It seems like indexed views cannot contain:

  • subqueries
  • common table expressions
  • derived tables
  • HAVING clauses

I'm desperate. Any other ideas are welcome :-)

Thanks a lot!

SkyWalker
  • 28,384
  • 14
  • 74
  • 132
sachaa
  • 1,977
  • 3
  • 18
  • 26

7 Answers7

14

This probably won't help if table is already in production but the right way to model this is to make version = 0 the permanent version and always increment the version of OLDER material. So when you insert a new version you would say:

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

Then this query would just be

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

No subqueries, no MAX aggregation. You always know what the current version is. You never have to select max(version) in order to insert the new record.

SkyWalker
  • 28,384
  • 14
  • 74
  • 132
jmucchiello
  • 18,754
  • 7
  • 41
  • 61
3

Maybe something like this?

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)

Just a wild guess...

jpalecek
  • 47,058
  • 7
  • 102
  • 144
  • Thanks for your response. I really liked the elegance of your solution but when I execute it I get: Column 'mytable.id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause. Any other ideas? – sachaa Mar 02 '09 at 01:40
  • Just add all the fields in SELECT to GROUP BY - AFAIK the standard needs it, but there are DBMSs that don't require it. – jpalecek Mar 02 '09 at 01:45
  • If I do this, whenever the title changes between versions this item (with the same fk_idothertable) will apper twice in the results (different title + different version) – sachaa Mar 02 '09 at 01:56
  • No, the extra lines you're talking about will be killed by the HAVING clause. [I assume dependency (idother..., version)=>id => contenttext] – jpalecek Mar 02 '09 at 02:40
  • 1
    Damn it! Indexed views in SQL Server cannot contain HAVING clauses either :-( – sachaa Mar 02 '09 at 03:01
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).
Thava
  • 1,597
  • 17
  • 13
0

I don't know how efficient this would be, but:

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
SkyWalker
  • 28,384
  • 14
  • 74
  • 132
Chris Shaffer
  • 32,199
  • 5
  • 49
  • 61
  • But still... even with a GROUP BY this query basically returns all the list items from mytable + the max version of each item. It does not just return the items with the highest version. – sachaa Mar 02 '09 at 02:03
0

You Might be able to make the MAX a table alias that does group by.

It might look something like this:

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
Charles Graham
  • 24,293
  • 14
  • 43
  • 56
0

I think FerranB was close but didn't quite have the grouping right:

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

yothenberg
  • 1,138
  • 1
  • 9
  • 16
  • Thanks Mark. This query does return the correct results but since it uses a "common table expression" is not valid for me in order to create an indexed view in SQL Server. – sachaa Mar 02 '09 at 02:51
-2

Like this...I assume that the 'mytable' in the subquery was a different actual table...so I called it mytable2. If it was the same table then this will still work, but then I imagine that fk_idothertable will just be '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

Hope this helps

James
  • 3,312
  • 1
  • 21
  • 15
  • The subquery is actually a query to the same table that extracts the highest version of a specific item. Notice that the same versioned items will have the same fk_idothertable. – sachaa Mar 02 '09 at 01:19