Group by and replace - sql

I have a table like that :
groupe subgroup id status
A a 1 up
A b 1 notdefined
A c 1 null
A a 2 up
A b 2 up
A c 2 up
A a 3 up
A b 3 up
A c 3 null
What I need is that for each combination (group-id) return a specified status
if for each (group,id) there is a status with notdefined return the global status as notdefined
if all status = up return global status = up
if there is up but there is a null return notspecified
so the result should be like that
Groupe id global_status
A 1 notdefined
A 2 up
A 3 notspecified
I've tried something on sqlfiddle

You can use aggregation and conditional logic:
select groupe, id,
(case when count(*) filter (where status = 'notdefined') > 0
then 'notdefined'
when count(*) filter (where status is null) > 0
then 'notspecified'
when max(status) = min(status) and min(status) = 'up'
then 'up'
else 'something else'
end) as global_status
from t
group by groupe, id;

Use boolean aggregates:
select
groupe,
id,
case
when bool_and(status = 'up') then 'up'
when bool_or(status = 'notdefined') then 'notdefined'
else 'notpecified'
end as status
from tab
group by 1, 2
order by 1, 2
SqlFiddle.

Related

SQL: Check if a row exists for each condition

I'm not able to find a correct SQL query for my needs.
I have a table like this:
first_id | second_id | value
1 1 valueABC
1 2 valueLoL
2 1 valueBlaBla
2 2 valueLoL
Now I would like to select the first_ids where they have at least one row for EACH condition:
second_id = 1 and value = 'valueABC'
AND
second_id = 2 and value = 'valueLoL'
I already tried this, but the query is not correct:
select distinct first_id from myTable where (second_id = 1 and value = 'valueABC')
and (second_id = 2 and value = 'valueLoL') group by first_id having count(first_id) = 2
So in my example, first_id = 1 should be selected, as we have a row for each condition.
Can you help me please ?
Thank you.
I think you can do something like the following:
select first_id
from t
group by first_id
having
Max(case when second_id = 1 and value = 'valueABC' then 1 end) is not null and
Max(case when second_id = 2 and value = 'valueLoL' then 1 end) is not null;
Try this:
select first_id from
(select first_id,
sum(case when second_id = 1 and value = 'valueABC' then 1 else 0 end) as nbCond1,
sum(case when second_id = 2 and value = 'valueLoL' then 1 else 0 end) as nbCond2
from myTable
group by first_id) t
where nbCond1 > 0 and nbCond2 > 0;
Basically grouping by first_id and counting how many times the two conditions appear in a row for each first_id, then only selecting first_ids that have the number of occurences of both conditions > 0.
Fiddle

SQL to find IDs which never had a column value

Below is my data:
ID
request_type
1
3
1
2
1
1
1
4
1
5
2
3
2
2
3
4
3
2
I need a query to fetch IDs that never had a request type of 1 (e.g. 2,3 from the previous table).
With conditional aggregation:
select id
from tablename
group by id
having count(case when request_type = 1 then 1 end) = 0
SELECT DISTINCT `ID`
FROM `my_table`
WHERE `ID` NOT IN (
SELECT DISTINCT `ID`
FROM `my_table`
WHERE `request_type` = 1
)
If 1 is the lowest possible value:
select ID
from tab
group by ID
having min (request_type) > 1
Or more generic:
select ID
from tab
group by ID
having max(case when request_type = 1 then 1 else 0 end) = 0

SQL Assign Custom values to those rows with similar IDs

|id|last|
|2 |NULL|
|2 |2018|
|3 |NULL|
|3 |NULL|
|4 |2011|
|4 |2013|
This is what my current table looks like. A new 'status' column is to be created for each 'id' that must have the below 3 values.
1 - If Similar id and only one NULL value
2 - If Similar id and no NULL value
0 - If Similar id and both NULL value
EXAMPLE: Id 2 will get 1, id 3 will be assigned 0 and id 4 will get 2. There can be only 2 similar ids in the id table (there are no 3 values of 2 or 4)
I could find the similar id, but having difficulties writing the cases
select id
from table
group by id
having count(id) = 2
We can determine the status values by using aggregation:
WITH cte AS (
SELECT id,
CASE WHEN COUNT(*) > 1 AND COUNT(CASE WHEN last IS NULL THEN 1 END) = 1
THEN 1
WHEN COUNT(*) > 1 AND COUNT(CASE WHEN last IS NULL THEN 1 END) = 0
THEN 2
WHEN COUNT(*) > 1 AND COUNT(CASE WHEN last IS NULL THEN 1 END) = COUNT(*)
THEN 0 ELSE -1 END AS status
FROM yourTable
GROUP BY id
)
SELECT t1.*, t2.status
FROM yourTable t1
INNER JOIN cte t2
ON t1.id = t2.id;
Note that I assign a status value of -1 to any id which does not meet one of the three criteria. This would include any id which only appears once, among other edge cases.
You can do it this way
select a.id, last,
case
when exists(select 1 from _table b where a.id = b.id and coalesce(b.last,0) <> coalesce(a.last,0) and (a.last is null or b.last is null))
then 1
when exists(select 1 from _table b where a.id = b.id
and coalesce(b.last,0) <> coalesce(a.last,0))
and not exists(select 1 from _table b where a.id = b.id
and b.last is null)
then 2
when exists(select 1 from _table b where a.id = b.id )
and exists(select 1 from _table b where a.id = b.id and b.last is null and a.last is null having count(*) =
(select count(*) from _table b where a.id = b.id))
then 0
end as status
from _table a
Output:
id last status
2 NULL 1
2 2018 1
3 NULL 0
3 NULL 0
4 2011 2
4 2013 2
If you want one row per id:
select id,
(case count(*) filter (value is null)
when 1 then 1
when 0 then 2
when 2 then 3
end) as status
from t
group by id;
If you want this as a column on the original data, use window functions:
select t.*,
(case count(*) filter (value is null) over (partition by id)
when 1 then 1
when 0 then 2
when 2 then 3
end) as status
from t;

sql select records having count > 1 where at lease one record has value

I'm trying to get all participants that have more than 1 record in the table where at lease one of those records has IsCurrent = 0 and IsActive = 1
This is what I have so far, but it's not working:
SELECT ParticipantId
FROM Contact
WHERE (IsCurrent = 0 AND IsActive = 1 AND ContactTypeId = 1)
Group by ParticipantId
Having COUNT(ParticipantId) > 1
This query brings back a record that matches that description, but I need all of the records that match that description, there are more.
You can use EXISTS:
SELECT ParticipantId
FROM Contact
WHERE EXISTS
( SELECT 1
FROM Contact c2
WHERE c2.ParticipantID = c.ParticipantId
AND ContactTypeId = 1
GROUP BY ParticipantID
HAVING COUNT(*) > 1
AND COUNT(CASE WHEN IsCurrent = 0 AND IsActive = 1 THEN 1 END) >= 1
);
Use it as a subquery and join to it:
select * from
(
SELECT ParticipantId
FROM Contact
WHERE (IsCurrent = 0 AND IsActive = 1 AND ContactTypeId = 1)
Group by ParticipantId
Having COUNT(ParticipantId) > 1
) base
inner join Contact c on c.ParticipantId = base.ParticipantID
WHERE (IsCurrent = 0 AND IsActive = 1 AND ContactTypeId = 1)
select
ParticipantId
from Contact as c
group by
ParticipantId
having
Count(*) > 1
and
Sum(Case when IsCurrent = 0 then 1 else 0 end) >= 1
and
Sum(Case when IsActive = 1 then 1 else 0 end) >= 1
I would first try this
I think you should just remove:
AND ContactTypeId = 1
which seems to be an idexed column
SELECT ParticipantId
FROM Contact
Group by ParticipantId
Having Count(*) > 1
Intersect
SELECT ParticipantId
FROM Contact
WHERE IsCurrent = 0
AND IsActive = 1
AND ContactTypeId = 1

How do I return count and not count in a SQL query?

If I have a table
AgentID | IsNew | TeamID
1 N 1
2 Y 2
3 Y 2
4 N 2
5 Y 1
I want to return the following from a query:
Team | CountIsNew = N | CountIsNew = Y
1 1 1
2 1 2
Is there a way I can do this?
Using Oracle 10
SELECT team, SUM(DECODE(IsNew, 'N', 1, 0)), SUM(DECODE(IsNew, 'Y', 1, 0))
FROM mytable
GROUP BY
team
SELECT TeamId
, SUM(CASE WHEN IsNew = 'N' THEN 1 ELSE 0 END) AS CountIsNotNew
, SUM(CASE WHEN IsNew = 'Y' THEN 1 ELSE 0 END) AS CountIsNew
FROM Agent
GROUP BY TeamId
Yet another way - COUNT doesn't count NULLs (except for COUNT(*)):
SELECT TeamId,
COUNT(DECODE(IsNew,'N',1)) CountIsNotNew,
COUNT(DECODE(IsNew,'Y',1)) CountIsNew
FROM Agent
GROUP BY TeamId;
Or, if you prefer CASE:
SELECT TeamId,
COUNT(CASE IsNew WHEN 'N' THEN 1 END) CountIsNotNew,
COUNT(CASE IsNew WHEN 'Y' THEN 1 END) CountIsNew
FROM Agent
GROUP BY TeamId;
(note: the "1"s could be any literal value)