how to select rows with multiple where conditions - sql

Not sure if the title made sense.
But this what I need, I am bad at sql:
I have table with the following data
ID| Value
------ | ------
1| A
1| B
1| C
1| D
2| A
2| B
2| C
I need O/P as
2 A
2 B
2 C
Using Select id where value in (A,B,C) and Not in (D)
is giving me o/p as
1 A
1 B
1 C
2 A
2 B
2 C
Need to select Id's having A,B,C but not D.

If you want to get rows which has one or more value in A, B, C, but not D:
select *
from your_table
where value in ('A', 'B', 'C')
and id not in (
select id
from your_table
where value = 'D'
);
If you want that all the three values A, B, C must be present for that id and but not D.
select id,
value
from (
select t.*,
count(distinct value) over (partition by id) n
from your_table t
where value in ('A', 'B', 'C')
and id not in (
select id
from your_table
where value = 'D'
)
)
where n = 3;
If you want to get rows having only A, B, C (and all of them present) then use :
select id,
value
from (
select t.*,
count(case
when value not in ('A', 'B', 'C')
then 1
end) over (partition by id) n,
count(distinct case
when value in ('A', 'B', 'C')
then value
end) over (partition by id) n2
from your_table t
)
where n = 0
and n2 = 3;

You can use conditional aggregation in a HAVING clause to filter across rows for each id:
SELECT id
FROM table1
GROUP BY id
HAVING MAX(CASE WHEN value = 'A' THEN 1 ELSE 0 END) = 1
AND MAX(CASE WHEN value = 'B' THEN 1 ELSE 0 END) = 1
AND MAX(CASE WHEN value = 'C' THEN 1 ELSE 0 END) = 1
AND MAX(CASE WHEN value = 'D' THEN 1 ELSE 0 END) = 0
This will show you which id values meet your criteria, and if you need you can put this in a subquery and join it to your table to get all the rows for that id:
SELECT a.*
FROM table1 a
JOIN ( SELECT id
FROM table1
GROUP BY id
HAVING MAX(CASE WHEN value = 'A' THEN 1 ELSE 0 END) = 1
AND MAX(CASE WHEN value = 'B' THEN 1 ELSE 0 END) = 1
AND MAX(CASE WHEN value = 'C' THEN 1 ELSE 0 END) = 1
AND MAX(CASE WHEN value = 'D' THEN 1 ELSE 0 END) = 0
) b
ON a.id = b.id

From your question, I understood that to find the count of Value column where the values are A, B, C but Value not equal to D. Then simply put those 3 value in IN operator. I will retrieve the result set without D.
Query
SELECT COUNT(Value) count, Value
FROM your_table_name
WHERE Value in ('A', 'B', 'C')
GROUP BY Value;

select id, value
from my_table
where value in ('A','B','C')
and id = 2

Related

How to check if non-unique key contains specific value pairs?

I have the following table:
CREATE TABLE TABLE1 (
ID,
status,
timestamp
)
I would like to check for ID's that have status A, but also for ID's that have status B AND status C. I would like them to be placed into a count somewhat as follows:
SELECT
ID
,SUM(CASE WHEN status = 'A' then 1
WHEN
-- something like the following:
status = 'B' and (status = 'C' AND [timestamp of C] > [timestamp of B]) then 1
else 0 END) as SUCCESS
FROM TABLE1
GROUP BY ID
Do I need a self join here? How do I get the IDs that have this status = B & C with a timestampB < timestampC conditional?
Example table:
INSERT INTO TABLE1(ID, status, timestamp)
VALUES(1, A, 5)
VALUES(2,B, 3)
VALUES(2, D, 5)
VALUES(3, A, 5)
VALUES(4, B, 9)
VALUES(4,C,10)
Results should be:
1, 1
2, 0
3, 1
4, 1
If you want ids that have A and whose timestamp for B is less than C, then use:
select id
from t
group by id
having sum(case when status = 'A' then 1 else 0 end) > 0 and
min(case when status = 'B' then timestamp end) < max(case when status = 'C' then timestamp end)
EDIT:
select id,
(case when sum(case when status = 'A' then 1 else 0 end) > 0 and
min(case when status = 'B' then timestamp end) < max(case when status = 'C' then timestamp end)
then 1 else 0
end) as success_flag
from t
group by id

how do we have count of a specific values for multiple columns with table having a unique column

If I have a table like :
u_id A B C D
----------------------------------
jud 1 1 0 1
bud 0 0 1 0
cud 1 1 0 1
nud 0 0 1 0
dud 1 0 0 1
aud 0 1 1 0
fud 1 0 1 1
which sql is useful to get output like:
count 0 count 1
-----------------------
A 3 4
B 4 3
C 3 4
D 3 4
Doesn't matter row or columns just need count of a specific value count for multiple columns in a table.
Instead of 0's and 1's it can be specific string values as well as 'yes' or 'no'
Thank you
Use UNION ALL and aggregation. Assuming that the only possible values in the columns are 0 and 1:
SELECT 'A' col, COUNT(*) - SUM(A) count0, SUM(A) count1 FROM mytable
UNION ALL SELECT 'B', COUNT(*) - SUM(B), SUM(B) FROM mytable
UNION ALL SELECT 'C', COUNT(*) - SUM(C), SUM(C) FROM mytable
UNION ALL SELECT 'D', COUNT(*) - SUM(D), SUM(D) FROM mytable
Demo on DB Fiddle:
| col | count0 | count1 |
| --- | ------ | ------ |
| A | 3 | 4 |
| B | 4 | 3 |
| C | 3 | 4 |
| D | 3 | 4 |
If other values than 0/1 are possible, then you can change the SELECTs to, eg 'yes'/'no', then:
SELECT
'A' col,
SUM(CASE WHEN A = 'no' THEN 1 ELSE 0 END) count_no,
SUM(CASE WHEN A = 'yes' THEN 1 ELSE O END) count_yes
FROM mytable
GROUP BY col
UNION ALL SELECT
'B' col,
SUM(CASE WHEN B = 'no' THEN 1 ELSE 0 END),
SUM(CASE WHEN B = 'yes' THEN 1 ELSE 0 END)
FROM mytable
GROUP BY col
UNION ALL SELECT
'C' col,
SUM(CASE WHEN C = 'no' THEN 1 ELSE 0 END),
SUM(CASE WHEN C = 'yes' THEN 1 ELSE 0 END)
FROM mytable
GROUP BY col
UNION ALL SELECT
'D' col,
SUM(CASE WHEN D = 'no' THEN 1 ELSE 0 END),
SUM(CASE WHEN D = 'yes' THEN 1 ELSE 0 END)
FROM mytable
GROUP BY col
If you are okay with a single row, you can do:
select sum(a), sum(1-a), sum(b), sum(1-b), sum(c), sum(1-c), sum(d), sum(1-d)
from t;
The advantage of this approach is that t is read only once. This is even more true if it is a complex view.
With that in mind, you can unpivot this result:
select v.x,
(case when v.x = 'a' then a_0 end) as a_0,
(case when v.x = 'a' then a_1 end) as a_1,
(case when v.x = 'b' then b_0 end) as b_0,
(case when v.x = 'b' then b_1 end) as b_1,
(case when v.x = 'c' then c_0 end) as c_0,
(case when v.x = 'c' then c_1 end) as c_1,
(case when v.x = 'd' then d_0 end) as d_0,
(case when v.x = 'd' then d_1 end) as d_1
from (select sum(a) as a_1, sum(1-a) as a_0,
sum(b) as b_1, sum(1-b) as b_0,
sum(c) as c_1, sum(1-c) as c_0,
sum(d) as d_1, sum(1-d) as d_0
from t
) s cross join
(values ('a'), ('b'), ('c'), ('d')) v(x) -- may require a subquery
You don't mention the database you're using, but in Oracle you can use DECODE and COUNT together to make this reasonably clean:
SELECT 'A' AS FIELD_NAME,
COUNT(DECODE(A, 0, 0, NULL)) AS ZERO_COUNT,
COUNT(DECODE(A, 0, NULL, A)) AS NON_ZERO_COUNT
FROM TEST_TABLE UNION ALL
SELECT 'B', COUNT(DECODE(B, 0, 0, NULL)),
COUNT(DECODE(B, 0, NULL, A))
FROM TEST_TABLE UNION ALL
SELECT 'C', COUNT(DECODE(C, 0, 0, NULL)),
COUNT(DECODE(C, 0, NULL, A))
FROM TEST_TABLE UNION ALL
SELECT 'D', COUNT(DECODE(D, 0, 0, NULL)),
COUNT(DECODE(D, 0, NULL, A))
FROM TEST_TABLE
dbfiddle here

Merge multiple columns into one column with multiple rows

In PostgreSQL, how can I merge multiple columns into one column with multiple rows?
The columns are all boolean, so I want to:
Filter for true values only
Replace the true value (1) with the name of the column (A, B or C)
I have this table:
ID | A | B | C
1 0 1 0
2 1 1 0
3 0 0 1
4 1 0 1
5 1 0 0
6 0 1 1
I want to get this table:
ID | Letter
1 B
2 A
2 B
3 C
4 A
4 C
5 A
6 B
6 C
I think you need something like this:
SELECT ID, 'A' as Letter FROM table WHERE A=1
UNION ALL
SELECT ID, 'B' as Letter FROM table WHERE B=1
UNION ALL
SELECT ID, 'C'as Letter FROM table WHERE C=1
ORDER BY ID, Letter
SELECT ID,
(CASE
WHEN TABLE.A = 1 then 'A'
WHEN TABLE.B = 1 then 'B'
WHEN TABLE.C = 1 then 'C'
ELSE NULL END) AS LETTER
from TABLE
You may try this.
insert into t2 select id, 'A' from t1 where A=1;
insert into t2 select id, 'B' from t2 where B=1;
insert into t2 select id, 'C' from t3 where C=1;
If you care about the order, then you can do this.
insert into t3 select id, letter from t2 order by id, letter;
W/o UNION
You can use a single query to get the desired output.Real time example
select id
,regexp_split_to_table((
concat_ws(',', case
when a = 0
then null
else 'a'
end, case
when b = 0
then null
else 'b'
end, case
when c = 0
then null
else 'c'
end)
), ',') l
from c1;
regexp_split_to_table() & concat_ws()

SQL - not equal to operator is ignoring the rest of the where clause

product_id |value
1 | a
1 | b
1 | c
2 | a
2 | c
2 | d
3 | a
3 | c
3 | d
Below is my code, what I am trying to achieve with it is to check which product_id's have both 'a' and 'c' -that part works fine.
My problem is that when i do the following:
AND value != ('b')
It completely ignores the not equal to operator and still returns b...
SELECT
product_id, value
FROM
table
WHERE
value = 'a'
OR value = 'c'
AND value != ('b')
GROUP BY product_id
HAVING count(value) = 2
order by product_id
I guess my question is how do I fix it and why is this happening? thank you very much for your time. :)
EDIT:
What I should achieve is :
product_id value
2 a
3 a
Add proper parenthesis to the where clause. Now the And operator is applied between value='C' and value<>'b'
SELECT
product_id, value
FROM
table
WHERE
(value = 'a'
OR value = 'c')
AND value != ('b')
GROUP BY product_id
HAVING count(value) = 2
order by product_id
Update : : looks like fixing parenthesis will not fix your problem. Use conditional count in having clause to filter the groups.
SELECT product_id,
min(value) value
FROM table
GROUP BY product_id
HAVING Count(CASE WHEN value = 'a' THEN 1 END) = 1
AND Count(CASE WHEN value = 'c' THEN 1 END) = 1
AND Sum(CASE WHEN value <> 'b' THEN 0 ELSE 1 END) = 0
Order of operations.
value = 'a' OR value = 'c' AND value <> ('b')
will evaluate the same as
value = 'a' OR (value = 'c' AND value <> ('b'))
Change your conditional (WHERE) clause to
(value = 'a' OR value = 'c') AND value <> ('b')

To retrieve records having only two specific values

Have the following Data in the table
Example Table
ID Value
1 a
1 b
1 c
2 a
2 b
2 c
3 a
3 b
I need to retrieve records having ID with only two values a and b.
So i am expecting only the Record with ID 3 .
Can anyone help me with the query
I guess you could do something like
select
ID,
sum(case when value = 'a' then 1
when value = 'b' then 1
else 3 end)
from
table1
group by id
having
sum (case when value = 'a' then 1
when value = 'b' then 1
else 3 end) =2
SQL Fiddle
That will work:
select x.id from
(
select id from mytable where value = 'a'
union all
select id from mytable where value = 'b'
) x
group by x.id
having COUNT(*) = 2
and not exists (select * from mytable t where t.id = x.id and value <> 'a' and value <> 'b')