Using a table alias in a case statement in a where clause - oracle

I have the following query:
Select emp_id, emp_name, department, salary
from tbl_employee emp
where (
CASE
when emp.department = 'C'
then (select count(*) from (SELECT 'Y' iscommissioner
FROM tbl_emp_department ted
where ted.department = emp.department ))
when emp.department = 'C'
then (select count(*) from (SELECT 'Y' iscommissioner
FROM tbl_emp_department ted
where ted.department = emp.department
and ted.id = 7))
END) > 0
I am getting the following error:
emp.department invalid identifier
What am I doing wrong? Why can't I access that column with a table alias in the where clause in a case statement?
Any idea?

This has nothing to do with the case statement. The issue is the scoping rules for correlated subqueries. They only nest one level deep. Here is another question with this problem.
For your example, you don't need nested subqueries. You can just do:
where (CASE when emp.department = 'C'
then (select count(*)
from tbl_emp_department ted
where ted.department = emp.department
)
when emp.department = 'C'
then (select count(*)
from tbl_emp_department ted
where ted.department = emp.department and ted.id = 7
)
END) > 0
However, because your query doesn't really make sense (the same conditions for the two when clauses, I suspect that your actual query may be more complicated. You may have to find another approach to do what you want, using explicit joins and aggregations or using analytic functions.

Related

Adding filters in subquery from CTE quadruples run time

I am working on an existing query for SSRS report that focuses on aggregated financial aid data split out into 10 aggregations. User wants to be able to select students included in that aggregated data based on new vs. returning and 'selected for verification.' For the new/returning status, I added a CTE to return the earliest admit date for a student. 2 of the 10 data fields are created by a subquery. I have been trying for 3 days to get the subquery to use the CTE fields for a filter, but they won't work. Either they're ignored or I get a 'not a group by expression' error. If I put the join to the CTE within the subquery, the query time jumps from 45 second to 400 seconds. This shouldn't be that complicated! What am I missing? I have added some of the code... 3 of the chunks work - paid_something doesn't.
with stuStatus as
(select
person_uid, min(year_admitted) admit_year
from academic_study
where aid_year between :AidYearStartParameter and :AidYearEndParameter
group by person_uid)
--- above code added to get student information not originally in qry
select
finaid_applicant_status.aid_year
, count(1) as fafsa_cnt --works
, sum( --works
case
when (
package_complete_date is not null
and admit.status is not null
)
then 1
else 0
end
) as admit_and_package
, (select count(*) --does't work
from (
select distinct award_by_aid_year.person_uid
from
award_by_aid_year
where
award_by_aid_year.aid_year = finaid_applicant_status.aid_year
and award_by_aid_year.total_paid_amount > 0 )dta
where
(
(:StudentStatusParameter = 'N' and stuStatus.admit_year = finaid_applicant_status.aid_year)
OR
(:StudentStatusParameter = 'R' and stuStatus.admit_year <> finaid_applicant_status.aid_year)
OR :StudentStatusParameter = '%'
)
)
as paid_something
, sum( --works
case
when exists (
select
1
from
award_by_person abp
where
abp.person_uid = fafsa.person_uid
and abp.aid_year = fafsa.aid_year
and abp.award_paid_amount > 0
) and fafsa.requirement is not null
then 1
else 0
end
) as paid_something_fafsa
from
finaid_applicant_status
join finaid_tracking_requirement fafsa
on finaid_applicant_status.person_uid = fafsa.person_uid
and finaid_applicant_status.aid_year = fafsa.aid_year
and fafsa.requirement = 'FAFSA'
left join finaid_tracking_requirement admit
on finaid_applicant_status.person_uid = admit.person_uid
and finaid_applicant_status.aid_year = admit.aid_year
and admit.requirement = 'ADMIT'
and admit.status in ('M', 'P')
left outer join stuStatus
on finaid_applicant_status.person_uid = stuStatus.person_uid
where
finaid_applicant_status.aid_year between :AidYearStartParameter and :AidYearEndParameter
and (
(:VerifiedParameter = '%') OR
(:VerifiedParameter <> '%' AND finaid_applicant_status.verification_required_ind = :VerifiedParameter)
)
and
(
(:StudentStatusParameter = 'N' and (stuStatus.admit_year IS NULL OR stuStatus.admit_year = finaid_applicant_status.aid_year ))
OR
(:StudentStatusParameter = 'R' and stuStatus.admit_year <> finaid_applicant_status.aid_year)
OR :StudentStatusParameter = '%'
)
group by
finaid_applicant_status.aid_year
order by
finaid_applicant_status.aid_year
Not sure if this helps, but you have something like this:
select aid_year, count(1) c1,
(select count(1)
from (select distinct person_uid
from award_by_aid_year a
where a.aid_year = fas.aid_year))
from finaid_applicant_status fas
group by aid_year;
This query throws ORA-00904 FAS.AID_YEAR invalid identifier. It is because fas.aid_year is nested too deep in subquery.
If you are able to modify your subquery from select count(1) from (select distinct sth from ... where year = fas.year) to select count(distinct sth) from ... where year = fas.year then it has the chance to work.
select aid_year, count(1) c1,
(select count(distinct person_uid)
from award_by_aid_year a
where a.aid_year = fas.aid_year) c2
from finaid_applicant_status fas
group by aid_year
Here is simplified demo showing non-working and working queries. Of course your query is much more complicated, but this is something what you could check.
Also maybe you can use dbfiddle or sqlfiddle to set up some test case? Or show us sample (anonimized) data and required output for them?

Merge 3 queries into a single sub query

Is it possible to write a single query to fetch data from the 3 tables?
Below are the 3 queries that I am running to fetch data, but would need a single query.
I have tried to write the below query but the data I amgetting is wrong
(SELECT M_NB,M_GF_IND,M_HEDGE_LNK FROM TABLE#DATA#DEALCOM_DBF WHERE M_GF_IND = 'Y' ) A
(SELECT M_NB,M_GF_IND,M_HEDGE_LNK FROM TABLE#DATA#DEALIRD_DBF WHERE M_GF_IND = 'Y' ) B
(SELECT M_NB,M_GF_IND,M_HEDGE_LNK FROM TABLE#DATA#DEALSCF_DBF WHERE M_GF_IND = 'Y' ) C
I have tried to write the below query but the data I am getting is wrong
SELECT DD.M_NB, DD.M_GF_IND, DD.M_HEDGE_LNK,
FF.M_NB, FF.M_GF_IND, FF.M_HEDGE_LNK
FROM TABLE#DATA#DEALCOM_DBF DD,
TABLE#DATA#DEALIRD_DBF FF
WHERE DD.M_GF_IND = 'Y' and FF.M_GF_IND = 'Y'
Sorry if this is a bad question, I am new to SQL and thus cannot figure out. Will update if I find any alternative.
Are you looking for UNION (ALL)?
SELECT M_NB,M_GF_IND,M_HEDGE_LNK FROM TABLE#DATA#DEALCOM_DBF WHERE M_GF_IND = 'Y'
UNION ALL
SELECT M_NB,M_GF_IND,M_HEDGE_LNK FROM TABLE#DATA#DEALIRD_DBF WHERE M_GF_IND = 'Y'
UNION ALL
SELECT M_NB,M_GF_IND,M_HEDGE_LNK FROM TABLE#DATA#DEALSCF_DBF WHERE M_GF_IND = 'Y';

How to pass parameter from query to subquery ? ORA-00904

I get ORA-00904 invalid identifier error, from this query
SELECT
tab1."col1" AS ID,
tab1."col4" AS Name,
tab1."col5" AS Place,
(SELECT SUBSTR (SYS_CONNECT_BY_PATH (one_row , ';'), 2) myConString
FROM (SELECT tab2."col3" || ',' || tab2."col4" AS one_row,
ROW_NUMBER () OVER(ORDER BY tab2."col1") rn,
COUNT (*) OVER () cnt
FROM dbo."table2" tab2
WHERE tab2."col1" = tab1."col1"
AND tab2."col2" = tab1."col2")
WHERE rn = cnt
START WITH rn = 1
CONNECT BY rn = PRIOR rn + 1)
FROM dbo."table1" tab1
WHERE tab1."col1" IN (1,2,3)
AND tab1."col2" = 1 AND tab1."col3" = 1;
in this specific place
tab2."col1" = tab1."col1" AND tab2."col2" = tab1."col2"
In the subquery I concatenate rows into string and it works great and give me the right results, something like
1,100;1,200;2,150....
I think problem is that I try to refer to objects more then one level of subquery, but I can't figure it out, how to rewrite the query.
Thanks for any help
Correlated subqueries can only reference things one level deep. Your tab1 table is two levels away from your tab2 table.
I can't quite wrap my head around your query, but can you rewrite this so that you have a join between tab1 and tab2 instead of having a correlated query in the select clause?

Not a single-group group function on a case count in oracle

I'm trying to adapt a query that works in MSSQL to Oracle, the query is much bigger (this part is just a field from a much bigger query) but I managed to reduce it so it looks simpler.
SELECT CASE WHEN COUNT(*) > 0 THEN COUNT(*)
ELSE (SELECT COUNT(*) FROM table2)
END
FROM table1
The error I'm getting is:
ora-00937 not a single-group group function
Can someone tell me where's the problem or how can I redefine it?
You can try with this query:
SELECT CASE WHEN (SELECT COUNT(*) FROM table1) > 0 then (SELECT COUNT(*) FROM table1)
ELSE (SELECT COUNT(*) FROM table2)
END
FROM dual;
It is still ugly but it works :)
Update:
To explain how it's working:
We have 2 cases:
If there are records in the table1 then show me how many records
there are
If the table1 is empty, then give me the number of records from the
table2
Dual is the dummy table.
I think that NikNik answer is cleaner but another solution would be:
SELECT *
FROM (SELECT CASE
WHEN Count(*) > 0 THEN Count(*)
ELSE (SELECT Count(*)
FROM table2)
END
FROM table1
GROUP BY table1.primarykey1,
table1.primarykey2)
WHERE ROWNUM = 1

How to handle singe-row subquery returns more than one row error with case statement

I am building an Oracle query which has a case statement involved.
SELECT
CASE WHEN
((SELECT agent_or_group_id from trans_slot where slot_id =
(SELECT slot_id from trans_slot where slot_alias = 'PP' and measure_expiration > sysdate)) > 0)
/*The below subquery returns 1 row*/
THEN (SELECT agent_or_group_id from trans_slot where slot_id =
(SELECT slot_id from trans_slot where slot_alias = 'PP' and measure_expiration > sysdate))
ELSE
/* The below subquery returns 2 rows*/
(SELECT child_agent_id FROM agent_object_group_member WHERE parent_agent_id IN
(SELECT agent_or_group_id FROM trans_slot WHERE slot_id IN
(SELECT slot_id FROM trans_slot WHERE slot_alias = 'PP' AND measure_expiration > sysdate)
)
)
END
"Agent_ID" from DUAL;
When the run the subqueries independent they run fine. But running the whole query returns
ORA-01427: single-row subquery returns more than one row
01427. 00000 - "single-row subquery returns more than one row"
*Cause:
*Action:
If I understand what you're after, you can't do it like that. You'll need to use a left outer join and then chose the column value you want to display.
Here's a simplified example based on the SQL you provided:
WITH t1 AS (SELECT 1 ID FROM dual UNION ALL
SELECT 0 ID FROM dual UNION ALL
SELECT 2 ID FROM dual),
t2 AS (SELECT 10 child_id, 0 parent_id FROM dual UNION ALL
SELECT 20 child_id, 0 parent_id FROM dual UNION ALL
SELECT 30 child_id, 1 parent_id FROM dual UNION ALL
SELECT 40 child_id, 2 parent_id FROM dual)
---- end of mimicking two tables with the sample data in them. See the query below:
SELECT COALESCE(t2.child_id, t1.id) ID
FROM t1
LEFT OUTER JOIN t2 ON (t1.id = t2.parent_id AND t1.id = 0);
ID
----------
10
20
2
1
Here, I have used the t1 and t2 subqueries to mimic the output you'd get from your main subqueries in your original query.
Then we outer join t2 to t1 only where the t1.id = 0. By doing this, you can then simply choose the t2.child_id value if it exists, otherwise use the t1.id value.
(I realise that in your example, the t1 equivalent subquery would only generate 1 row, based on what you said, but I've included 3 rows so that you can see what the results would be based on the different ids.)
ETA:
In your case, the t1 subquery in my example above would be:
SELECT agent_or_group_id
from trans_slot
where slot_id = (SELECT slot_id
from trans_slot
where slot_alias = 'PP'
and measure_expiration > sysdate)
and the t2 subquery would be:
SELECT child_agent_id
FROM agent_object_group_member
WHERE parent_agent_id IN (SELECT agent_or_group_id
FROM trans_slot
WHERE slot_id IN (SELECT slot_id
FROM trans_slot
WHERE slot_alias = 'PP'
AND measure_expiration > sysdate))

Resources