PL/SQL Case When - Map input to other column values - sql

I've checked elsewhere on Stack but I couldn't find a similar scenario.
I want to essentially map 1 input from a PL/SQL procedure to 3 different values in MY_TABLE; otherwise I just want to filter on the input. I would like a solution similar to the below pseudocode
SELECT * FROM MY_TABLE
WHERE COLUMN 1 = 'B'
AND CASE WHEN p_input = 'F' THEN COLUMN_1 IN ('F','A','B')
ELSE COLUMN_1 = p_input;

I don't recommend using case expressions in the where. Just use simpler boolean logic operators:
WHERE COLUMN 1 = 'B' AND
( (p_input = 'F' AND COLUMN_1 IN ('F', 'A', 'B')) OR
COLUMN_1 = p_input
)
In particular, Oracle doesn't have a boolean type in SQL (there is on in PL/SQL). So a case expression cannot return a boolean value.

If you are learning how to use case..when in where clause then following is the code:
CASE WHEN p_input = 'F' AND COLUMN_1 IN ('F','A','B') THEN 1
WHEN COLUMN_1 = p_input THEN 1
END = 1
But as suggested in other answer, it is recomended to use or And it is always a good idea to use negative condition p_input <> 'F' in second condition while using OR as follows:
(
(p_input = 'F' AND COLUMN_1 IN ('F', 'A', 'B'))
OR
(p_input <> 'F' AND COLUMN_1 = p_input)
)

Related

SQL case when multiple records

I have 4 country columns that keep a track of the books in the specific region. For example, EN='Y', GE='N', and so on. I am creating a view and want to add each record in the available country column. So if EN='Y' and GE='Y' it would list them as 'en, ge' in the available country. So far what I have is:
case
when EN = 'Y' and GE = 'Y'
then 'EN, GE'
when EN = 'N' and GE = 'Y'
then 'GE'
end as 'available country'
This works but I have 4 more columns and might add more and defining each condition feels insufficient.
Is there a better way to concatenate values instead of specifying each one condition?
You could use CONCAT_WS with CASE expressions:
SELECT CONCAT_WS(',', CASE WHEN EN = 'Y' THEN 'EN' END,
CASE WHEN GE = 'Y' THEN 'GE' END,
...) AS "available country"
FROM yourTable;
But a much better table design would be something like this:
id | language | val
1 | EN | Y
1 | GE | N
Using this design, you could simply aggregate by id and filter for only those languages which have yes values, e.g. something like this:
SELECT id, ARRAY_AGG(language)
FROM yourTable
WHERE val = 'Y'
GROUP BY id;
You can turn the columns into rows (what they should be in the first place), then use string_agg() to get the comma separated list:
select t.id, f.*
from the_table t
cross join lateral (
select string_agg(colname, ',') as available_countries
from (
values (en, 'EN'),
(ge, 'GE'),
(de, 'DE'),
(fr, 'FR')
) x(flag, colname)
where flag = 'Y'
) as f
Unfortunately, I think you will have to use CASE WHEN with you current database design. Please check if this works for you.
select TRIM(BOTH ',' FROM c1_new||c2_new||c3_new||c4_new) as concat_col
from
(
select case when c1 = 'Y' then 'C1,' else null end as c1_new,
case when c2 = 'Y' then 'C2,' else null end as c2_new,
case when c3 = 'Y' then 'C3,' else null end as c3_new,
case when c4 = 'Y' then 'C4' else null end as c4_new
from TABLE
)
I have not worked in PostgreSQL. So if anybody needs Oracle equivalent :
SELECT TRIM (
TRAILING ',' FROM ( DECODE (c1, 'Y', 'c1' || ',', NULL)
|| DECODE (c2, 'Y', 'c2' || ',', NULL)
|| DECODE (c3, 'Y', 'c3' || ',', NULL)
|| DECODE (c4, 'Y', 'c4' || ',', NULL)))
FROM (SELECT 'Y' c1, 'Y' c2,'N' c3,'Y' c4 FROM DUAL)
This query returns :
c1,c2,c4

Oracle: CASE conditional STATEMENT in the WHERE Clause is not working?

I have below query with CASE statement and this is trowing "missing keyword error"
Can you please help.
select *
from podConfigKey_Tab PCK
WHERE
CASE WHEN (PCK.Keyid = 'TLMAPIConfigMgr.UseDB'
and PCK.DEFAULTKEYIDVALUE = 'FALSE')
THEN PCK.Keyid = 'TLMAPIConfigMgr.UseDB'
ELSE PCK.Keyid != 'TLMAPIConfigMgr.UseDB'
END;
A case expression returns a single value, not a syntactic construct like a=b. You could, however, emulate this behavior with a series of logical operators:
SELECT *
FROM podConfigKey_Tab PCK
WHERE PCK.DEFAULTKEYIDVALUE = 'FALSE' OR
PCK.Keyid != 'TLMAPIConfigMgr.UseDB'
your query should be something more like as mentioned below (removed the else portion to make the below query work), you need to have predicate after the WHERE clause so that you can match value that is return by the CASE statment
select * from podConfigKey_Tab PCK
WHERE PCK.Keyid =
CASE WHEN (PCK.Keyid = 'TLMAPIConfigMgr.UseDB' and PCK.DEFAULTKEYIDVALUE = 'FALSE') THEN 'TLMAPIConfigMgr.UseDB'
END ;
The Oracle CASE expression (like DECODE) returns a value, but by itself it is not a predicate which can evaluate to TRUE or FALSE. You need to establish some condition such that the value returned by the CASE statement can be evaluated. For example:
with sample_data as
(select 'dog' pet, 'y' has_fur from dual union all
select 'cat', 'y' from dual union all
select 'bird', 'n' from dual)
select *
from sample_data
where (case when has_fur = 'y' then 1 else 0 end) = 1;
SQL Fiddle Example

Case when statement in SQL

I am using the following query. In this query I want to apply the where clause based on passed parameter. But the issue is that where clause is like 'value = if parameterVal = 'I' than NULL else NOT NULL'
I've build a query like this
SELECT * FROM MASTER
WHERE
Column1 IS (CASE WHEN :Filter = 'I' THEN 'NULL' ELSE 'NOT NULL' END)
but it's not working. Help me solve this.
UPDATE
Updating question to elaborate question more clearly.
I've one table MASTER. Now I am passing one parameter in query that is Filter (indicated by :Filter in query).
Now when the Filter parameter's value is 'I' than it should return the following result.
SELECT * FROM MASTER WHERE Column1 IS NULL
but if the passed argument is not equal to 'I' than,
SELECT * FROM MASTER WHERE Column1 IS NOT NULL
SELECT * FROM MASTER
WHERE (Filter = 'I' AND Column1 IS NULL)
OR
(Filter <> 'I' AND Column1 IS NOT NULL)
If you really insist on using a CASE the SELECT could be rewritten as:
SELECT *
FROM MASTER
WHERE CASE
WHEN COLUMN1 IS NULL AND FILTER = 'I' THEN 1
WHEN COLUMN1 IS NOT NULL AND FILTER <> 'I' THEN 1
ELSE 0
END = 1
SQLFiddle here
Frankly, though, I think that this is very difficult to interpret, and I suggest that #MAli's version is better.
Your case has assignment not equality check

CASE in WHERE expression using IN clause

I need that when var1 is equal to 'Y' the query show the result of the table with the column = 'Y', but when var1 = 'N' the result should be the data with column with 'Y' and 'N'.
I need to put it in a where clause, cause I'm using oracle forms.
I tried this way but the query didn't show any result:
SELECT *
FROM table
WHERE column1 IN ((CASE WHEN var1 = 'Y' THEN q'[('Y')]'
ELSE TO_CHAR(q'[('Y','N')]')
END))
Can you help me? Thank you.
There is no need for CASE logic here, as you can fit this into a regular WHERE clause with boolean logic, wrapping each condition (var1 = 'Y', var1 <> 'Y') in a () group.
SELECT *
FROM table
WHERE
(var1 = 'Y' AND column1 = 'Y')
OR (var1 <> 'Y' AND column1 IN ('Y','N'))
Note, I used var1 <> 'Y' here to emulate your ELSE case, but if it is only two possible values Y/N you may use var1 = 'N' for clarity.
WHERE
(var1 = 'Y' AND column1 = 'Y')
OR (var1 = 'N' AND column1 IN ('Y','N'))
Actually, if Y/N are the only possible values for column1, then it could be simplified to:
WHERE
(var1 = 'Y' AND column1 = 'Y')
-- Returns all rows for column1 if Y,N are the only possible values
-- No need to explicitly filter it
OR (var1 <> 'Y')

How do I perform an IF...THEN in an SQL SELECT?

How do I perform an IF...THEN in an SQL SELECT statement?
For example:
SELECT IF(Obsolete = 'N' OR InStock = 'Y' ? 1 : 0) AS Saleable, * FROM Product
The CASE statement is the closest to IF in SQL and is supported on all versions of SQL Server.
SELECT CAST(
CASE
WHEN Obsolete = 'N' or InStock = 'Y'
THEN 1
ELSE 0
END AS bit) as Saleable, *
FROM Product
You only need to use the CAST operator if you want the result as a Boolean value. If you are happy with an int, this works:
SELECT CASE
WHEN Obsolete = 'N' or InStock = 'Y'
THEN 1
ELSE 0
END as Saleable, *
FROM Product
CASE statements can be embedded in other CASE statements and even included in aggregates.
SQL Server Denali (SQL Server 2012) adds the IIF statement which is also available in access (pointed out by Martin Smith):
SELECT IIF(Obsolete = 'N' or InStock = 'Y', 1, 0) as Saleable, * FROM Product
The case statement is your friend in this situation, and takes one of two forms:
The simple case:
SELECT CASE <variable> WHEN <value> THEN <returnvalue>
WHEN <othervalue> THEN <returnthis>
ELSE <returndefaultcase>
END AS <newcolumnname>
FROM <table>
The extended case:
SELECT CASE WHEN <test> THEN <returnvalue>
WHEN <othertest> THEN <returnthis>
ELSE <returndefaultcase>
END AS <newcolumnname>
FROM <table>
You can even put case statements in an order by clause for really fancy ordering.
From SQL Server 2012 you can use the IIF function for this.
SELECT IIF(Obsolete = 'N' OR InStock = 'Y', 1, 0) AS Salable, *
FROM Product
This is effectively just a shorthand (albeit not standard SQL) way of writing CASE.
I prefer the conciseness when compared with the expanded CASE version.
Both IIF() and CASE resolve as expressions within a SQL statement and can only be used in well-defined places.
The CASE expression cannot be used to control the flow of execution of
Transact-SQL statements, statement blocks, user-defined functions, and
stored procedures.
If your needs can not be satisfied by these limitations (for example, a need to return differently shaped result sets dependent on some condition) then SQL Server does also have a procedural IF keyword.
IF #IncludeExtendedInformation = 1
BEGIN
SELECT A,B,C,X,Y,Z
FROM T
END
ELSE
BEGIN
SELECT A,B,C
FROM T
END
Care must sometimes be taken to avoid parameter sniffing issues with this approach however.
You can find some nice examples in The Power of SQL CASE Statements, and I think the statement that you can use will be something like this (from 4guysfromrolla):
SELECT
FirstName, LastName,
Salary, DOB,
CASE Gender
WHEN 'M' THEN 'Male'
WHEN 'F' THEN 'Female'
END
FROM Employees
Use CASE. Something like this.
SELECT Salable =
CASE Obsolete
WHEN 'N' THEN 1
ELSE 0
END
SELECT
(CASE
WHEN (Obsolete = 'N' OR InStock = 'Y') THEN 'YES'
ELSE 'NO'
END) as Salable
, *
FROM Product
Microsoft SQL Server (T-SQL)
In a select, use:
select case when Obsolete = 'N' or InStock = 'Y' then 'YES' else 'NO' end
In a where clause, use:
where 1 = case when Obsolete = 'N' or InStock = 'Y' then 1 else 0 end
From this link, we can understand IF THEN ELSE in T-SQL:
IF EXISTS(SELECT *
FROM Northwind.dbo.Customers
WHERE CustomerId = 'ALFKI')
PRINT 'Need to update Customer Record ALFKI'
ELSE
PRINT 'Need to add Customer Record ALFKI'
IF EXISTS(SELECT *
FROM Northwind.dbo.Customers
WHERE CustomerId = 'LARSE')
PRINT 'Need to update Customer Record LARSE'
ELSE
PRINT 'Need to add Customer Record LARSE'
Isn't this good enough for T-SQL?
SELECT
CASE
WHEN OBSOLETE = 'N' or InStock = 'Y' THEN 'TRUE'
ELSE 'FALSE'
END AS Salable,
*
FROM PRODUCT
Simple if-else statement in SQL Server:
DECLARE #val INT;
SET #val = 15;
IF #val < 25
PRINT 'Hi Ravi Anand';
ELSE
PRINT 'By Ravi Anand.';
GO
Nested If...else statement in SQL Server -
DECLARE #val INT;
SET #val = 15;
IF #val < 25
PRINT 'Hi Ravi Anand.';
ELSE
BEGIN
IF #val < 50
PRINT 'what''s up?';
ELSE
PRINT 'Bye Ravi Anand.';
END;
GO
Use a CASE statement:
SELECT CASE
WHEN (Obsolete = 'N' OR InStock = 'Y')
THEN 'Y'
ELSE 'N'
END as Available
etc...
A new feature, IIF (that we can simply use), was added in SQL Server 2012:
SELECT IIF ( (Obsolete = 'N' OR InStock = 'Y'), 1, 0) AS Saleable, * FROM Product
Use pure bit logic:
DECLARE #Product TABLE (
id INT PRIMARY KEY IDENTITY NOT NULL
,Obsolote CHAR(1)
,Instock CHAR(1)
)
INSERT INTO #Product ([Obsolote], [Instock])
VALUES ('N', 'N'), ('N', 'Y'), ('Y', 'Y'), ('Y', 'N')
;
WITH cte
AS
(
SELECT
'CheckIfInstock' = CAST(ISNULL(NULLIF(ISNULL(NULLIF(p.[Instock], 'Y'), 1), 'N'), 0) AS BIT)
,'CheckIfObsolote' = CAST(ISNULL(NULLIF(ISNULL(NULLIF(p.[Obsolote], 'N'), 0), 'Y'), 1) AS BIT)
,*
FROM
#Product AS p
)
SELECT
'Salable' = c.[CheckIfInstock] & ~c.[CheckIfObsolote]
,*
FROM
[cte] c
See working demo: if then without case in SQL Server.
For start, you need to work out the value of true and false for selected conditions. Here comes two NULLIF:
for true: ISNULL(NULLIF(p.[Instock], 'Y'), 1)
for false: ISNULL(NULLIF(p.[Instock], 'N'), 0)
combined together gives 1 or 0. Next use bitwise operators.
It's the most WYSIWYG method.
SELECT 1 AS Saleable, *
FROM #Product
WHERE ( Obsolete = 'N' OR InStock = 'Y' )
UNION
SELECT 0 AS Saleable, *
FROM #Product
WHERE NOT ( Obsolete = 'N' OR InStock = 'Y' )
SELECT CASE WHEN profile.nrefillno = 0 THEN 'N' ELSE 'R'END as newref
From profile
case statement some what similar to if in SQL server
SELECT CASE
WHEN Obsolete = 'N' or InStock = 'Y'
THEN 1
ELSE 0
END as Saleable, *
FROM Product
This isn't an answer, just an example of a CASE statement in use where I work. It has a nested CASE statement. Now you know why my eyes are crossed.
CASE orweb2.dbo.Inventory.RegulatingAgencyName
WHEN 'Region 1'
THEN orweb2.dbo.CountyStateAgContactInfo.ContactState
WHEN 'Region 2'
THEN orweb2.dbo.CountyStateAgContactInfo.ContactState
WHEN 'Region 3'
THEN orweb2.dbo.CountyStateAgContactInfo.ContactState
WHEN 'DEPT OF AGRICULTURE'
THEN orweb2.dbo.CountyStateAgContactInfo.ContactAg
ELSE (
CASE orweb2.dbo.CountyStateAgContactInfo.IsContract
WHEN 1
THEN orweb2.dbo.CountyStateAgContactInfo.ContactCounty
ELSE orweb2.dbo.CountyStateAgContactInfo.ContactState
END
)
END AS [County Contact Name]
If you're inserting results into a table for the first time, rather than transferring results from one table to another, this works in Oracle 11.2g:
INSERT INTO customers (last_name, first_name, city)
SELECT 'Doe', 'John', 'Chicago' FROM dual
WHERE NOT EXISTS
(SELECT '1' from customers
where last_name = 'Doe'
and first_name = 'John'
and city = 'Chicago');
As an alternative solution to the CASE statement, a table-driven approach can be used:
DECLARE #Product TABLE (ID INT, Obsolete VARCHAR(10), InStock VARCHAR(10))
INSERT INTO #Product VALUES
(1,'N','Y'),
(2,'A','B'),
(3,'N','B'),
(4,'A','Y')
SELECT P.* , ISNULL(Stmt.Saleable,0) Saleable
FROM
#Product P
LEFT JOIN
( VALUES
( 'N', 'Y', 1 )
) Stmt (Obsolete, InStock, Saleable)
ON P.InStock = Stmt.InStock OR P.Obsolete = Stmt.Obsolete
Result:
ID Obsolete InStock Saleable
----------- ---------- ---------- -----------
1 N Y 1
2 A B 0
3 N B 1
4 A Y 1
SELECT CASE WHEN Obsolete = 'N' or InStock = 'Y' THEN 1 ELSE 0
END AS Saleable, *
FROM Product
You can have two choices for this to actually implement:
Using IIF, which got introduced from SQL Server 2012:
SELECT IIF ( (Obsolete = 'N' OR InStock = 'Y'), 1, 0) AS Saleable, * FROM Product
Using Select Case:
SELECT CASE
WHEN Obsolete = 'N' or InStock = 'Y'
THEN 1
ELSE 0
END as Saleable, *
FROM Product
Using SQL CASE is just like normal If / Else statements.
In the below query, if obsolete value = 'N' or if InStock value = 'Y' then the output will be 1. Otherwise the output will be 0.
Then we put that 0 or 1 value under the Salable Column.
SELECT
CASE
WHEN obsolete = 'N' OR InStock = 'Y'
THEN 1
ELSE 0
END AS Salable
, *
FROM PRODUCT
Question:
SELECT IF(Obsolete = 'N' OR InStock = 'Y' ? 1 : 0) AS Saleable, * FROM Product
ANSI:
Select
case when p.Obsolete = 'N'
or p.InStock = 'Y' then 1 else 0 end as Saleable,
p.*
FROM
Product p;
Using aliases -- p in this case -- will help prevent issues.
SELECT
if((obsolete = 'N' OR instock = 'Y'), 1, 0) AS saleable, *
FROM
product;
For those who uses SQL Server 2012, IIF is a feature that has been added and works as an alternative to Case statements.
SELECT IIF(Obsolete = 'N' OR InStock = 'Y', 1, 0) AS Salable, *
FROM Product
It will be something like that:
SELECT OrderID, Quantity,
CASE
WHEN Quantity > 30 THEN "The quantity is greater than 30"
WHEN Quantity = 30 THEN "The quantity is 30"
ELSE "The quantity is under 30"
END AS QuantityText
FROM OrderDetails;
I like the use of the CASE statements, but the question asked for an IF statement in the SQL Select. What I've used in the past has been:
SELECT
if(GENDER = "M","Male","Female") as Gender
FROM ...
It's like the Excel or sheets IF statements where there is a conditional followed by the true condition and then the false condition:
if(condition, true, false)
Furthermore, you can nest the if statements (but then use should use a CASE :-)
(Note: this works in MySQL Workbench, but it may not work on other platforms)
For the sake of completeness, I would add that SQL uses three-valued logic. The expression:
obsolete = 'N' OR instock = 'Y'
Could produce three distinct results:
| obsolete | instock | saleable |
|----------|---------|----------|
| Y | Y | true |
| Y | N | false |
| Y | null | null |
| N | Y | true |
| N | N | true |
| N | null | true |
| null | Y | true |
| null | N | null |
| null | null | null |
So for example if a product is obsolete but you dont know if product is instock then you dont know if product is saleable. You can write this three-valued logic as follows:
SELECT CASE
WHEN obsolete = 'N' OR instock = 'Y' THEN 'true'
WHEN NOT (obsolete = 'N' OR instock = 'Y') THEN 'false'
ELSE NULL
END AS saleable
Once you figure out how it works, you can convert three results to two results by deciding the behavior of null. E.g. this would treat null as not saleable:
SELECT CASE
WHEN obsolete = 'N' OR instock = 'Y' THEN 'true'
ELSE 'false' -- either false or null
END AS saleable
There are multiple conditions.
SELECT
(CASE
WHEN RIGHT((LEFT(POSID,5)),4) LIKE '1001' THEN 'DM'
WHEN RIGHT((LEFT(POSID,5)),4) LIKE '1002' THEN 'GS'
WHEN RIGHT((LEFT(POSID,5)),4) LIKE '1003' THEN 'MB'
WHEN RIGHT((LEFT(POSID,5)),4) LIKE '1004' THEN 'MP'
WHEN RIGHT((LEFT(POSID,5)),4) LIKE '1005' THEN 'PL'
WHEN RIGHT((LEFT(POSID,5)),4) LIKE '1008' THEN 'DM-27'
WHEN RIGHT((LEFT(POSID,5)),4) LIKE '1011' THEN 'PB'
WHEN RIGHT((LEFT(POSID,5)),4) LIKE '1012' THEN 'UT-2'
WHEN RIGHT((LEFT(POSID,5)),4) LIKE '1013' THEN 'JGC'
WHEN RIGHT((LEFT(POSID,5)),4) LIKE '1014' THEN 'SB'
WHEN RIGHT((LEFT(POSID,5)),4) LIKE '1015' THEN 'IR'
WHEN RIGHT((LEFT(POSID,5)),4) LIKE '1016' THEN 'UT-3'
WHEN RIGHT((LEFT(POSID,5)),4) LIKE '1017' THEN 'UT-4'
WHEN RIGHT((LEFT(POSID,5)),4) LIKE '1019' THEN 'KR'
WHEN RIGHT((LEFT(POSID,5)),4) LIKE '1020' THEN 'SYB-SB'
WHEN RIGHT((LEFT(POSID,5)),4) LIKE '1021' THEN 'GR'
WHEN RIGHT((LEFT(POSID,5)),4) LIKE '1022' THEN 'SYB-KP'
WHEN RIGHT((LEFT(POSID,5)),4) LIKE '1026' THEN 'BNS'
ELSE ''
END) AS OUTLET
FROM matrixcrm.Transact