Case Statement embedded in NVL - sql

I am trying to setup the below logic in as a divisor. I need to use NVL() to avoid the divide by zero error. I need to get a distinct count of the case statement.
Here is what I have:
(NVL(COUNT(DISTINCT
CASE WHEN ({field1} = 'a' OR {field1} = 'b' OR
{field1} = 'c' OR {field1} = 'd') AND
({field2} = 'a' OR {field2} = 'b') AND
({field3} > TO_DATE('01-JAN-2017', 'DD-MON-YYYY'))
THEN 1 END), 1))
There is something wrong with my syntax but cannot figure it out.
Any help would be appreciated. Thank you!

I changed lots of ORs to simpler IN
COUNT(DISTINCT
CASE
WHEN field1 IN ('a','b','c','d')
AND field2 IN ('a','b')
AND field3 > TO_DATE('01-JAN-2017', 'DD-MON-YYYY')
THEN 1
END)
COUNT(DISTINCT ...) doesn't return null, so removed redundant NVL
Now, the above will result is either 0 or 1.
You probably want this without distinct:
COUNT(
CASE
WHEN field1 IN ('a','b','c','d')
AND field2 IN ('a','b')
AND field3 > TO_DATE('01-JAN-2017', 'DD-MON-YYYY')
THEN 1
END)
Since, it is a divisor, it can't be 0, so you'll have to place safeguard for that. One sensible output in case the divisor is 0 is null. One way to achieve that is:
case when (expression) <> 0 then dividend/(expression) end

Related

SQL Ignore non matching columns

I'm trying to develop a stored procedure which does a select on a table. The stored procedure has 4 inputs: Input1, Input2, Input3, Input4.
Table has 4 columns: Col1,Col2,Col3, Col4.
The requirement is if there is no match on all selects, we need to ignore that and select pick next one:
Use case:
Select *
from table
where Col1=Input1
and Col2=Input2
and Col3=Input3
and Col4=Input4;
If there are no returns for the condition due to Col2 not equal to Input2, select needs to ignore it and try to match on others like:
Select *
from table
where Col1=Input1
and Col3=Input3
and Col4=Input4;
It should go like that till last possible option for the response:
Select *
from table
where Col1=Input1;
Please assist if there is a way and thanks in advance.
As it's mentioned in the comments, the question is a bit vague so I'm afraid any answer would be a bit vague too. You could do something like this:
select top 1 *,
case when Coll=input1 then 1 else 0 end as match1,
case when Col2=input2 then 1 else 0 end as match2,
case when Col3=input3 then 1 else 0 end as match3,
case when Col4=input4 then 1 else 0 end as match4
from table
order by match1+match2+match3+match4 desc
this is assuming SQL server as the DBMS (if it is Oracle, you may need to use LIMIT instead of TOP, for instance) and I also assumed that the columns have all the same weight in terms of matching.
Also, I'm assuming that you want only one of the best matches, if not you may need to do some changes to the TOP and/or use a where clause.
Finally, You may want to use isnull() if your columns and/or inputs are nullable.
Hmmm . . . One method is:
select t.*
from (select t.*,
sum(case when condition1 then 1 else 0 end) over () as c_1,
sum(case when condition1 and condition2 then 1 else 0 end) over () as c_12,
sum(case when condition1 and condition2 and condition3 then 1 else 0 end) over () as c_123,
sum(case when condition1 and condition2 and condition3 and condition4 then 1 else 0 end) over () as c_1234
from t
) t
where (c_1234 > 0 and condition1 and condition2 and condition3 and condition4) or
(c_1234 = 0 and c_123 > 0 and condition1 and condition2 and condition3) and
(c_123 = 0 and c_12 > 0 and condition1 and condition2 ) and
(c_12 = 0 and c_1 > 0 and condition1 ) ;
Depending on the conditions and other considerations -- such as whether you only expect one row -- there might be simpler methods.

Is there a way to contruct this kind of result using group by in sql?

I have a table which consists of data where in I'm having trouble counting the corresponding rows.
Here is the sample table:
I am expecting an output like this:
You can do conditional aggregation:
select
sum(case when result = 'X' then 1 else 0 end) count_x,
sum(case when result is null then 1 else 0 end) count_blank
from mytable
I assume that by blank you mean null. If not, then you can change the condition in the second sum() from result is null to result = ''.
If you are running MySQL, this can be shortened a little:
select
sum(result = 'X') count_x,
sum(result is null) count_blank
from mytable

ORA-00936: missing expression using prompt

Cannot get a CASE WHEN to work in my query using a variable.
select field1, field2
from table
where 1=1
AND GLCC.SEGMENT2
(CASE
WHEN :P_AccountType = 'B' THEN < 40000
WHEN :P_AccountType = 'P' THEN => 40000
ELSE
BETWEEN '00000' AND '99999'
END)
any idea where/what I do wrong
Many thanks.
I don't think the variable is the problem, you can't build a WHERE clause with syntax like that, as far as I can see.
Also, what is the 1=1 for?
And the => should surely be the other way round.
And why are you using a numeric representation of the number in two places and a character one in the other?
Try
select field1, field2 from table
where (GLCC.SEGMENT2 < 40000 and :P_AccountType = 'B')
or (GLCC.SEGMENT2 >= 40000 and :P_AccountType = 'P' )
or (GLCC.SEGMENT2 BETWEEN 0 AND 99999 and :P_AccountType not in ('B','P'))
or something like that (not in a position to test!)
I think you need such a collation as below :
SELECT field1, field2
FROM "table"
WHERE
(CASE
WHEN :P_AccountType = 'B' AND SEGMENT2 < '40000' THEN 1
WHEN :P_AccountType = 'P' AND SEGMENT2 >= '40000' THEN 1
ELSE
CASE WHEN SEGMENT2 BETWEEN '00000' AND '99999' THEN 1 END
--> the above case line is needed when :P_AccountType is neither "B", nor "P"
END) = 1

PostgreSQL, SELECT CASE COALESCE

I need to 'name' categories: mycat is a text column with possible values '0' to '4'.
SELECT CASE mycat
WHEN '0' THEN 'ZERO'
WHEN '1' THEN 'ONE'
WHEN '2' THEN 'TWO'
WHEN '3' THEN 'THREE'
WHEN '4' THEN 'OTHER'
END AS my_category,
COALESCE(SUM(col1), 0),
COALESCE(SUM(col2), 0),
COALESCE(SUM(col3), 0)
FROM mytable
GROUP BY mycat
ORDER BY mycat;
That works OK, but I have some an error in my program which very rarely writes null (or '' as I can see in pgAdmin). In such cases I have to treat that '' the same as '0'.
But I can't get that!
I try like this:
SELECT CASE COALESCE(mycat, '0')
But this doesn't solve it at all.
How to get that '' will be summed and grouped together with '0' category?
PostgreSQL 9.3, Windows.
you need to use COALESCE in the group by and order by also similar to how you planned to change the case expression, but postgres is giving error , so another option is to wrap your statement in a subquery and do group by
SELECT my_category,
COALESCE(SUM(col1), 0),
COALESCE(SUM(col2), 0),
COALESCE(SUM(col3), 0)
FROM
(
SELECT CASE coalesce(mycat ,'0')
WHEN '0' THEN 'ZERO'
WHEN '1' THEN 'ONE'
WHEN '2' THEN 'TWO'
WHEN '3' THEN 'THREE'
WHEN '4' THEN 'OTHER'
WHEN '' THEN 'ZERO'
END AS my_category,
col1,
col2,
col3
FROM mytable
) T
GROUP BY my_category
ORDER BY my_category
You can have this without subquery. You could repeat the expression in the GROUP BY and ORDER BY clause. But it's much simpler to use the ordinal number of the output column instead:
SELECT CASE mycat
WHEN '1' THEN 'ONE'
WHEN '2' THEN 'TWO'
WHEN '3' THEN 'THREE'
WHEN '4' THEN 'OTHER'
ELSE 'ZERO' -- catches all other values
END AS my_category
, COALESCE(SUM(col1), 0) AS sum1
, COALESCE(SUM(col2), 0) AS sum2
, COALESCE(SUM(col3), 0) AS sum3
FROM mytable
GROUP BY 1
ORDER BY 1;
I chose the simplest and fastest code. The ELSE branch catches 0, '' and NULL - or any other value not yet filtered! But you say there are no others.
A couple of rants:
mycat is 'text' column with possible values '0' to '4'.
This is wrong in two ways.
Obviously, there are empty strings ('') and / or NULL values, too.
With that fixed, integer, smallint, of "char" with a CHECK cnstraint would be sensible choices for the data type. (Maybe even enum.) text, not so much.
To find out your actual range of values:
SELECT mycat, count(*) AS ct
FROM mytable
GROUP BY 1
ORDER BY 2 DESC;
If your client obfuscates NULL and empty values, test with mycat IS NULL. You need to know and understand the difference in many situations.
This orders by the resulting text in my_category like: ONE, OTHER, THREE, TWO, ZERO? I doubt you want that.

How to count the rows which contains non zero values in sql

SELECT round(COUNT(dmd_1wk),2) AS NBR_ITEMS_1WK
FROM table;
Field dmd_1wk has so many zeros in it. How do I Count the non zero values?
It sounds like you just need to add a WHERE clause:
SELECT
round(COUNT(dmd_1wk),2) AS NBR_ITEMS_1WK
FROM table
WHERE dmd_1wk <> 0;
If you want the count of both non-zero and zero values, then you can use something like:
SELECT
round(COUNT(case when dmd_1wk <> 0 then dmd_1wk end),2) AS NBR_ITEMS_1WK_NonZero,
round(COUNT(case when dmd_1wk = 0 then dmd_1wk end),2) AS NBR_ITEMS_1WK_Zero
FROM table;
Method 1: Case Statement. This may be useful if you need to continue to process all rows (which a where clause would prevent).
SELECT count(case when dmd_1wk = 0 then 0 else 1 end) as NonZeroCount FROM MyTable
Method 2: Where Clause.
SELECT
count(1) as NonZeroCount
FROM
MyTable
WHERE
dmd_1wk <> 0
I'd like to offer another solution using NULLIF since COUNT won't count NULL values:
SELECT round(COUNT(NULLIF(dmd_1wk,0)),2) AS NBR_ITEMS_1WK
FROM table;
And here is the Fiddle.
Good luck.
Methinks bluefeets answer is probably what you are really looking for, as it sounds like you just want to count non-zeros; but this will get you a count of zero and non-zero items if that's not the case:
SELECT
ROUND(SUM(CASE NVL(dmd_1wk, 0) = 0 THEN 1 ELSE 0 END), 2) AS "Zeros",
ROUND(SUM(CASE NVL(dmd_1wk, 0) != 0 THEN 1 ELSE 0 END), 2) AS "NonZeros"
FROM table
Although there is no point in rounding a whole number, I've included your original ROUNDs as I'm guessing you're using it for formatting, but you might want to use:
TO_CHAR(SUM(...), '999.00')
as that's the intended function for formatting numbers.
You can filter them.
SELECT round(COUNT(dmd_1wk),2) AS NBR_ITEMS_1WK
FROM table
WHERE dmd_1wk <> 0;