Return single value when checking table rows values - sql

Im trying to return a single value from a table with a lot of rows if a condition is met.
For example, I have a table (ID (pk), CODE (pk), DESCRIPTION) which has a lot of rows. How can I return in a single row if..
SELECT CASE
WHEN CODE IN ('1', '2') THEN '100'
WHEN CODE IN ('2', '3') THEN '200'
WHEN CODE IN ('5', '7') THEN '300'
END AS ASDASD
FROM TABLE
WHERE ID = 1;
The problem is that CODE must check for both and not just one of them. The code as it is will return if for example that ID has got the code '2'.
ASDASD
NULL
'200'
And I want to return just '200' because that ID has got code '2' and '3'.

Assuming codes are not duplicated for a particular id:
SELECT ID,
(CASE WHEN SUM(CASE WHEN CODE IN ('1', '2') THEN 1 ELSE 0 END) = 2
THEN '100'
WHEN SUM(CASE WHEN CODE IN ('2', '3') THEN 1 ELSE 0 END) = 2
THEN '200'
WHEN SUM(CASE WHEN CODE IN ('5', '7') THEN 1 ELSE 0 END) = 2
THEN '300'
END) AS ASDASD
FROM TABLE
WHERE ID = 1
GROUP BY ID;
I added ID to the SELECT, just because this might be useful for multiple ids.

You could try and use condition aggregation, as follows :
SELECT CASE
WHEN MAX(DECODE(code, '1', 1)) = 1 AND MAX(DECODE(code, '2', 1)) = 1
THEN '100'
WHEN MAX(DECODE(code, '2', 1)) = 1 AND MAX(DECODE(code, '3', 1)) = 1
THEN '200'
WHEN MAX(DECODE(code, '5', 1)) = 1 AND MAX(DECODE(code, '7', 1)) = 1
THEN '300'
END AS asdasd
FROM TABLE
WHERE ID = 1;
DECODE() is a handy Oracle function that compares an expression (code) to a series of values and returns results accordingly. Basically, condition MAX(DECODE(code, '1', 1)) = 1 ensures that at least one row has code = '1'.
PS : are you really storing numbers as strings ? If code is a number datatype, please remove the single quotes in the above query.

You need to check the number returned by a query like this:
SELECT COUNT(DISTINCT CODE) FROM TABLE WHERE ID = 1 AND CODE IN ('1', '2')
If this number is 2 then ID = 1 has both CODE values '1' and '2'.
SELECT
CASE
WHEN (SELECT COUNT(DISTINCT CODE) FROM TABLE WHERE ID = 1 AND CODE IN ('1', '2')) = 2 THEN '100'
WHEN (SELECT COUNT(DISTINCT CODE) FROM TABLE WHERE ID = 1 AND CODE IN ('2', '3')) = 2 THEN '200'
WHEN (SELECT COUNT(DISTINCT CODE) FROM TABLE WHERE ID = 1 AND CODE IN ('5', '7')) = 2 THEN '300'
END AS ASDASD
FROM TABLE

Related

SQL query to get repeating column value that have other columns in a certain codition

Let's say we have below table of below schema.
create table result
(
id int,
task_id int,
test_name string,
test_result string
);
And dataset populated on this table looks like this.
insert into result
values (1, 1, 'test_a', 'pass'),
(2, 1, 'test_b', 'fail'),
(3, 1, 'test_c', 'pass'),
(4, 1, 'test_d', 'pass'),
(5, 2, 'test_a', 'pass'),
(6, 2, 'test_b', 'pass'),
(7, 2, 'test_c', 'pass'),
(8, 2, 'test_d', 'pass');
Basically single task has multiple test results entry. I want to retrieve task_id that has test_b fail but all the other test passed. So in this example it should return only task_id: 1.
I've tried with EXISTS and HAVING but it doesn't seem working in this case. I'm new to SQL. How can I implement it?
I would just use aggregation with a having clause:
select task_id
from result
group by task_id
having sum(case when test_name = 'test_b' and test_result = 'fail' then 1 else 0 end) = 1 and
sum(case when test_result = 'pass' then 1 else 0 end) = count(*) - 1;
The first condition validates that test_b failed. The second counts the number of passes and it should be one less then the number of rows for the task.
If your database supports except (or minus), you an use set-based operations:
select task_id
from result
where test_name = 'test_b' and test_result = 'fail'
except
select task_id
from result
where test_name <> 'test_b' and test_result = 'fail'
Maybe selecting distinct task IDs that have a fail result:
select distinct [task_id], [task_result]
from [result]
where [task_result] = 'fail'
Note that this query will scan the entire table unless there is an index on task_result.
Following code first sums test takers per task and counts fro 'test_b' whether it failed or not. Outer select ensure 'test_b' failed and other have passed.
select task_id from (
select
task_id,
count(test_result) numberoftakers,
sum(case when test_result<>'pass' AND test_name='test_b' then 1 else 0 end) numberoffailb,
sum(case when test_result='pass' then 1 else 0 end) numberofallpasses
from result
group by task_id) a
where numberoftakers=numberoffailb+numberofallpasses and numberoffailb=1
Assuming that (task_id, task_name) is a unique key of your table, you can indeed use (not) exists, along with a correlated subqueries wich ensures that other records having the same task_id did not passed.
select task_id
from result r
where
test_name = 'test_b'
and test_result = 'fail'
and not exists (
select 1
from result r1
where
r1.task_id = r.task_id
and r1.id != r.id
and r1.test_result = 'fail'
)
The left join antipattern also comes to mind:
select r.task_id
from result r
left join result r1
on r1.task_id = r.task_id
and r1.id != r.id
and r1.test_result = 'fail'
where
r.test_name = 'test_b'
and r.test_result = 'fail'
and r1.id is null
Demo on DB Fiddle - Both queries return:
| task_id |
| :------ |
| 1 |

Count of each distinct value in data field

I'm looking to write a query which will return each district value in the table along with the count of each distinct value for a given time period
I am Currently using the following query
Select count(distinct account_type)
From Table_1
Where date between '2019-08-01' and '2019-08-31' and
account_type = '0' and
account_type = '1' and
account_type = '2' and
account_type = '3' and
account_type = '4'
The result set that I'm looking for is as follows
account_type Count
0 123
1 456
2 789
3 101112
4 131415
The result set that I get is
account_type
0
Your WHERE-clause excludes all elements since they cannot be of type 0 and 1 (etc.) contemporarily.
Furthermore, by means of count(distinct account_type) you get the number of distinct account types; not the number of elements for each account type.
Try this:
SELECT account_type,
COUNT(*)
FROM table_1
WHERE date BETWEEN '2019-08-01' AND '2019-08-31'
AND account_type IN ('0', '1', '2', '3', '4')
GROUP BY account_type
ORDER BY account_type;
In case the account_type is always a single character (so for example '06' doesn't exist), you can also use:
AND account_type BETWEEN '0' AND '4'

using COUNT (CASE WHEN....) vs CASE WHEN = ...THEN COUNT . I get different results, can someone kindly explain why?

When i use method 1: ' COUNT (Case WHEN..) ' method it produces the output that I want, but when i use the 2nd method ' CASE WHEN .. COUNT ' method, i get a diagonal matrix of sorts which is not what I am looking for.
My steps are :
i) Created a dummy table
INSERT INTO job (jobid, jobname, [priority])
VALUES ('something', '1', 1),
('something', '2', 2),
('something', '3', 3),
('something', '4', 4),
('something', '5', 5),
('something', '6', 1),
('something', '7', 1),
('something', '8', 3),
('something', '9', 3),
('something', '10', 2);
ii) method 1 : COUNT (CASE WHEN....)
SELECT
COUNT(CASE WHEN [Priority] = 1 THEN 1 ELSE NULL END ) as Priority1,
COUNT(CASE WHEN [Priority] = 2 THEN 1 ELSE NULL END )as Priority2,
COUNT(CASE WHEN [Priority] = 3 THEN 1 ELSE NULL END )as Priority3
FROM job
Result :
Priority1 Priority2 Priority3
3 2 3
iii) method 2 : CASE WHEN .... COUNT
SELECT
CASE WHEN [Priority] = 1 THEN COUNT(*) END as Priority1,
CASE WHEN [Priority] = 2 THEN COUNT(*) END as Priority2,
CASE WHEN [Priority] = 3 THEN COUNT(*) END as Priority3
FROM job
GROUP BY [Priority]
Result :
Priority1 Priority2 Priority3
3 NULL NULL
NULL 2 NULL
NULL NULL 3
NULL NULL NULL
NULL NULL NULL
Method 1 gives me the right result, but method 2's output suprised me... i was expecting the same result as method 1!
Method 1:
The aggregate function COUNT is applied at the table level and COUNT ignored and consumed all the NULL values (cases where [Priority] is other than 1, 2 or 3). So, at the end you got only 1 row.
Method 2:
The aggregate function COUNT is applied to each row of the table. So, the result contains equal number of rows as the number of unique [Priority] values in the table. And result contains some NULL because the case condition didn't satisfied in those cases and COUNT return NULL.
You have a group by in the second method, so you are going to get one row per value in Priority.
So you sort of want:
SELECT CASE WHEN [Priority] = 1 THEN COUNT(*) END as Priority1,
CASE WHEN [Priority] = 2 THEN COUNT(*) END as Priority2,
CASE WHEN [Priority] = 3 THEN COUNT(*) END as Priority3
FROM job;
But this won't work. Because [Priority] is not aggregated.
Hmmm, you are basically back to your first method, where the condition is in the argument to the aggregation function Your expectation is wrong. Use the first method (although I personally prefer using SUM() to COUNT()).

How to use sum(case) with three conditions

I usually use sum(case) to get sum of some columns:
i.e. SUM(CASE WHEN over05 = 'OK' THEN 1 ELSE 0 END) AS OK_05
and this is perfect when I have a column with two values, but when I have a column where I have three values:
i.e. over 05 = '1' or 'X' or '2'
how can I do a sum(case)?
If you want all three values to return the same thing, you should use IN():
SUM(
CASE
WHEN over05 IN ('1', 'X', '2') THEN 1
ELSE 0 END
) AS OK_05
If you want each value to return something different, you should use multiple WHEN ... THEN :
SUM(
CASE
WHEN over05 = '1' THEN 1
WHEN over05 = 'X' THEN 2
WHEN over05 = '2' THEN 3
ELSE 0 END
) AS OK_05

postgresql - How to return null values in NOT IN expression

I have a column which can contain values from 1 to 6 and null..
Then when I try to run the query below.. rows with null value on that column do not return
select * from customer where Position not IN ('1','2', '3', '4' ,'5', '6')
Is there way I can retrieve null results without adding this on above query
OR Position is null
One trick you could use would be to coalesce the position to some value which does not appear in the restricted list, e.g.
SELECT *
FROM customer
WHERE COALESCE(Position, '9') NOT IN ('1', '2', '3', '4' ,'5', '6')
However, I would probably just use an is null clause here:
SELECT *
FROM customer
WHERE Position NOT IN ('1', '2', '3', '4' ,'5', '6') OR
Position IS NULL
Is there way I can retrieve null results without adding OR Position is null
There are plenty of ways, but OR Position is null is the easiest, the shorteste and the most natural way.
If you don't like it, then there are few ideas below:
select *
from customer where ID NOT IN (
select ID
from customer where Position IN ('1','2', '3', '4' ,'5', '6')
);
select *
from customer where
1 = CASE WHEN Position IN ('1','2', '3', '4' ,'5', '6')
THEN 0 ELSE 1 END;
select * from customer c
left join (
SELECT * FROM unnest(ARRAY[1,2,3,4,5,6]) x
) x
ON c.position = x.x
WHERE x.x IS NULL
If you want to return the NULL result then include that as a condition in your WHERE clause else NO it's not possible
select * from customer
where Position not IN ('1','2', '3', '4' ,'5', '6')
or Position is null
You can add a condition : Position IS NULL
SELECT *
FROM Customer
WHERE Position NOT IN ('1','2', '3', '4' ,'5', '6') OR Position IS NULL
You can see this here => http://rextester.com/HEZ87754
Hope this helps!!!