Can a CASE expression have 2 resultant values - sql

I have to write a case expression in SQL which goes like this,
case condition
if (T_CD = 'Y')
Case C_CD = 'H3'
set R_ID = 3 and RS_ID = 25
CASE A_FLG = 'N' and Mod = 'D'
set R_ID = 3 and RS_ID = 31
Both R_ID and RS_ID populate columns in a different table and have to be derived as per condition above.
My question is - Since I want 2 separate fields out of my case expression, will a single Case give out 2 resultant field values for me. Or Do I have to write 2 different case expressions for it.

If your dbms supports row types, maybe this works for you:
select case when a = 1 then (1,2) else (3,4) end from testtable;
The SQL Validator says:
The following feature outside Core SQL-2003 is used:
T051, "Row types"

Related

Using case statement to compare columns

I'd like to use a case statement to compare multiple rows on 2 columns. For example, if row 1 column 1 and row 2 column 1 match but row 1 column 2 and row 2 column 2 don't, then xx. I have
CASE
WHEN (pprof.description = pprof.description and PCtrl.[sequence] <> PCtrl.[sequence])
THEN xx
but that doesn't return any values, which I know to be incorrect. I'm new to SQL so apologies if I've got this all wrong.
Edit:
Here's some sample data:
Column 1
Column 2
Column 3
123
-A
-No
123
-B
-Yes
Can't figure out the formatting here but there are 3 columns of data above. I'd like the case statement to evaluate whether column 1 match in 2 different rows (i.e., 123 = 123) and also whether column 2 doesn't (A <> B) and if both those conditions are true, return a value in column 3 (in my case, make the No a Yes, since 123-B is Yes). It might be worth noting that the "Yes" and "No" themselves are built into the larger case statement here:
(CASE WHEN tenure.description not in ('casual','co-op','fswep') THEN CASE WHEN (pprof.description = pprof.description and PCtrl.[sequence] <> PCtrl.[sequence])
THEN CASE WHEN (PEmp.Employee_Number = PEmp3.Supervisor_Number) THEN 'Yes' ELSE 'No' END END END) as 'People Manager'
You will want to do a self join here. Without seeing your data, I can't really give you an answer, but you want something like this.
Update A
Set column_3 = 'YES' /* or put CASE statement here /*
FROM pprof A
INNER JOIN pprof B
ON a.description = b.description
AND a.sequence != b.sequence
You may need more join conditions depending on the form of your data and what you want.

How to set flag based on values in previous columns in same table ? (Oracle)

I'm creating a new table and carrying over several columns from a previous table. One of the new fields that I need to create is a flag that will have values 0 or 1 and value needs to be determined based on 6 previous fields in the table.
The 6 previous columns have preexisting values of 0 or 1 stored for each one. This new field needs to check whether any of the 6 columns have 1 and if so set the flag to 0. If there is 0 in all 6 fields then set itself to 1.
Hopefully this makes sense. How can I get this done in oracle? I assume a case statement and some sort of forloop?
You can use greatest() function: GREATEST
create table t_new
as
select
case when greatest(c1,c2,c3,c4,c5,c6)=1 -- at least one of them contains 1
then 0
else 1
end c_new
from t_old;
Or even shorter:
create table t_new
as
select
1-greatest(c1,c2,c3,c4,c5,c6) as c_new
from t_old;
In case of greatest = 1, (1-1)=0, otherwise (1-0)=1
You can use a virtual column with a case expression; something like:
flag number generated always as (
case when val_1 + val_2 + val_3 + val_4 + val_5 + val_6 = 0 then 1 else 0 end
) virtual
db<>fiddle
or the same thing with greatest() as #Sayan suggested.
Using a virtual column means the flag will be right for newly-inserted rows, and if any of the other values are updated; you won't have to recalculate or update the flag column manually.
I've assumed the other six columns can't be null and are constrained to only be 0 or 1, as the question suggests. If they can be null you can add nvl() or coalesce() to each term in the calculation.

Max match same numbers from each row

To generate 1mln rows of report with the below mentioned script is taking almost 2 days so, really appreciate if somebody could help me with different script which the report can be generated within 10-15mins please.
The requirement of the report is as following;
Table “cover” contains 5mln rows & 6 columns of data and likewise table “data” contains 500,000 rows and 6 columns.
So, each numbers of the rows in table cover has to go through table date and provide the maximum matches.
For instance, as mentioned on the below tables, there could be 3 matches in row #1, 2 matches in row #2 and 5 matches in row #3 so the script has to select the max selection which is 5 in row #3.
Sample table
UPDATE public.cover_sheet AS fc
SET maxmatch = (SELECT MAX(tmp.mtch)
FROM (
SELECT (SELECT CASE WHEN fc.a=drwo.a THEN 1 ELSE 0 END) +
(SELECT CASE WHEN fc.b=drwo.b THEN 1 ELSE 0 END) +
(SELECT CASE WHEN fc.c=drwo.c THEN 1 ELSE 0 END) +
(SELECT CASE WHEN fc.d=drwo.d THEN 1 ELSE 0 END) +
(SELECT CASE WHEN fc.e=drwo.e THEN 1 ELSE 0 END) +
(SELECT CASE WHEN fc.f=drwo.f THEN 1 ELSE 0 END) AS mtch
FROM public.data AS drwo
) AS tmp)
WHERE fc.code>0;
SELECT *
FROM public.cover_sheet AS fc
WHERE fc.maxmatch>0;
As #a_horse_with_no_name mentioned in the comment to the question, your question is not clear...
Seems, you want to get the number of records which 6 fields from both tables are equal.
I'd suggest to:
reduce the number of select statements, then the speed of query execution will increase,
split your query into few smaller ones (good practice), to check your logic,
use join to get equal data, see: Visual Representation of SQL Joins
use subquery or cte to get result on which you'll be able to update table.
I think you want to get result as follow:
SELECT COUNT(*) mtch
FROM public.cover_sheet AS fc INNER JOIN public.data AS drwo ON
fc.a=drwo.a AND fc.b=drwo.b AND fc.c=drwo.c AND fc.d=drwo.d AND fc.e=drwo.e AND fc.f=drwo.f
If i'm not wrong and above query is correct, the time of execution of above query will reduce to about 1-2 minutes.
Finally, update query may look like:
WITH qry AS
(
-- proper select statement here
)
UPDATE public.cover_sheet AS fc
SET maxmatch = qry.<fieldname>
FROM qry
WHERE fc.code>0 AND fc.<key> = qry.<key>;
Note:
I do not see your data and i know nothing about its structure, relationships, etc. So, you have to change above query to your needs.

Returning several values within a CASE expression in subquery and separate columns again in main query

My test table looks like this:
# select * from a;
source | target | id
--------+--------+----
1 | 2 | 1
2 | 3 | 2
3 | 0 | 3
My query is this one:
SELECT *
FROM (
SELECT
CASE
WHEN id<>1
THEN source
ELSE 0
END
AS source,
CASE
WHEN id<>1
THEN target
ELSE 0
END
AS target
FROM a
) x;
The query seems a bit odd because the CASE expression with the same criteria is repeated for every column. I would like to simplify this and tried the following, but it doesn't work as expected.
SELECT *
FROM (
SELECT
CASE
WHEN id<>1
THEN (source, target)
ELSE (0, 0)
END
AS r
FROM a
) x;
It yields one column with a row value, but I would rather get the two original columns. Separating them with a (r).* or similar doesn't work, because the "record type has not been registered".
I found several questions here with solutions regarding functions returning RECORD values, but none regarding this example with a sub-select.
Actually, there is a quite long list of columns, so repeating the same CASE expression many times makes the whole query quite unreadable.
Since the real problem - as opposed to this simplified case - consists of several CASE expressions and several column groups, a solution with a UNION won't help, because the number of UNIONs would be large and make it unreadable as well as several CASEs.
My actual question is: How can I get the original columns from the row value?
This answers the original question.
If I understood your needs, you want 0 and 0 for source and target when id = 1:
SELECT
0 AS source,
0 AS target
FROM tablename
WHERE id = 1
UNION ALL
SELECT
source,
target
FROM tablename
WHERE id <> 1
Revised answer: You can make your query work (fixing the record type has not been registered issue) by creating a TYPE:
CREATE TYPE stpair AS (source int, target int);
And cast the composite value column to that type:
SELECT id, (cv).source, (cv).target
FROM (
SELECT id, CASE
WHEN id <> 1 THEN (source, target)::stpair
ELSE (0, 0)::stpair
END AS cv
FROM t
) AS x
Having said that, it should be far more convenient to use arrays:
SELECT id, av[1] AS source, av[2] AS target
FROM (
SELECT id, CASE
WHEN id <> 1 THEN ARRAY[source, target]
ELSE ARRAY[0, 0]
END AS av
FROM t
) AS x
Demo on db<>fiddle
Will this work for you?
select source,target,id from a where id <>1 union all select 0 as source,0 as target,id from a where id=1 order by id
I have used union all to included cases where multiple records may have ID=1

SQL How to Count Number of Specific Values in a Row

Lets say I have a table that has 50 Fields. 20 of those fields can contain the Value "YES" "NO" or "N/A". How do I query the number of "YES"s for a given row?
You write a long statement that adds up the values:
select ((case when value1 = 'Yes' then 1 else 0 end) +
(case when value2 = 'Yes' then 1 else 0 end) +
. . .
(case when value50 = 'Yes' then 1 else 0 end)
) as NumYesses
This would be much easier if you normalized the data, so each value was in a separate row. You would do this by having a separate table, called a junction or association table.
Also, you can generate this code in a spreadsheet, such as Excel, by using formulas on the columns names (or by writing a query that uses metadata in your database).
Note: this is generic ANSI SQL, because you don't specify the database. There may be some shortcuts to writing the code in different databases.