Reputation: 768
I have a sql which is failing in a left outer join subquery with
ORA-01427: single-row subquery returns more than one row
Here is the left outer join
query fragment:
LEFT OUTER JOIN (aa.location) LOCATION
ON (location_info_300.client_num = location.client_num
AND location_info_300.source = location.source
AND location_info_300.location_code = location.location_code
AND 1 =
(SELECT ROW_NUMBER()
OVER(PARTITION BY location_code, client_num, SOURCE
ORDER BY expiry_date DESC)
AS rec_order_by_expiry_desc
FROM aa.location l2
WHERE location.client_num = l2.client_num
AND location.source = l2.source
AND location.location_code = l2.location_code
AND l2.expiry_date >=
TO_DATE('01-JAN-' || location_info_300.reporting_year,
'DD-MON-YYYY')
AND l2.effective_date <=
TO_DATE('31-DEC-' || location_info_300.reporting_year,
'DD-MON-YYYY')))
I tried fixing it by doing the following change in the last AND
criteria:
1 =
(SELECT rec_order_by_expiry_desc
FROM (SELECT ROW_NUMBER() OVER (PARTITION BY LOCATION_CODE, CLIENT_NUM, SOURCE ORDER BY EXPIRY_DATE DESC) AS REC_ORDER_BY_EXPIRY_DESC
FROM aa.LOCATION l2
WHERE location.CLIENT_NUM = l2.CLIENT_NUM
AND location.SOURCE = l2.SOURCE
AND location.LOCATION_CODE = l2.LOCATION_CODE
AND l2.EXPIRY_DATE >= TO_DATE('01-JAN-'||location_info_300.REPORTING_YEAR,'DD-MON-YYYY')
AND l2.EFFECTIVE_DATE <= TO_DATE('31-DEC-'||location_info_300.REPORTING_YEAR,'DD-MON-YYYY'))
WHERE rec_order_by_expiry_desc = 1)
But now I am getting the following error:
ORA-00904: "LOCATION_INFO_300"."REPORTING_YEAR": invalid identifier
I am not sure what else to try. I hope someone does though!
Upvotes: 0
Views: 768
Reputation: 4055
checking for 1= (get row number for max expiry date) just checks that there IS a max expiry date for those criteria, and if so then all rows would be returned for that combination of client, source, and location. Is this what you want?
If you want the actual record with the max expiry date, then
LEFT OUTER JOIN (aa.location) LOCATION
ON (location_info_300.client_num = location.client_num
AND location_info_300.source = location.source
AND location_info_300.location_code = location.location_code
AND location.expiry_date =
(SELECT MAX(expiry_date)
FROM aa.location l2
WHERE location.client_num = l2.client_num
AND location.source = l2.source
AND location.location_code = l2.location_code
AND l2.expiry_date >=
TO_DATE('01-JAN-' || location_info_300.reporting_year,
'DD-MON-YYYY')
AND l2.effective_date <=
TO_DATE('31-DEC-' || location_info_300.reporting_year,
'DD-MON-YYYY')))
Upvotes: 1
Reputation: 1529
Your first subquery should return exactly one row, but it returns more than one.
This is because you perform an analytic function, which is essentially supposed to return a varied amount of rows. The only way to assert that only one row will be returned is using an aggregate function or using conditions that will assure that.
Regarding the second query, note that you're calling a field from a table that doesn't exist in the given context.
If you're trying to check if a row exists or not, you should read into EXISTS function, or perhaps using COUNT in the subquery.
Edit: here is an example for the second option (the fist was already posted):
(SELECT COUNT(*)
FROM aa.location l2
WHERE location.client_num = l2.client_num
AND location.source = l2.source
AND location.location_code = l2.location_code
AND l2.expiry_date >= TO_DATE('01-JAN-' || location_info_300.reporting_year, 'DD-MON-YYYY')
AND l2.effective_date <= TO_DATE('31-DEC-' || location_info_300.reporting_year, 'DD-MON-YYYY'))
Note that I removed the fields in the PARTITION BY clause and didn't add them in GROUP BY because they conflict with your need of only one row.
Upvotes: 1
Reputation: 23588
I think you're basically checking if the row exists in the subquery? If so, then just do an EXISTS
:
LEFT OUTER JOIN (aa.location) LOC
ON (location_info_300.client_num = loc.client_num
AND location_info_300.source = loc.source
AND location_info_300.location_code = loc.location_code
AND exists (SELECT null
FROM aa.location l2
WHERE loc.client_num = l2.client_num
AND loc.source = l2.source
AND loc.location_code = l2.location_code
AND l2.expiry_date >= TO_DATE('01-JAN-' || location_info_300.reporting_year, 'DD-MON-YYYY')
AND l2.effective_date <= TO_DATE('31-DEC-' || location_info_300.reporting_year, 'DD-MON-YYYY')))
N.B. I changed the alias of the aa.location table, just to avoid any possible conflicts between the outer and sub-query's aa.location tables (much better to make sure that aliases aren't the same as existing identifier names to avoid any potential scope clash issues. Also, it makes it easier to understand when you read the query).
Upvotes: 3