简体   繁体   中英

Left outer join and necessary subquery with date in Oracle

I'm struggling with a left outer join where the right portion, the one that could contain nulls, doesn't show me values. I think the situation is called an implicit inner join. The reason is that the subquery doesn't return nulls but rather nothing for what I'd like to have on my right part.

(This question differs from Left Outer Join Not Working? in that a subquery to find the maximum date is needed.)

Here is a simplified example:

Table Contracts:
customer_id, status

Table Values:
customer_id, value_code, value, begin_date

I want to display all customers with status = 'active' and the latest value for a certain value_code, lets say 'volume'. There are more value_codes and the values have a certain date from which they are valid. There can also be no value_code BUT then I want a NULL on the right side.

So here is what I tried to do:

SELECT * FROM CONTRACTS C
LEFT JOIN VALUES V ON C.CUSTOMER_ID = V.CUSTOMER_ID
                      AND VALUE_CODE = 'VOLUME'
WHERE C.STATUS = 'ACTIVE'
     AND V.BEGIN_DATE = (
                         SELECT MAX(BEGIN_DATE) FROM VALUES V2 
                         WHERE V2.CUSTOMER_ID = V.CUSTOMER_ID
                         AND V2.VALUE_CODE = 'VOLUME'
                         )

I can't put the subquery in the join clause, Oracle won't accept that. On the other hand my subquery makes it so that all the rows that have no entry for a value with code 'volume' are omitted. What I want is to have value = NULL instead with all the customers on the left.

Thanks for helping!

Filter the VALUE rows first and then LEFT JOIN :

SELECT *
FROM   CONTRACTS C
       LEFT JOIN
       ( SELECT *
         FROM   (
           SELECT V.*,
                  ROW_NUMBER() OVER ( PARTITION BY CUSTOMER_ID ORDER BY BEGIN_DATE DESC )
                    AS rn
           FROM   VALUES V
         )
         WHERE rn = 1
       ) V
ON     ( C.CUSTOMER_ID = V.CUSTOMER_ID
         AND VALUE_CODE = 'VOLUME' )
WHERE C.STATUS = 'ACTIVE'

As MT0 suggested using partitioning and sorting by row number helped. Only had to include value_code in the partitioning for my purpose.

So this query finally did what I wanted:

SELECT *
FROM   CONTRACTS C
       LEFT JOIN
       ( SELECT *
         FROM   (
           SELECT V.*,
                  ROW_NUMBER() OVER ( PARTITION BY CUSTOMER_ID, VALUE_CODE ORDER BY BEGIN_DATE DESC )
                    AS rn
           FROM   VALUES V
         )
         WHERE rn = 1
       ) V
ON     ( C.CUSTOMER_ID = V.CUSTOMER_ID
         AND VALUE_CODE = 'VOLUME' )
WHERE C.STATUS = 'ACTIVE'

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM