views:

2233

answers:

6

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!

A: 

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
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.
Sacha
A: 

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
Chris Shaffer
You need a group by.
Charles Graham
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.
Sacha
A: 

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
+2  A: 

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
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?
Sacha
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
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)
Sacha
No, the extra lines you're talking about will be killed by the HAVING clause. [I assume dependency (idother..., version)=>id => contenttext]
jpalecek
WOW! It works. Thanks a lot!
Sacha
Damn it! Indexed views in SQL Server cannot contain HAVING clauses either :-(
Sacha
A: 

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
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.
Sacha
+6  A: 

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.

jmucchiello
Very clever idea. Thanks Joe!
Sacha
Very neat! Well done.
Jonathan Leffler