Postgresql combine IN with NOT IN - sql

I have a table of entities where each can have different statuses. For the sake of keeping history, each status change is reflected by a new row.
Example:
Entity Id Status
123456 1
123456 2
789000 1
Assuming i want to find all rows that have only status 1 (so if they have other statuses they should not be returned), How do I do that?

This query:
select entityid
from tablename
group by entityid
having min(status) = 1 and max(status) = 1
returns all the entityids that you want, so you can use it with the operator IN:
select * from tablename
where entityid in (
select entityid
from tablename
group by entityid
having min(status) = 1 and max(status) = 1
)

Just use not exists:
select t.*
from t
where not exists (select 1
from t t2
where t2.entity_id = t.entity_id and t2.status <> 1
);

Related

SQL remove duplicate row depend on certain value

I spend day in hope to figure out how to solve this query.
I have following table
ID Name Pregnancy Gender
1 Raghad Yes Female
1 Raghad No Female
2 Ohoud no Male
What I need is to remove duplicate (in this case 1,1) and to keep one of these rows which has a pregnancy status of yes.
To clarify, I can't use delete since it's a restricted database. I can only retrieve data.
Using an exists clause:
DELETE
FROM yourTable t1
WHERE
pregnancy = 'no' AND
EXISTS (SELECT 1 FROM yourTable t2 WHERE t2.ID = t1.ID AND t2.pregnancy = 'yes');
There are other ways to go about doing this, e.g. using ROW_NUMBER, but as you did not tag your database, I offer the above solution which should work on basically any database.
If you want to just view your data with the "duplicates" removed, then use:
SELECT *
FROM yourTable t1
WHERE
pregnancy = 'yes' OR
NOT EXISTS (SELECT 1 FROM yourTable t2 WHERE t2.ID = t1.ID AND t2.pregnancy = 'yes');
If column Pregnancy have just two values "Yes" and "No", in that case you can use ROW_NUMBER() also to get the results.
;WITH CTE
AS (
SELECT *,ROW_NUMBER() OVER (PARTITION BY id ORDER BY Pregnancy DESC) RN
FROM TABLE_NAME
)
SELECT *
FROM CTE
WHERE RN = 1
In case of multiple values when you want to give highest priorty to "Yes", you can write your query like following
;WITH CTE
AS (
SELECT *,ROW_NUMBER() OVER
(PARTITION BY id ORDER BY CASE WHEN Pregnancy = 'Yes' then 0 else 1 end) RN
FROM TABLE_NAME
)
SELECT *
FROM CTE
WHERE RN= 1
For this sample data you can group by ID, Name, Gender and return the maximum value of the column Pregnancy for each group since Yes is greater compared to No:
SELECT ID, Name, MAX(Pregnancy) Pregnancy, Gender
FROM tablename
GROUP BY ID, Name, Gender
See the demo.
Results:
> ID | Name | Pregnancy | Gender
> -: | :----- | :-------- | :-----
> 1 | Raghad | Yes | Female
> 2 | Ohoud | No | Male
Here is how you could do it in MySQL 8.
Similar Common Table Expressions exist in SQL Server and Oracle.
There you may need to add a comma after then closing parentheses that
ends the CTE (with) definition.
with dups as (
Select id from test
group by id
Having count(1) > 1
)
select * from test
where id in (select id from dups)
and Pregnancy = 'Yes'
union all
select * from test where id not in (select id from dups);
You can see it in action, by running it here
Note this does it without deleting the original.
But it gives you a result set to work with that has what you want.
If you wanted to delete, then you could use this instead, after the dups CTE definition:
delete from test
where id in (select id from dups) and Pregnancy = 'No'
Or distill this into:
delete from test
where id in (Select id from test
group by id
Having count(1) > 1) and Pregnancy = 'No'
1) First of all, update design of your table. ID must be primary key. This would automatically restrict the duplicate rows having same ID.
2) You can use Group by and having clause to remove duplicates
delete from table where pregnancy='no' and exists (SELECT
id
FROM table
GROUP BY id
HAVING count(id)>1)

SQL query to return rows where only one record is present in a given status

I have a table with data similar to below. I am trying to get a list of results that will display all rows where only one unique SourceID exists in status 10. If I were querying this table, I would expect ID's 3 and 4 to be returned.
Table Example
Select *
From table
Where Status = 10 and Source ID in
(
Select SourceID
From Table
Group by SourceID
Having Count(*) = 1
)
You can use NOT EXISTS :
SELECT t.*
FROM table t
WHERE NOT EXISTS (SELECT 1 FROM table t1 WHERE t1.SourceID = t.SourceID AND t1.Status <> t.Status);
Maybe that would work?
SELECT ID FROM Mytable
WHERE [Status] = 10
GROUP BY ID
HAVING COUNT(SourceID) = 1
First, find out all the unique SourceIDs
SELECT
SourceID
FROM
Data
GROUP BY
SourceID
HAVING
COUNT(SourceID) = 1
And then use this query as a sub query to get all the rows that has unique SourceID;
SELECT
*
FROM
Data
WHERE
SourceID IN (
SELECT
SourceID
FROM
Data
GROUP BY
SourceID
HAVING
COUNT(SourceID) = 1
)
Use a sub-query to check if t there is an exact count of 1 of those source id's
SELECT t.* FROM YourTable t WHERE t.status = 10
AND
(SELECT COUNT(0) x From YourTable t2
where t2.sourceid = t.sourceid) = 1

SQL Query If Condition for specific text within two rows

I need to write a query, but I'm not exactly sure how to write it. I need to grab all the EmployeeIds based off the status. If it is completed and pending, completed takes precedence. However, if there is only pending and not completed I'll take pending, else don't take any of the rows for that employee. I just need one item per EmployeeId. Technically I would also need to grab the earliest date but I think I would know how to write that part.
RowNumber Status EmployeeId Produce Date
-------------------------------------------------------
1 New 1 Apples 1/1/18
2 Pending 1 BlueBerry 1/2/18
3 New 1 Oranges 1/3/18
4 Pending 2 Bananas 1/1/18
5 New 2 Grapes 1/2/18
6 Complete 2 Limes 1/3/18
So in this example I need the following below
RowNumber Status EmployeeId Produce Date
--------------------------------------------------------
2 Pending 1 BlueBerry 1/2/18
6 Complete 2 Limes 1/3/18
The hardest part for me is trying to figure out how to compare strings. Basically (semi pseudo-code)
Select top 1
t.EmployeeId, t.Produce, t.Date, stat.Status
(Case
If t.Status = 'Complete'
Select 'Complete'
If t.Status = 'Pending'
Select 'Pending'
Else
Dont Add this row ) stat
From
Table t
Where
t.Status = 'Complete' or t.Status = 'Pending'
Order by
t.Date
There's at least two different approaches coming to my mind :
SELECT DISTINCT EmployeeId
FROM Table
WHERE Status = "Complete"
UNION
SELECT DISTINCT t1.EmployeeId
FROM Table t1
LEFT OUTER JOIN Table t2
ON t2.EmployeeId = t1.EmployeeId
AND t2.Status = "Complete"
WHERE t1.Status = "Pending"
AND t2.Status IS NULL
… or using coalesce() if your database engine supports it:
SELECT DISTINCT pend.EmployeeId,
coalesce(comp.Status,pend.Status),
coalesce(comp.RowNumber,pend.RowNumber)
FROM MyTable pend
LEFT OUTER JOIN MyTable comp
ON comp.EmployeeId = pend.EmployeeId
AND comp.Status = 'Complete'
WHERE pend.Status IN ('Complete','Pending');
Is this what you want using row_number() :
select top (1) with ties *
from table t
where Status in ('Completed', 'Pending')
order by row_number() over (partition by EmployeeId
order by (case Status when 'Completed'
then 0
when 'Pending'
then 1
end)
);
You could do this with a UNION ALL coupled with a WHERE NOT EXISTS:
SELECT t1.*
FROM [Table] t1
WHERE [Status] = 'Completed'
UNION ALL
SELECT t2.*
FROM [Table] t2
WHERE [Status] = 'Pending'
AND NOT EXISTS (
SELECT 1
FROM [Table]
WHERE EmployeeId = t2.EmployeeId
AND [Status] = [Completed]
);
You could calculate a ROW_NUMBER that uses a CASE WHEN.
And then filter on that.
For example:
SELECT RowNumber, [Status], EmployeeId, Produce, [Date]
FROM
(
SELECT
RowNumber, [Status], EmployeeId, Produce, [Date],
ROW_NUMBER() OVER (
PARTITION BY EmployeeId
ORDER BY [Date] DESC,
CASE [Status] WHEN 'Complete' THEN 1 WHEN 'Pending' THEN 2 ELSE 9 END
) AS RN
FROM [YourTable] t
WHERE [Status] IN ('Complete','Pending')
) q
WHERE RN = 1
ORDER BY EmployeeId

Find the latest 3 records with the same status

I need to find the latest 3 records for each user that has a particular status on 'Fail'. At first it seems easy but I just can't seem to get it right.
So in a table of:
ID Date Status
1 2017-01-01 Fail
1 2017-01-02 Fail
1 2017-02-04 Fail
1 2015-03-21 Pass
1 2014-02-19 Fail
1 2016-10-23 Pass
2 2017-01-01 Fail
2 2017-01-02 Pass
2 2017-02-04 Fail
2 2016-10-23 Fail
I would expect ID 1 to be returned as the most recent 3 records are fails, but not ID 2, as they have a pass within their three fails. Each user may have any number of Pass and Fail records. There are thousands of different IDs
So far I've tried a CTE with ROW_NUMBER() to order the attempts but can't think of a way to ensure that the latest three results all have the same status of Fail.
Expected Results
ID Latest Fail Date Count
1 2017-02-04 3
Maybe try something like this:
WITH cte
AS
(
SELECT id,
date,
status,
ROW_NUMBER () OVER (PARTITION BY id ORDER BY date DESC) row
FROM #table
),cte2
AS
(
SELECT id, max(date) as date, count(*) AS count
FROM cte
WHERE status = 'fail'
AND row <= 3
GROUP BY id
)
SELECT id,
date AS latest_fail,
count
FROM cte2
WHERE count = 3
Check This.
Demo : Here
with CTE as
(
select *,ROW_NUMBER () over( partition by id order by date desc) rnk
from temp
where Status ='Fail'
)
select top 1 ID,max(DATE) as Latest_Fail_Date ,COUNT(rnk) as count
from CTE where rnk <=3
group by ID
Ouptut :
I think you can do this using cross apply:
select i.id
from (select distinct id from t) i cross apply
(select sum(case when t.status = 'Fail' then 1 else 0 end) as numFails
from (select top 3 t.*
from t
where t.id = i.id
order by date desc
) ti
) ti
where numFails = 3;
Note: You probably have a table with all the ids. If so, you an use that instead of the select distinct subquery.
Or, similarly:
select i.id
from (select distinct id from t) i cross apply
(select top 3 t.*
from t
where t.id = i.id
order by date desc
) ti
group by i.id
having min(ti.status) = 'Fail' and max(ti.status) = 'Fail' and
count(*) = 3;
Here you go:
declare #numOfTries int = 3;
with fails_nums as
(
select *, row_number() over (partition by ID order by [Date] desc) as rn
from #fails
)
select ID, max([Date]) [Date], count(*) as [count]
from fails_nums fn1
where fn1.rn <= #numOftries
group by ID
having count(case when [Status]='Fail' then [Status] end) = #numOfTries
Example here

Find out particular id

I have a table in sql like this:
id billpay
-------------------------
1024 0
1024 0
1024 1
1025 1
1025 1
I want to retrieve only those id having billpay 1
Please help me with this
Try this:
select distinct id from yourtable where billpay = 1
It should be like this:
SELECT id FROM tabel WHERE billpay = 1;
This will retrieve those ids in ascending order which have at least one record in the table with billpay = 1.
The DISTINCT keyword will ensure you don't receive back multiple records with the same id.
SELECT DISTINCT id
FROM [TableName]
WHERE billpay = 1
ORDER BY id ASC
If you want to exclude those ids which also have records with billpay = 0, then use this:
SELECT DISTINCT id
FROM [TableName]
WHERE billpay = 1
AND id NOT IN (SELECT id FROM [TableName] WHERE billpay = 0)
ORDER BY id ASC
Regards,
select ID
from MyData
Where billpay = 1
Group By ID
The group by will list unique IDs
select ID
from MyData A
Where not exists (select 'X' from MyData B where B.billpay <> 1 and B.ID = A.ID)
Group By ID
This will only list IDs where billpay is only 1
Try this:
SELECT id
FROM mytable
GROUP BY id
HAVING COUNT(CASE WHEN COALESCE(billpay, 0) <> 1 THEN 1 END) = 0
The above will select only those ids associated to billpay=1 and nothing but billpay=1.
SQL Fiddle Demo
The following query selects the ids from group of ids where the number of records with billpay = 1 is the same as the number of records in the group
select id
from bills
group by id
having sum(billpay) = count(id)
Use NOT EXISTS to find rows with no other than billplay 1, use DISTINCT to return only one of each id found.
select distinct id
from tablename t1
where not exists (select 1 from tablename t2
where t1.id = t2.id
and t2.billpay <> 1)
Try to use GROUP BY +MIN statement to exclude Id's with existing billpay=0
SELECT id
FROM yourtable
GROUP BY id
HAVING MIN(billpay)=1