If this is your first visit, be sure to check out the FAQ by clicking the link above. You may have to register before you can post: click the register link above to proceed. To start viewing messages, select the forum that you want to visit from the selection below.

 
Go Back  dBforums > Data Access, Manipulation & Batch Languages > ANSI SQL > SQL Query help - do I JOIN or APPLY?

Reply
 
LinkBack Thread Tools Search this Thread Display Modes
  #1 (permalink)  
Old 09-28-11, 19:46
thephotomatt thephotomatt is offline
Registered User
 
Join Date: Sep 2011
Posts: 1
SQL Query help - do I JOIN or APPLY?

Hi All- consider the following scenario with some mocked up data:

TableA
User_ID, Name
1, John
2, Sally
3, Mary

TableB
User_ID, Status_ID, Status_Updated_Date
1, 60, 2011-01-01
1, 61, 2011-01-03
1, 62, 2011-01-15
2, 60, 2011-02-10
2, 61, 2011-02-16
2, 62, 2011-02-21
3, 60, 2011-03-07
3, 61, 2011-03-10
3, 62, 2011-03-13

What I want to do is to join TableA on TableB (two times I believe), and have data that appears on TableB on two separate rows appear in my query output on a single row...specifically the rows for each User that have the greatest and second greatest 'Status_Updated_Date':

TableB
User_ID, Status_ID, Status_Updated_Date
1, 60, 2011-01-01
1, 61, 2011-01-03
1, 62, 2011-01-15

2, 60, 2011-02-10
2, 61, 2011-02-16
2, 62, 2011-02-21

3, 60, 2011-03-07
3, 61, 2011-03-10
3, 62, 2011-03-13


Given that, if I want the record with the greatest 'Status_Updated_Date' for a particular User ID on TableB, I can do so by querying:
Code:
SELECT DISTINCT TOP 1 *
FROM TableB WHERE User_ID = 2
ORDER BY Status_Updated_Date DESC
And would get back:
User_ID, Status_ID, Status_Updated_Date
2, 62, 2011-02-21


Likewise, if I want the record with the second greatest 'Status Updated Date' for a particular User ID on TableB, I can do so querying:
Code:
SELECT TOP 1 *
FROM (SELECT DISTINCT TOP 2 *
FROM TableB
WHERE User_ID = 2
ORDER BY Status_Updated_Date DESC) AS a
ORDER BY Status_Updated_Date
And would get back:
User_ID, Status_ID, Status_Updated_Date
2, 61, 2011-02-16


Where I'm stuck is how to construct my WHERE syntax to make the following happen on a single output row:

TableA.User_ID, TableA.User, TableB.Status_ID (2nd greatest), TableB.Status_ID (greatest), TableB.Status_Updated_Date (greatest)
1, John, 61, 62, 2011-01-15
2, Sally, 61, 62, 2011-02-21
3, Mary, 61, 62, 2011-03-13


This way, I can determine when the most recent Status change took place, what the Status previously was, and what the Status is now.

Thanks in advance to everyone who can assist!


-Matthew

Last edited by thephotomatt; 09-28-11 at 21:22.
Reply With Quote
  #2 (permalink)  
Old 09-29-11, 02:42
JarlH JarlH is offline
Registered User
 
Join Date: Dec 2008
Location: At work...
Posts: 68
The simple solution is to use correlated subqueries. Something like.

SELECT TableA.Name,
TableA.User_ID,
[...]
(SELECT DISTINCT TOP 1 *
FROM TableB WHERE User_ID = 2 and TableA.User_ID = TableB.User_ID
ORDER BY Status_Updated_Date DESC)
[...]
FROM TableA

(May slow down performance...)


Note that TOP is vendor specific SQL (not ANSI compliant).
Reply With Quote
  #3 (permalink)  
Old 02-05-12, 10:05
tonkuma tonkuma is offline
Registered User
 
Join Date: Feb 2008
Location: Japan
Posts: 2,191
ROW_NUMBER() windowed table function was included in ANSI SQL:2003,
and it was implemented on many commercial RDBMS(e.g. Oracle, DB2, MS SQL Server).

See
Overview of SQL:2003
SQL:2003 Has Been Published

Example 1:
Note: Tested on DB2 9.7.5 for Windows.
Code:
WITH
/**************************************************
**********  Start of sample/test data.   **********
**************************************************/
  TableA
( User_ID , Name ) AS (
VALUES
  ( 1 , 'John'  )
, ( 2 , 'Sally' )
, ( 3 , 'Mary'  )
)
, TableB
( User_ID , Status_ID , Status_Updated_Date ) AS (
VALUES
  ( 1, 60 , '2011-01-01' )
, ( 1, 61 , '2011-01-03' )
, ( 1, 62 , '2011-01-15' )
, ( 2, 60 , '2011-02-10' )
, ( 2, 61 , '2011-02-16' )
, ( 2, 62 , '2011-02-21' )
, ( 3, 60 , '2011-03-07' )
, ( 3, 61 , '2011-03-10' )
, ( 3, 62 , '2011-03-13' )
)
/**************************************************
**********    End of sample/test data.   **********
**************************************************/
SELECT a.User_ID
     , a.Name
     , MAX( CASE rnum
            WHEN 2 THEN
                 b.Status_ID
            END
          ) AS Status_ID_2nd
     , MAX( CASE rnum
            WHEN 1 THEN
                 b.Status_ID
            END
          ) AS Status_ID_max
     , MAX( b .Status_Updated_Date
          ) AS Status_Updated_Date
 FROM  TableA AS a
 INNER JOIN (
       SELECT b.*
            , ROW_NUMBER()
                 OVER( PARTITION BY User_ID
                           ORDER BY Status_Updated_Date DESC
                     ) AS rnum
        FROM  TableB AS b
       ) AS b
   ON  b.User_ID =  a.User_ID
   AND b.rnum    <= 2
 GROUP BY
       a.User_ID
     , a.Name
 ORDER BY
       a.User_ID
;
------------------------------------------------------------------------------

USER_ID     NAME  STATUS_ID_2ND STATUS_ID_MAX STATUS_UPDATED_DATE
----------- ----- ------------- ------------- -------------------
          1 John             61            62 2011-01-15         
          2 Sally            61            62 2011-02-21         
          3 Mary             61            62 2011-03-13         

  3 record(s) selected.

Last edited by tonkuma; 02-05-12 at 10:19.
Reply With Quote
  #4 (permalink)  
Old 02-07-12, 08:26
tonkuma tonkuma is offline
Registered User
 
Join Date: Feb 2008
Location: Japan
Posts: 2,191
Tested on Mimer SQL Developers - Mimer SQL-2003 Validator

Comments got error.
Code:
Result:

WITH 
/************************************************** 
^-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
syntax error: /*******************************... <end>
  correction: <identifier> AS ( SELECT * FROM <identifier> ) SELECT * FROM
              <identifier> <end>

I removed comments.

The SQL-2003 Validator!

Enter your SQL statement(s) in the box below and simply click the "Test SQL" button:

Code:
WITH
  TableA
( User_ID , Name ) AS (
VALUES
  ( 1 , 'John'  )
, ( 2 , 'Sally' )
, ( 3 , 'Mary'  )
)
, TableB
( User_ID , Status_ID , Status_Updated_Date ) AS (
VALUES
  ( 1, 60 , '2011-01-01' )
, ( 1, 61 , '2011-01-03' )
, ( 1, 62 , '2011-01-15' )
, ( 2, 60 , '2011-02-10' )
, ( 2, 61 , '2011-02-16' )
, ( 2, 62 , '2011-02-21' )
, ( 3, 60 , '2011-03-07' )
, ( 3, 61 , '2011-03-10' )
, ( 3, 62 , '2011-03-13' )
)
SELECT a.User_ID
     , a.Name
     , MAX( CASE rnum
            WHEN 2 THEN
                 b.Status_ID
            END
          ) AS Status_ID_2nd
     , MAX( CASE rnum
            WHEN 1 THEN
                 b.Status_ID
            END
          ) AS Status_ID_max
     , MAX( b .Status_Updated_Date
          ) AS Status_Updated_Date
 FROM  TableA AS a
 INNER JOIN (
       SELECT b.*
            , ROW_NUMBER()
                 OVER( PARTITION BY User_ID
                           ORDER BY Status_Updated_Date DESC
                     ) AS rnum
        FROM  TableB AS b
       ) AS b
   ON  b.User_ID =  a.User_ID
   AND b.rnum    <= 2
 GROUP BY
       a.User_ID
     , a.Name
 ORDER BY
       a.User_ID
;

Code:
 
Result:

The following features outside Core SQL-2003 are used:

F641, "Row and table constructors"
F391, "Long identifiers"
T611, "Elementary OLAP operations"
F591, "Derived tables"
T121, "WITH (excluding RECURSIVE) in query expression"
F661, "Simple tables"

The following vendor reserved word is used:

NAME
Reply With Quote
Reply

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On