Calculation filtered sets in SQL - sql

I have the following table invoving sort of 3 sets and I'm going to calculate the count of sets in whcich there is no (TaskId = 4), How can I achieve that?
SetId TaskId
1 0
1 1
1 4
2 0
2 2
2 3
3 0
3 2
3 4

Use conditional aggregation:
SELECT SetId
FROM yourTable
GROUP BY SetId
HAVING SUM(CASE WHEN TaskId = 4 THEN 1 ELSE 0 END) = 0;
The basic idea here is to scan each SetId group of records and count the number of times which a TaskId value of 4 occurs. The HAVING clause retains only groups for which the 4 value never occurs.

Use a CASE expression to check whether the TaskId value is 4. And use SUM function with grouping SetId.
Query
select [SetId],
SUM(case [TaskId] when 4 then 0 else 1 end) as [sum]
from [your_table_name]
group by [SetId];

I think you are looking for something like
SELECT *
FROM mytable t1
WHERE t1.SetId NOT IN (SELECT t2.SetId FROM mytable t2 WHERE t2.TaskId = 4)
(select the full sets that have no TaskId=4)
or
SELECT distinct SetId
FROM mytable t1
WHERE t1.SetId NOT IN (SELECT t2.SetId FROM mytable t2 WHERE t2.TaskId = 4)
(select just the SetIds that have no TaskId=4)

Related

SQL: return rows with only the earliest date for each id but only if it satisfies condition

I would like to get list of unique id that have 'condition=1' before 'condition=2'.
id
date
condition1
condition2
1
2022/02
1
0
1
2022/04
0
1
1
2022/05
0
0
2
2021/09
0
1
2
2022/01
1
0
3
2022/02
1
0
3
2022/05
0
1
In this case it would be 1 and 3.
SELECT id, MIN(date) FROM TABLE GROUP BY id
I know that i can do something like this to get first dates for id but i just cant figure out what to do for my problem
We can GROUP BY id and build two conditional MIN dates using CASE WHEN.
In the HAVING clause we say that the minimum date with condition 1 must appear before the minimum date with condition 2.
SELECT id
FROM yourtable
GROUP BY id
HAVING MIN(CASE WHEN condition1 = 1 THEN date END) <
MIN(CASE WHEN condition2 = 1 THEN date END)
ORDER BY id;
Try out here: db<>fiddle
Something like:
SELECT DISTINCT Id
FROM
(SELECT id, MIN(date)
FROM TheTable
WHERE Condition1 = 1
GROUP BY Id) c1
INNER JOIN
(SELECT Id, MIN(date)
FROM TheTable
WHERE Condition2 = 1
GROUP BY Id) c2
ON c1.Id=C2.Id AND c1.Date < c2.Date

Update a table based on the count of specific records in the same table

I'm using Microsoft SQL Server and I have a table shown below in my database:
Id groupName flag type
--------------------------------
1 aaa 0 0
2 aaa 1 0
3 aaa 0 0
4 bbb 0 0
5 bbb 0 0
6 bbb 0 0
I'd like to update the type column values based on the count of specific records in the same table.
Assuming that the records are grouped by their groupName, if a group has at least 1 record whose flag = 1, the typevalues of all the records in the same group should be 1, else 0.
In my case, I'd like to get the following output.
Id groupName flag type
--------------------------------
1 aaa 0 1
2 aaa 1 1
3 aaa 0 1
4 bbb 0 0
5 bbb 0 0
6 bbb 0 0
How can I do this with a SQL statement?
Edit
The following, of course, does not work.
UPDATE t1
SET type = CASE
WHEN ((SELECT COUNT(*) FROM tg) > 0)
THEN 1
ELSE 0
END
FROM [sampleDB].[dbo].[groups] t1
JOIN (SELECT t2.groupName, t2.flag
FROM [sampleDB].[dbo].[groups] t2) tg ON tg.groupName = t1.groupName
AND tg.flag = 1;
You can try the below -
UPDATE t
SET
type = case when cnt>=1 then 1 else 0 end
FROM
tablename t
INNER JOIN (select group_name, count(case when flag=1 then 1 end) as cnt from tablename
group by group_name)t1
ON t.group_name= t1.group_name
I would use window functions:
with toupdate as (
select g.*, max(flag) over (partition by groupname) as new_flag
from [sampleDB].[dbo].[groups] g
)
update toupdate
set flag = new_flag
where flag <> new_flag;
Note the where clause so only rows where the flag is changing are updated. SQL Server attempts to update row even when no values change, incurring overhead. The where clause saves most of this overhead.
You can simply do it by using a simple JOIN as:
UPDATE D
SET Type = 1
FROM Data D JOIN
(
SELECT GroupName
FROM Data
WHERE Flag = 1
) T ON D.GroupName = T.GroupName;
Here is a db-fiddle
This is the most performant way to solve this problem by using exists statement:
update t1
set type = 1
from table t1
where exists ( select 1
from table t2
where t1.groupname = t2.groupname
and flag = 1
)
This could be other option
;with data
As (
Select count(groupname) groupname
,max(flag) flag
,type
From Tablename
Group by type
Having count(groupname)>1
And max(flag)=1
)
Update c
Set type =1
From data c

Find subsequent occurrence of a value in a table

I have a table which looks like shown below
ID SubmittedValue ApprovedValue
1 25.9 0
1 29 29
1 25.9 25.9
1 50 0
1 45 0
1 10 0
1 10 10
Expected result
ID SubsequentlyApproved(CNT) Total_Amt_sub_aprvd
1 2 35.9
We get the above result because 25.9+10 since it is repeated in the subsequent rows.
How to perform VLOOKUP like functionality for this scenario. I tried the subquery but it didn't work.
SELECT a.id,
SUM(CASE WHEN a.ApprovedValue=0 THEN 1 ELSE 0 END) AS SUB_COUNT
FROM myTable a
join (select id, sum( case when SubmittedValue=ApprovedValue then 1 end) as check_value from myTable) b
on b.id=a.id and SUB_COUNT=check_value
but this is not giving me the expected result.
You seem to want to count rows where the values are the same and the first value appears more than once. If so, you can use window functions and aggregation:
select id, count(*), sum(ApprovedValue)
from (select t.*, count(*) over (partition by id, SubmittedValue) as cnt
from t
) t
where cnt > 1 and SubmittedValue = ApprovedValue
group by id
Without window functions using a semi-join
select id, count(*), sum(submittedvalue)
from test t1
where submittedvalue=approvedvalue
and exists (select 1
from test t2
where t1.id=t2.id and t1.submittedvalue=t2.submittedvalue
group by id, submittedvalue
having count(*)>1)
group by id;

Find Common Rows for some Row Values in SQL

I have a table with Ids and a subId column. And I have a user defined data type with a list of SubIds. I want all those ids which have all the sub-ids present in my user-defined data type. for example:
The table is:
ID SubID
1 2
1 3
1 4
2 3
2 4
2 2
3 3
3 2
and the data type is
CREATE TYPE SubIds AS TABLE
( SubId INT );
GO
With Value
SubID
3
4
I want the output to be
ID
1
2
Because only the ID 1 and 2 contain both the subIds 3 & 4
Note: the combination of Id and Sub ID will always be unique if its of any use
Let's assume that #s is your table of ids:
select t.ID
from t
Where t.SubId in (select SubId from #s)
group by t.Id
having count(*) = (select count(*) from #s);
This assumes that the two tables do not have duplicates. If duplicates are present, you can use:
select t.ID
from t
Where t.SubId in (select SubId from #s)
group by t.Id
having count(distinct t.SubId) = (select count(distinct s.SubId) from #s s);
Try this way
select ID
from yourtable
Where SubID in (3,4)
Group by ID
having Count(distinct SubID)=2
Another more flexible approach
select ID
from yourtable
Group by ID
having sum(case when SubID = 3 then 1 else 0 end) >= 1
and sum(case when SubID = 4 then 1 else 0 end) >= 1
If you want to pull SubId's from SubIds table type then,
SELECT ID
FROM yourtable T
JOIN (SELECT SubID,
Count(1) OVER() AS cnt
FROM SubIds) S
ON T.SubID = S.SubID
GROUP BY ID,Cnt
HAVING Count(DISTINCT T.SubID) = s.cnt

Checking if the row has the max value in a group

I'm trying get to find out if a row has the max value in a group. Here's really simple example:
Data
VoteCount LocationId UserId
3 1 1
4 1 2
3 2 2
4 2 1
Pseudo-query
select
LocationId,
sum(case
when UserId = 1 /* and has max vote count*/
then 1 else 0
end) as IsUser1Winner,
sum(case
when UserId = 2 /* and has max vote count*/
then 1 else 0
end) as IsUser2Winner
from LocationVote
group by LocationID
It should return:
LocationId IsUser1Winner IsUser2Winner
1 0 1
2 1 1
I also couldn't find a way to generate dynamic column names here. What would be the simplest way to write this query?
You could also do this using a Case statement
WITH CTE as
(SELECT
MAX(VoteCount) max_votes
, LocationId
FROM LocationResult
group by LocationId
)
SELECT
A.LocationId
, Case When UserId=1
THEN 1
ELSE 0
END IsUser1Winner
, Case when UserId=2
THEn 1
ELSE 0
END IsUser2Winner
from LocationResult A
inner join
CTE B
on A.VoteCount = B.max_votes
and A.LocationId = B.LocationId
Try this:
select *
from table t
cross apply (
select max(votes) max_value
from table ref
where ref.group = t.group
)votes
where votes.max_value = t.votes
but if your table is huge and has no propriate indexes performance may be poor
Another way is to get max values by groups into table variable or temp table and then join it to original table.