CASE .. WHEN expression in Oracle SQL - sql

I have the table with 1 column and has following data
Status
a1
i
t
a2
a3
I want to display the following result in my select query
Status| STATUSTEXT
a1 | Active
i | Inactive
t | Terminated
a2 | Active
a3 | Active
One way I could think was using a Switch When expression in select query
SELECT
status,
CASE status
WHEN 'a1' THEN 'Active'
WHEN 'a2' THEN 'Active'
WHEN 'a3' THEN 'Active'
WHEN 'i' THEN 'Inactive'
WHEN 't' THEN 'Terminated'
END AS StatusText
FROM stage.tst
Is there any other way of doing this where I don't need to write When expression 3 times for Active Status and the entire active status can be checked in one single expression?

You could use an IN clause
Something like
SELECT
status,
CASE
WHEN STATUS IN('a1','a2','a3')
THEN 'Active'
WHEN STATUS = 'i'
THEN 'Inactive'
WHEN STATUS = 't'
THEN 'Terminated'
END AS STATUSTEXT
FROM
STATUS
Have a look at this demo
SQL Fiddle DEMO

You can rewrite it to use the ELSE condition of a CASE:
SELECT status,
CASE status
WHEN 'i' THEN 'Inactive'
WHEN 't' THEN 'Terminated'
ELSE 'Active'
END AS StatusText
FROM stage.tst

Of course...
select case substr(status,1,1) -- you're only interested in the first character.
when 'a' then 'Active'
when 'i' then 'Inactive'
when 't' then 'Terminated'
end as statustext
from stage.tst
However, there's a few worrying things about this schema. Firstly if you have a column that means something, appending a number onto the end it not necessarily the best way to go. Also, depending on the number of status' you have you might want to consider turning this column into a foreign key to a separate table.
Based on your comment you definitely want to turn this into a foreign key. For instance
create table statuses ( -- Not a good table name :-)
status varchar2(10)
, description varchar2(10)
, constraint pk_statuses primary key (status)
)
create table tst (
id number
, status varchar2(10)
, constraint pk_tst primary key (id)
, constraint fk_tst foreign key (status) references statuses (status)
)
Your query then becomes
select a.status, b.description
from tst a
left outer join statuses b
on a.status = b.status
Here's a SQL Fiddle to demonstrate.

It will be easier to do using decode.
SELECT
status,
decode ( status, 'a1','Active',
'a2','Active',
'a3','Active',
'i','Inactive',
't','Terminated',
'Default')STATUSTEXT
FROM STATUS

Since web search for Oracle case tops to that link, I add here for case statement, though not answer to the question asked about case expression:
CASE
WHEN grade = 'A' THEN dbms_output.put_line('Excellent');
WHEN grade = 'B' THEN dbms_output.put_line('Very Good');
WHEN grade = 'C' THEN dbms_output.put_line('Good');
WHEN grade = 'D' THEN dbms_output.put_line('Fair');
WHEN grade = 'F' THEN dbms_output.put_line('Poor');
ELSE dbms_output.put_line('No such grade');
END CASE;
or other variant:
CASE grade
WHEN 'A' THEN dbms_output.put_line('Excellent');
WHEN 'B' THEN dbms_output.put_line('Very Good');
WHEN 'C' THEN dbms_output.put_line('Good');
WHEN 'D' THEN dbms_output.put_line('Fair');
WHEN 'F' THEN dbms_output.put_line('Poor');
ELSE dbms_output.put_line('No such grade');
END CASE;
Per Oracle docs: https://docs.oracle.com/cd/B10501_01/appdev.920/a96624/04_struc.htm

Following syntax would work :
....
where x.p_NBR =to_number(substr(y.k_str,11,5))
and x.q_nbr =
(case
when instr(substr(y.m_str,11,9),'_') = 6 then to_number(substr(y.m_str,11,5))
when instr(substr(y.m_str,11,9),'_') = 0 then to_number(substr(y.m_str,11,9))
else
1
end
)

SELECT
STATUS,
CASE
WHEN STATUS IN('a1','a2','a3')
THEN 'Active'
WHEN STATUS = 'i'
THEN 'Inactive'
WHEN STATUS = 't'
THEN 'Terminated' ELSE null
END AS STATUSTEXT
FROM
stage.tst;

You can only check the first character of the status. For this you use substring function.
substr(status, 1,1)
In your case past.

DECODE(SUBSTR(STATUS,1,1),'a','Active','i','Inactive','t','Terminated','N/A')

Related

SQL query to return rows with one distinct field and use CASE to create new evaluation column

I want to write an SQL query to return rows with one distinct field and use CASE to create new evaluation column. Any help is appreciated. Deets below:
table
id
status
category
string
string
bigint
--------
--------
----------
pseudo query:
return (distinct id), time_created, NEW_COL
where category is 123123
and where new_col //create new col with these values
(
if status = 'good' then 'GOOD'
if status = 'bad' then 'BAD'
)
FROM table
result:
id
time_created
new_col
1
Jun-1
BAD
2
Jul-21
GOOD
3
Jun-12
GOOD
4
Aug-1
GOOD
--- I keep getting a lint error right on my CASE keyword:
"expecting " '%', '*', '+', '-', '.', '/', 'AT', '[', '||',""
one of queries I tried:
SELECT
ID, time_created
CASE
WHEN status = 'good' THEN 'GOOD'
WHEN status = 'bad' THEN 'BAD'
END
as STATUS_new
FROM TBL
WHERE CATEGORY = '871654671'
ORDER BY time_created
You just have a small syntax error (and bad column name in your sql fiddle). You just need a comma after the time created column.
SELECT
ID, time as time_created,
CASE
WHEN status = 'good' THEN 'GOOD'
WHEN status = 'bad' THEN 'BAD'
END
as STATUS_new
FROM TBL
WHERE CATEGORY = '871654671'
ORDER BY time_created
Here is the working query:
http://www.sqlfiddle.com/#!18/7293b5/11
SELECT
ID, TIME, 'STATUS_new' =
CASE STATUS
WHEN 'good' THEN 'GOOD'
WHEN 'bad' THEN 'BAD'
END
FROM TBL
WHERE CATEGORY = '871654671'
ORDER BY TIME
you must put the new name of the column before the CASE
the column you are defining the CASE must be defined directly behind the case and all the WHEN conditions are directly related to it.
in your fiddle you used the wrong column name of your TIME column

How to create a new column based on data out from a query

I have a quick question.
ATtaching the SS for reference.
How can i set a new column 'Status' as NO based the nvl condition if the id is null then i have to map to the corresponding of another table .
You can do that using case statement.
select
nvl(b.id,a.id) as id
,b.name
,case when nvl(b.id,a.id) is null then 'No' else 'Yes' End as Status
from dd b,
(select id, name from demo group by id, name)a
where a.id=b.id(+)

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.

Subquery returned more than 1 value - where clause

I have a query to deactivate some accounts that were activated. Basically it looks at the year_end_close table and any accounts that exist there are updated in the master table to an inactive status (A or B).
update master
set account_type = case account_type
when 'C' then 'A'
when 'D' then 'B'
else account_type
end
where account_num =
(select account_num
from year_end_close
where account_type in('C', 'D'))`
I get "Subquery returned more than 1 value" from the where clause - what am I doing wrong? When I comment out the where clause I no longer get that error, so it's something in that clause.
Change your = to IN
UPDATE master
SET account_type =
CASE account_type
WHEN 'C' then 'A'
WHEN 'D' then 'B'
ELSE account_type
END
WHERE account_num IN
(
SELECT account_num
FROM year_end_close
WHERE account_type IN('C', 'D')
)
By using the = you are saying that you are going to compare 1 item from the left side of the = with 1 item from the right side. There are more than one account_num in year_end_close with an account_type of C or D. This makes your sub-query return more than 1 result. The query is unable to determine which value from the sub-query should match the value on the left. Using the IN allows the query to check the left value for any valid account_num from your sub-query.
Try an IN rather than checking for equality:
update master
set account_type = case account_type
when 'C' then 'A'
when 'D' then 'B'
else account_type
end
where account_num IN (select account_num from year_end_close where account_type in('C', 'D'))`
...though it rather depends on your data/schema.
Other answers from #matt and #Adam are correct - you need to use In rather than = if you stick to a subquery. But I think this is the same as:
UPDATE m Set
account_type =
Case account_type
WHEN 'C' then 'A'
WHEN 'D' then 'B'
ELSE account_type END
From master m Join year_end_close c
On c.account_num = m.account_num
And c.account_type in ('C', 'D')
EDIT: This syntax works in MS Sql Server but may not work in all other vendors' database products.
Which database are you using?

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

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
)