Why the case when doesn't work in oracle - sql

I have a query like the below showed.
select * from tbl a
WHERE a.device_cat =
(CASE (SELECT :VIEW1
FROM DUAL
WHERE :VIEW1 IN
(SELECT DISTINCT version
FROM tbl2))
WHEN NULL
THEN
NULL
ELSE
DECODE (:device_cat, 'ALL', a.device_cat, :device_cat)
END)
So, when the below query is null, a.device_cat should be null, if so, the above query will always return empty records. But, the records are definitely exists when a.device_cat is null. Please help me! Thanks!
(SELECT :VIEW1
FROM DUAL
WHERE :VIEW1 IN
(SELECT DISTINCT version
FROM tbl2)

You cannot compare to null like this:
a.device_cat =null
Try this:
select * from tbl a
WHERE nvl(a.device_cat, 0) =
nvl((CASE (SELECT :VIEW1
FROM DUAL
WHERE :VIEW1 IN
(SELECT DISTINCT version
FROM tbl2))
WHEN null
THEN
null
ELSE
DECODE (:device_cat, 'ALL', a.device_cat, :device_cat)
END), 0)

You cannot compare null to null like that.
It is like:
undefined = undefined
Which is not true... Something you don't know isn't something you don't know. At least, in SQL.
You can use coalesce to circumvent this:
WHERE coalesce(a.device_cat, '###') =
(CASE (SELECT coalesce(:VIEW1, '###')

The WHEN NULL case will never be reached, because in SQL NULLs are not equal to each other.
Switching to the other CASE syntax should work, though:
CASE
WHEN (
SELECT :VIEW1 FROM DUAL WHERE :VIEW1 IN (SELECT DISTINCT version FROM tbl2)
) IS NULL
THEN NULL
ELSE DECODE (:device_cat, 'ALL', a.device_cat, :device_cat)
END
This expression uses IS NULL operator instead of the comparison, providing the desired behavior.

Related

SSRS cannot parse SQL query

I am trying to define an SQL query in my SSRS report, but I am getting some syntax errors:
Error in SELECT clause: expression near 'WHEN'.
Missing FROM clause.
Error in SELECT clause: expression near ','.
Unable to parse query text.
The query is not that complicated, but rather a bit convoluted, but I'll try to convey at least the structure of it here:
select
(CASE WHEN columnA1 is null THEN columnA2 ELSE columnA1 END) as columnA,
(CASE WHEN columnB1 is null THEN columnB2 ELSE columnB1 END) as "custom_name_for_columnB"
from
(
(select a.columnA1, ...
from myTable a, ...
// join conditions
)
union
select * from
(select a.columnA1, ...
from myTable a, ...
// join conditions
order by someColumn) source
)
);
I don't think it really matters what the query does since I ran it in my DMBS successfully, so I'm pretty sure it's correct SQL syntax (I'm working on Oracle DB). I think what I'm not seeing is some syntax specific to SSRS. I'm completely new to it, so I don't know whether it supports the entire SQL syntax like CASE WHEN, unions etc.
As it complains about CASE (as if it doesn't recognize the syntax), try some more options:
COALESCE:
select coalesce(columnA1, columnA2) columnA,
coalesce(columnB1, columnB2) columnB
from ...
NVL:
select nvl(columnA1, columnA2) columnA,
nvl(columnB1, columnB2) columnB
from ...
DECODE:
select decode(columnA1, null, columnA2, columnA1) columnA,
decode(columnB1, null, columnB2, columnB1) columnB
from ...
Correct my query if I made too many changes, but I think there are a couple of things wrong:
SELECT (CASE WHEN t.columnA1 IS NULL THEN t.columnA2 ELSE t.columnA1 END) as columnA,
(CASE WHEN t.columnB1 IS NULL THEN t.columnB2 ELSE t.columnB1 END) as [custom_name_for_columnB]
FROM(
SELECT a.columnA1, ...
FROM myTable a, ...
JOIN conditions
UNION
SELECT * FROM
(
SELECT a.columnA1, ...
FROM m myTable a, ...
JOIN conditions
--ORDER BY someColumn --Can't have ORDER BY in subquery without TOP(n) / FOR XML.
) source
)t; --Needs an alias

Oracle case when NOT null?

I see a lot of queries made for Oracle using CASE columnX WHEN NULL
how to design query if
CASE columnX WHEN NOT NULL ?
SQL developer throws an error on the query so how to make condition query using CASE... WHEN...?
WITH dates_list AS
(SELECT TO_DATE('02-19-2018','MM-dd-yyyy') + ROWNUM - 1 AS DAY,
rownum AS row_num
FROM dual
CONNECT BY LEVEL <= (TO_DATE('03-29-2018','MM-dd-yyyy') - TO_DATE('02-19-2018','MM-dd-yyyy')+1)
)
SELECT dates1.day, dates2.*, count(case dates2.day when null then 1 else 0 end)
over (partition by dates2.day order by dates1.day) as cnt
FROM dates_list dates1
LEFT JOIN
(SELECT *
FROM dates_list
WHERE TO_CHAR(DAY,'D') NOT IN (7,1)
) dates2 ON dates1.day = dates2.day
ORDER BY dates1.day;
above query gives error when I change when null to when not null
You can't use the CASE sentence in that way with NULL values, because columnX is never "equal" to NULL. You should use instead:
CASE WHEN columnX IS NULL THEN
or
CASE WHEN columnX IS NOT NULL THEN
You now have a function NVL that can turn this:
CASE WHEN columnX IS NOT NULL THEN columnX ELSE 'No value' END
Into this:
NVL(columnX, 'No value')
If columnX is a select, it make everything a lot cleaner

ORACLE: USE RESULT OF CASE-WHEN-STATEMENT

I have a huge query and I am wondering if it is in Oracle possible
to get the result of a case-when-statement and use it for comparison? My CASE-STATEMENT is declared in the Select-Statement and it looks like this.
SELECT........
(CASE
WHEN (Select 1 from DUAL) = 1 THEN 'TEST'
ELSE 'TEST2'
END) AS TEST;
Now I want to get the result of this case-statement and use it in the where part? Is it possible? (Sry this may be a dumb question)
If you define your CASE statement in either an inline-view or a common table expression (aka WITH clause), you can refer to it by whatever alias you give it.
For example (inline-view):
SELECT ...
FROM ( SELECT .....
(CASE
WHEN (Select 1 from DUAL) = 1 THEN 'TEST'
ELSE 'TEST2'
END) AS TEST
FROM...
) v
WHERE v.test = 'TEST2';
As a common table expression, it would be:
WITH cte AS ( SELECT........
(CASE
WHEN (Select 1 from DUAL) = 1 THEN 'TEST'
ELSE 'TEST2'
END) AS TEST
FROM ... )
SELECT ...
FROM cte
WHERE test = 'TEST2';
You can use a case statement in the where clause, for eg.:
select * from table
where table.field = (CASE
WHEN (Select 1 from DUAL) = 1 THEN 'TEST'
ELSE 'TEST2'
END)
This will compare the value returned from the case statement with the table field.

sql query NVL string with apostrophes (')

So I have an sql query i am currently working on that is like this:
SELECT * from tableA where
( status = NVL('','OPEN') or status = NVL('','CLOSED') )
and delete_flag != 'Y'
The above query works fine and gives me the result I want.. but I was wondering if there is anyway I can combine the above status IN NVL line to one instead of using the or there.
for example, I want to be able to do:
SELECT * from tableA where
status IN NVL('','OPEN','CLOSED')
and delete_flag != 'Y'
But the apostrophes are not working with me here.. how can I work around it?
You are getting an input parameter from your application that can have the values "Open", "Closed" or null
You want to be able to select status values that equal this input paremeter if it is null or the value of the input if it isn't.
To have null for a filter default to all you use COALESCE and the column you are filtering on.
Like this
SELECT * from tableA
where COALESCE(parameter,status) = status
and status in ('OPEN','CLOSED') -- see comments
and delete_flag != 'Y'
In this case if parameter is OPEN you will get all OPEN items, if parameter is CLOSED you will get all closed items and if it is null you will get all items.
This is a very common pattern in web applications.
Single line version
SELECT * from tableA
where COALESCE(parameter,CASE WHEN status in ('OPEN','CLOSED') then status ELSE '' END) = status
and delete_flag != 'Y'
simply:
SELECT * from tableA
where 1=1
and nvl(status, '---') IN ('OPEN','CLOSED')
and delete_flag != 'Y'
In Oracle an empty string '' is equivalent to NULL.
So NVL( '', 'OPEN' ) is the equivalent to NVL( NULL, 'OPEN' ) which can be simplified to just 'OPEN'.
So your query is:
SELECT *
FROM tableA
WHERE ( status = 'OPEN' OR status = 'CLOSED' )
AND delete_flag != 'Y'
Which can be simplified to:
SELECT *
FROM tableA
WHERE status IN ( 'OPEN', 'CLOSED' )
AND delete_flag != 'Y'
You can implement a dynamic list of options using a collection:
CREATE TYPE stringlist IS TABLE OF VARCHAR2(100);
/
SELECT a.*
FROM tableA a
INNER JOIN
( SELECT stringlist( 'OPEN', 'CLOSED' ) AS options FROM DUAL ) o
ON ( o.options IS EMPTY OR a.status MEMBER OF o.options )
WHERE a.delete_flag <> 'Y'

Counting null and non-null values in a single query

I have a table
create table us
(
a number
);
Now I have data like:
a
1
2
3
4
null
null
null
8
9
Now I need a single query to count null and not null values in column a
This works for Oracle and SQL Server (you might be able to get it to work on another RDBMS):
select sum(case when a is null then 1 else 0 end) count_nulls
, count(a) count_not_nulls
from us;
Or:
select count(*) - count(a), count(a) from us;
If I understood correctly you want to count all NULL and all NOT NULL in a column...
If that is correct:
SELECT count(*) FROM us WHERE a IS NULL
UNION ALL
SELECT count(*) FROM us WHERE a IS NOT NULL
Edited to have the full query, after reading the comments :]
SELECT COUNT(*), 'null_tally' AS narrative
FROM us
WHERE a IS NULL
UNION
SELECT COUNT(*), 'not_null_tally' AS narrative
FROM us
WHERE a IS NOT NULL;
Here is a quick and dirty version that works on Oracle :
select sum(case a when null then 1 else 0) "Null values",
sum(case a when null then 0 else 1) "Non-null values"
from us
for non nulls
select count(a)
from us
for nulls
select count(*)
from us
minus
select count(a)
from us
Hence
SELECT COUNT(A) NOT_NULLS
FROM US
UNION
SELECT COUNT(*) - COUNT(A) NULLS
FROM US
ought to do the job
Better in that the column titles come out correct.
SELECT COUNT(A) NOT_NULL, COUNT(*) - COUNT(A) NULLS
FROM US
In some testing on my system, it costs a full table scan.
As i understood your query, You just run this script and get Total Null,Total NotNull rows,
select count(*) - count(a) as 'Null', count(a) as 'Not Null' from us;
usually i use this trick
select sum(case when a is null then 0 else 1 end) as count_notnull,
sum(case when a is null then 1 else 0 end) as count_null
from tab
group by a
Just to provide yet another alternative, Postgres 9.4+ allows applying a FILTER to aggregates:
SELECT
COUNT(*) FILTER (WHERE a IS NULL) count_nulls,
COUNT(*) FILTER (WHERE a IS NOT NULL) count_not_nulls
FROM us;
SQLFiddle: http://sqlfiddle.com/#!17/80a24/5
This is little tricky. Assume the table has just one column, then the Count(1) and Count(*) will give different values.
set nocount on
declare #table1 table (empid int)
insert #table1 values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(NULL),(11),(12),(NULL),(13),(14);
select * from #table1
select COUNT(1) as "COUNT(1)" from #table1
select COUNT(empid) "Count(empid)" from #table1
Query Results
As you can see in the image, The first result shows the table has 16 rows. out of which two rows are NULL. So when we use Count(*) the query engine counts the number of rows, So we got count result as 16. But in case of Count(empid) it counted the non-NULL-values in the column empid. So we got the result as 14.
so whenever we are using COUNT(Column) make sure we take care of NULL values as shown below.
select COUNT(isnull(empid,1)) from #table1
will count both NULL and Non-NULL values.
Note: Same thing applies even when the table is made up of more than one column. Count(1) will give total number of rows irrespective of NULL/Non-NULL values. Only when the column values are counted using Count(Column) we need to take care of NULL values.
I had a similar issue: to count all distinct values, counting null values as 1, too. A simple count doesn't work in this case, as it does not take null values into account.
Here's a snippet that works on SQL and does not involve selection of new values.
Basically, once performed the distinct, also return the row number in a new column (n) using the row_number() function, then perform a count on that column:
SELECT COUNT(n)
FROM (
SELECT *, row_number() OVER (ORDER BY [MyColumn] ASC) n
FROM (
SELECT DISTINCT [MyColumn]
FROM [MyTable]
) items
) distinctItems
Try this..
SELECT CASE
WHEN a IS NULL THEN 'Null'
ELSE 'Not Null'
END a,
Count(1)
FROM us
GROUP BY CASE
WHEN a IS NULL THEN 'Null'
ELSE 'Not Null'
END
Here are two solutions:
Select count(columnname) as countofNotNulls, count(isnull(columnname,1))-count(columnname) AS Countofnulls from table name
OR
Select count(columnname) as countofNotNulls, count(*)-count(columnname) AS Countofnulls from table name
Try
SELECT
SUM(ISNULL(a)) AS all_null,
SUM(!ISNULL(a)) AS all_not_null
FROM us;
Simple!
If you're using MS Sql Server...
SELECT COUNT(0) AS 'Null_ColumnA_Records',
(
SELECT COUNT(0)
FROM your_table
WHERE ColumnA IS NOT NULL
) AS 'NOT_Null_ColumnA_Records'
FROM your_table
WHERE ColumnA IS NULL;
I don't recomend you doing this... but here you have it (in the same table as result)
use ISNULL embedded function.
All the answers are either wrong or extremely out of date.
The simple and correct way of doing this query is using COUNT_IF function.
SELECT
COUNT_IF(a IS NULL) AS nulls,
COUNT_IF(a IS NOT NULL) AS not_nulls
FROM
us
SELECT SUM(NULLs) AS 'NULLS', SUM(NOTNULLs) AS 'NOTNULLs' FROM
(select count(*) AS 'NULLs', 0 as 'NOTNULLs' FROM us WHERE a is null
UNION select 0 as 'NULLs', count(*) AS 'NOTNULLs' FROM us WHERE a is not null) AS x
It's fugly, but it will return a single record with 2 cols indicating the count of nulls vs non nulls.
This works in T-SQL. If you're just counting the number of something and you want to include the nulls, use COALESCE instead of case.
IF OBJECT_ID('tempdb..#us') IS NOT NULL
DROP TABLE #us
CREATE TABLE #us
(
a INT NULL
);
INSERT INTO #us VALUES (1),(2),(3),(4),(NULL),(NULL),(NULL),(8),(9)
SELECT * FROM #us
SELECT CASE WHEN a IS NULL THEN 'NULL' ELSE 'NON-NULL' END AS 'NULL?',
COUNT(CASE WHEN a IS NULL THEN 'NULL' ELSE 'NON-NULL' END) AS 'Count'
FROM #us
GROUP BY CASE WHEN a IS NULL THEN 'NULL' ELSE 'NON-NULL' END
SELECT COALESCE(CAST(a AS NVARCHAR),'NULL') AS a,
COUNT(COALESCE(CAST(a AS NVARCHAR),'NULL')) AS 'Count'
FROM #us
GROUP BY COALESCE(CAST(a AS NVARCHAR),'NULL')
Building off of Alberto, I added the rollup.
SELECT [Narrative] = CASE
WHEN [Narrative] IS NULL THEN 'count_total' ELSE [Narrative] END
,[Count]=SUM([Count]) FROM (SELECT COUNT(*) [Count], 'count_nulls' AS [Narrative]
FROM [CrmDW].[CRM].[User]
WHERE [EmployeeID] IS NULL
UNION
SELECT COUNT(*), 'count_not_nulls ' AS narrative
FROM [CrmDW].[CRM].[User]
WHERE [EmployeeID] IS NOT NULL) S
GROUP BY [Narrative] WITH CUBE;
SELECT
ALL_VALUES
,COUNT(ALL_VALUES)
FROM(
SELECT
NVL2(A,'NOT NULL','NULL') AS ALL_VALUES
,NVL(A,0)
FROM US
)
GROUP BY ALL_VALUES
select count(isnull(NullableColumn,-1))
if its mysql, you can try something like this.
select
(select count(*) from TABLENAME WHERE a = 'null') as total_null,
(select count(*) from TABLENAME WHERE a != 'null') as total_not_null
FROM TABLENAME
Just in case you wanted it in a single record:
select
(select count(*) from tbl where colName is null) Nulls,
(select count(*) from tbl where colName is not null) NonNulls
;-)
for counting not null values
select count(*) from us where a is not null;
for counting null values
select count(*) from us where a is null;
I created the table in postgres 10 and both of the following worked:
select count(*) from us
and
select count(a is null) from us
In my case I wanted the "null distribution" amongst multiple columns:
SELECT
(CASE WHEN a IS NULL THEN 'NULL' ELSE 'NOT-NULL' END) AS a_null,
(CASE WHEN b IS NULL THEN 'NULL' ELSE 'NOT-NULL' END) AS b_null,
(CASE WHEN c IS NULL THEN 'NULL' ELSE 'NOT-NULL' END) AS c_null,
...
count(*)
FROM us
GROUP BY 1, 2, 3,...
ORDER BY 1, 2, 3,...
As per the '...' it is easily extendable to more columns, as many as needed
Number of elements where a is null:
select count(a) from us where a is null;
Number of elements where a is not null:
select count(a) from us where a is not null;