tags:

views:

81

answers:

3

I'm trying to make a Teradata SQL query that will return the n-th chronological visit date for each user. E.g.,

user  |  visit_date
---------------------
  a      1/1      
  b      1/10
  c      1/20
  a      1/3
  a      1/4
  b      1/5
  c      1/15
  b      1/9


> magic_query_for_Second_visit;
user  |  second
------------------
  a       1/3
  b       1/9
  c       1/20

I tried something like the below, but Teradata shrieked that "Ordered analytical functions are not allowed in WHERE clause." I've pulled my hair for a while but am not making much progress. Anyone seen this?

select user,
  row_number() over (partition by user order by visit_date desc) as rnum

from visitstable
  where rnum = 2

If I exclude the where then my result looks promising... I just can't extract what I need!

 user |  visit_date | rnum
---------------------------
  a        1/1          1
  a        1/3          2
  a        1/4          3
  b        1/5          1
  b        1/9          2
  b        1/10         3 
  c        1/15         1
  c        1/20         2

Thanks in advance for the help!

A: 
Crappy Coding Guy
It's Teradata... I'll see if I can translate your suggestion. Thanks.
Chris
Does your query work, and return the expected row numbers, if you don't have the where clause? If so, you may be able to insert the results into a temp table, then select the appropriate row numbers out of that.
Crappy Coding Guy
Yes, temporary table would probably work portably... I just wanted to minimize the number of temporary tables, since there are hundreds of millions of records.
Chris
+1  A: 

Oh, heaven forbid I should look in the documentation. FYI, it looks like qualify lets you specify ordered analytic constraints. However, qualify isn't ANSI, so I'm still handing out points for a portable solution...

I can do:

select user,
  row_number() over (partition by user order by visit_date desc) as rnum

from visitstable
qualify rnum=2

...and get...

user  |   visit_date  |  rnum
-----------------------------
  a        1/3            2
  b        1/9            2
  c        1/20           2

Bookmark it! :)

Chris
+1 for finding your own solution.
Adam Bernier
A: 
SELECT 
    user

FROM 
    visitstable

QUALIFY ROW_NUMBER() OVER (
    PARTITION BY 
        user
    ORDER BY 
        visit_date DESC) = 2

I see from your comment that you don't necessarily want to rely on the Teradata-specific QUALIFY. The following is a sort-of portable solution (as many RDBMSes are now supporting CTEs) based on another chap's answer:

WITH tmp (user, rnum) AS (
    SELECT 
        user, 
        ROW_NUMBER() OVER (
            PARTITION BY 
                user 
            ORDER BY 
                visit_date DESC) AS rnum 
    FROM 
        visitstable)
SELECT
    tmp.*
FROM 
    tmp
WHERE 
    tmp.rnum = 2
Adam Bernier
Thanks, Adam. TD didn't like the first solution. The "BY" in `QUALIFY BY ROW_NUMBER()` had to go.The 2nd one makes my brain sizzle, which is great on a Friday morning. Thanks!
Chris
Oops, sorry about the `BY` typo. I've now edited that out, thanks. The second example is a temporary table that is made available to your main query. Once I got over the syntax I noticed huge performance gains by simply moving the first step or so of existing queries up into the `WITH` clause. Best of luck to you.
Adam Bernier