A better way of writing this particular SELECT query in a VIEW - sql

I would like to know if there is a better way of writing the SELECT clause in the query below. There are three possible cases for status. It can either have a value of 'A' or 'N' or null.
I think that placing the AND status = 'A' in the where clause is redundant when you are comparing the subTable values to a value of 'A'. Is there any way that this could be rewritten more efficiently? I feel like restricting the status in the WHERE clause is always going to give you an 'A' and hence pointless to do the CASE statement.
ALTER VIEW dbo.st_review_status_vw AS
(
SELECT c.st_id, c.ms_price_comp_fy,
CASE
WHEN (SELECT status FROM
(SELECT st_id, status, ms_price_comp_fy
FROM ms_price_comp
WHERE st_id = c.st_id
AND ms_price_comp_fy = c.ms_price_comp_fy
AND status = 'A'
GROUP BY st_id, status, ms_price_comp_fy)
AS subTable) = 'A' THEN 'C'
ELSE 'I'
END AS status,
MAX(date_approved) AS date_completed
FROM ms_price_comp AS c
GROUP BY c.st_id, c.ms_price_comp_fy
)

try this
ALTER VIEW dbo.st_review_status_vw AS (
SELECT c.st_id, c.ms_price_comp_fy,
CASE WHEN MIN(status) = 'A' THEN 'C'
ELSE 'I'
END AS status,
MAX(date_approved) AS date_completed
FROM ms_price_comp AS c
GROUP BY c.st_id, c.ms_price_comp_fy
)

This looks to be the same to me
ALTER VIEW dbo.st_review_status_vw AS (
SELECT
c.st_id
, c.ms_price_comp_fy
, CASE
WHEN MIN(status) = 'A' THEN 'C'
ELSE 'I'
END AS status
, MAX(date_approved) AS date_completed
FROM ms_price_comp AS c
GROUP BY c.st_id, c.ms_price_comp_fy
)

Related

Use of CASE with criteria from multiple tables

I have to do a select query to create a view with specific criteria.
I have multiple tables which contains many many columns and lines.
However, I have extracted a value to use as my key (e.g.: id). I have 7000+ of those unique keys that I extracted from all my tables with the function UNION to avoid duplicates.
Now, I want to add a column INDICATOR_1 which will affect the value YES or NO based on criteria.
This is where I struggle.
I need to find the line in those tables that contain the id. After that, I'd like to check, always in that line, if the field XYZ contains the value 'N' (example). If yes, affect the value 'YES' to INDICATOR_1, else it's no.
In a matter of pseudo-code, what I want to do looks like this :
CASE
WHEN id = (id from table_1) AND (if table_1.xyz = 'N')
THEN 'YES'
ELSE 'NO'
END AS INDICATOR_1
I don't know if I'm clear enough, but your help will be greatly appreciated.
If I understand correctly, you want a separate indicator for each table. Something like this:
select i.*,
(case when exists (select 1
from table1 t1
where t1.id = i.id and t1.xyz = 'N'
)
then 'YES' else 'NO'
end) as indicator_1,
(case when exists (select 1
from table2 t2
where t2.id = i.id and t2.xyz = 'N'
)
then 'YES' else 'NO'
end) as indicator_2,
. . .
from (<your id list here>) i
I think you should fix this in the union, where you have all the data you need. You probably have something like:
SELECT Id
FROM table_1
UNION
SELECT Id
FROM table_2
How about selecting the information you want as well (I use distinct here to clarify):
SELECT DISTINCT Id
, CASE WHEN table_1.xyz = 'N' THEN 'N'
ELSE 'Y'
END INDICATOR_1
FROM table_1
This can lead to more records than you had, if id's can have records of both flavours exist. We can fix that with a row number in an outer query. You end up with something like:
SELECT Id
, INDICATOR_1
FROM (
SELECT Id
, INDICATOR_1
, ROW_NUMBER()OVER(PARTITION BY ID ORDER BY CASE WHEN INDICATOR_1 ='N' THEN 0 ELSE 1 END) RN
FROM (
SELECT Id
, CASE WHEN table_1.xyz = 'N' THEN 'N'
ELSE 'Y'
END INDICATOR_1
FROM table_1
UNION
...
) T
) S
WHERE S.RN = 1
You can in fact shorten that by using the inner most case expression in the ROW_NUMBER expression.

SQL Select a specific value in the group

I have this following table
Dept---------- Sub_Dept---- Dept Type
Sales.............Advertising........A
Sales.............Marketing......... B
Sales.............Analytics.......... C
Operations.....IT..................... C
Operations.....Settlement........C
And the result should be if a department got a department type as A then change all record of that department to A, else keep it same
Dept---------- Sub_Dept---- Dept Type
Sales.............Advertising........A
Sales.............Marketing......... A
Sales.............Analytics.......... A
Operations.....IT..................... C
Operations.....Settlement........C
Anybody can give a suggestion on this? I thought of using the GROUP BY but have to output the Sub Department as well
Thanks a lot
I would do:
update t
set depttype = 'a'
where exists (select 1 from t t2 where t2.dept = t.dept and t2.dept = 'a') and
t.dept <> 'a';
If you just want a select, then do:
select t.*,
(case when sum(case when depttype = 'a' then 1 else 0 end) over (partition by dept) > 1
then 'a'
else depttype
end) as new_depttype
from t;
Use below query
select a11.dept, a12.Sub_Dept, (case when a12.min_dep_type='A' then 'A' else a11.dep_type) as dep_type
from tab a11
JOIN (select dept, min(dep_type) min_dep_type from tab group by dept) a12
on a11.dept = a12.dept
Try this:
update table
set depttype= case when dept in (select dept from table where depttype='a') then 'a' else depttype end
This should work:
select a.dept, a.sub_dept,
case when b.dept is not null then 'A' else dept_type end as dept_type
from aTable a
left join(
select distinct Dept from aTable where dept_type = 'A'
)
b on b.dept = a.dept
You could use analytic functions to check whether exists the specific value in the group.
Try below query:
SELECT t.Dept,
t.Sub_Dept,
NVL(MIN(CASE WHEN t.Dept_Type = 'A'
THEN Dept_Type END) OVER (PARTITION BY t.Dept), t.Dept_Type) AS Dept_Type
FROM table_1 t
Using the analytic function MIN(), you can search for the value of 'A' (if it does exist inside the group). MIN works for non-null values only, so if you don't have any 'A' in the group, the result will be NULL.
At this point, you can use NVL to choose whether to print the value found in the group or the actual dept_type of the row.

SQL Group By with Substr

I am trying to group by with substring and I know I cannot use an alias but even like this, it is not producing any results.
select
substr(cd_orig_bic,5,2) cd,
case
when substr(CD_TXN_TYPE,1,1) = '1' then 'a'
when substr(CD_TXN_TYPE,1,1) = '2' then 'b'
else 'OTHER'
end txn_type,
d_booking,
d_value,
d_execution,
from c.c_t_transaction_queue a join c.c_d_currency b on a.id_currency=b.id_currency
where
d_effective>=to_date('01.01.2017','DD.MM.YYYY')
and
d_effective<=to_date('30.09.2017','DD.MM.YYYY')
and substr(cd_orig_bic,5,2)!='SK'
group by substr(cd_orig_bic,5,2);
You don't have any aggregation function in your query.
Here is how "group by" works:
SELECT sum(column1) -- here is a aggregation function
,column2
FROM table
GROUP BY column2 -- here is the column you want to aggregate on
The result is the sum of column1 for each value of column 2
You can read this article for more information.
Why do you want to group by the data as there is no aggregate function used in the query.
If you want unique values you could have distinct values using distinct keyword.
select distinct
substr(cd_orig_bic,5,2) cd,
case
when substr(CD_TXN_TYPE,1,1) = '1' then 'a'
when substr(CD_TXN_TYPE,1,1) = '2' then 'b'
else 'OTHER'
end txn_type,
d_booking,
d_value,
d_execution,
from c.c_t_transaction_queue a join c.c_d_currency b on a.id_currency=b.id_currency
where
d_effective>=to_date('01.01.2017','DD.MM.YYYY')
and
d_effective<=to_date('30.09.2017','DD.MM.YYYY')
and substr(cd_orig_bic,5,2)!='SK';
Also if you want to have aggregate function you could have above query as inner and use group by in the outer query.
select cd,txn_type,d_booking,sum(d_value) as value,
d_execution from (select
substr(cd_orig_bic,5,2) cd,
case
when substr(CD_TXN_TYPE,1,1) = '1' then 'a'
when substr(CD_TXN_TYPE,1,1) = '2' then 'b'
else 'OTHER'
end txn_type,
d_booking,
d_value,
d_execution,
from c.c_t_transaction_queue a join c.c_d_currency b on a.id_currency=b.id_currency
where
d_effective>=to_date('01.01.2017','DD.MM.YYYY')
and
d_effective<=to_date('30.09.2017','DD.MM.YYYY')
and substr(cd_orig_bic,5,2)!='SK'
) group by cd,txn_type,d_booking,d_execution;

Can Oracle PL/SQL CASE statement include a SELECT query?

I'm trying to do something similar to this:
CASE
WHEN number IN (1,2,3) THEN 'Y' ELSE 'N' END;
Instead I want to have a query in the place of the list, like so:
CASE
WHEN number IN (SELECT num_val FROM some_table) THEN 'Y' ELSE 'N' END;
I can't seem to get this to work. Also, here is an example of the query.
SELECT number, (CASE
WHEN number IN (SELECT num_val FROM some_table) THEN 'Y' ELSE 'N' END) AS YES_NO
FROM some_other_table;
Yes, it's possible. See an example below that would do what you are intending. The difference is that it uses EXISTS instead of IN.
SELECT a.number,
(CASE WHEN EXISTS (SELECT null FROM some_table b where b.num_val = a.number)
THEN 'Y'
ELSE 'N'
END) AS YES_NO
FROM some_other_table a;
EDIT:
I confess: I like the answers given by the others better personally.
However, there will be a difference between this query and the others depending on your data.
If for a value number in the table some_other_table you can have many matching entries of num_val in the table some_table, then the other answers will return duplicate rows. This query will not.
That said, if you take the left join queries given by the others, and add a group by, then you won't get the duplicates.
I suggest using an OUTER JOIN instead of trying to use a subquery in a CASE expression:
SELECT t.NUMBER,
CASE
WHEN s.NUM_VAL IS NOT NULL THEN 'Y'
ELSE 'N'
END AS YES_NO
FROM SOME_OTHER_TABLE t
LEFT OUTER JOIN SOME_TABLE s
ON s.NUM_VAL = t.NUMBER
Best of luck.
Seems like you just need to join the tables and do a decode.
with x as
(
select 1 as num from dual
union
select 2 as num from dual
union
select 3 as num from dual
),
y as
(
select 1 as num from dual
union
select 2 as num from dual
union
select 4 as num from dual
)
select x.num, decode(y.num, null, 'N','Y') as yes_no
from x
left outer join y on (x.num = y.num)
Output:
NUM YES_NO
1 Y
2 Y
3 N
You can use subquery in case statement:
select
case dummy
when 'X' then (select 'TRUE' from dual)
else 'FALSE'
end TEST
from dual;
TEST
TRUE
select
case (select 'XXX' from dual)
when 'XXX' then 'TRUE'
else 'FALSE'
end TEST
from dual;
TEST
TRUE

SQL select display row data as columns for varchar items

This is the query I am using to generate my table:
SELECT cu.idchanneluser AS Approver,
mcr.idrule AS Rule,
mrd.idseq AS Seq,
mcr.idcust AS CustID,
cu.iduser AS USERID
FROM mstchanneluser cu,
mstcatruledetail mrd,
mstcatrule mcr
WHERE idchannel='01'
AND mrd.idlist=cu.iduser
AND mrd.idrule=mcr.idrule
AND mcr.nbrauth='2'
AND mcr.isautoauth='N'
and this the snapshot of result:
but I want result like this:
FirstAuthorizer SecondAuthorzier Rule
rohitcorp ajitcorp 3090
CORPTEST TESTCORP 8634
ABHIMAKER CORPTEST 11705
I tried this query:
SELECT CASE WHEN idseq = '0' THEN idchanneluser ELSE NULL END AS Approver,
case when idseq = '1' THEN idchanneluser ELSE NULL END AS secondApprover,
cu.idchanneluser AS Approver,
mcr.idrule AS Rule,
mrd.idseq AS Seq,
mcr.idcust AS CustID,
cu.iduser AS USERID
FROM mstchanneluser cu,
mstcatruledetail mrd,
mstcatrule mcr
WHERE idchannel='01'
AND mrd.idlist=cu.iduser
AND mrd.idrule=mcr.idrule
AND mcr.nbrauth='2'
AND mcr.isautoauth='N'
and it will be returning me this result check the snapshot.
this is derived from your second query, you just need to use MAX and group them by mcr.idrule
SELECT MAX(CASE WHEN idseq = '0' THEN idchanneluser ELSE NULL END) AS Approver
, MAX(CASE WHEN idseq = '1' THEN idchanneluser ELSE NULL END) AS secondApprover
, mcr.idrule AS RULE
FROM mstchanneluser cu
, mstcatruledetail mrd
, mstcatrule mcr
WHERE idchannel = '01'
AND mrd.idlist = cu.iduser
AND mrd.idrule = mcr.idrule
AND mcr.nbrauth = '2'
AND mcr.isautoauth = 'N'
GROUP BY mcr.idrule
Another way in oracle with Analytical function lead lag
select tab.y FirstAuthorizer,tab.x SecondAuthorzier,tab.rule from
(
select lead(appprover,0) over (partition by rule order by seq) x ,
lag(appprover,1) over (partition by rule order by seq) y,
rule
from tbl ) tab
where tab.y is not null;