SQL: find one to many in one table - sql

there is a table:
t1
Id | Acc Status| Acc Type|
-----------------------------------------
1 |Online | home
2 |Offine | work
3 |Declined | work
1 |Activated | home
.
Question is:
need to find all Id which has 'Acc Status' = Online AND 'Acc Status' = Activated

You must group by id and count the distinct values of accstatus:
select id
from t1
where accstatus in ('Online', 'Activated')
group by id
having count(distinct accstatus) = 2
If you need the ids that have only these accstatus values:
select id
from t1
group by id
having
count(distinct accstatus) = 2
and
sum(case when accstatus not in ('Online', 'Activated') then 1 else 0 end) = 0

You can try below query -
SELECT id
FROM T1
WHERE AccStatus IN ('Online', 'Activated')
GROUP BY id
HAVING COUNT(DISTINCT AccStatus) = 2;

Try this below query
SELECT id
FROM Table_name
WHERE AccStatus = 'Online'
and id IN (SELECT id FROM Table_name wHERE AccStatus = 'Activated')

select DiSTINCT Id
from t1
where [Acc Status] in ("Online", "Activated")

Related

Oracle SQL: How to select only ID‘s which are member in specific groups?

I want to select only those ID‘s which are in specific groups.
For example:
ID GroupID
1 11
1 12
2 11
2 12
2 13
Here I want to select the ID's which are in the groups 11 and 12 but in no other groups.
So the result should show just the ID 1 and not 2.
Can someone provide a SQL for that?
I tried it with
SELECT ID FROM table
WHERE GroupID = 11 AND GroupID = 12 AND GroupID != 13;
But that didn't work.
You can use aggregation:
select id
from mytable
group by id
having min(groupID) = 11 and max(groupID) = 12
This having condition ensures that the given id belongs to groupIDs 11 and 12, and to no other group. This works because 11 and 12 are sequential numbers.
Other options: if you want ids that belong to group 11 or 12 (not necessarily both), and to no other group, then:
having sum(case when groupId in (11, 12) then 1 end) = count(*)
If numbers are not sequential, and you want ids in both groups (necessarily) and in no other group:
having
max(case when groupID = 11 then 1 end) = 1
and max(case when groupID = 12 then 1 end) = 1
and max(case when groupID in (11, 12) then 0 else 1 end) = 0
SELECT t.id FROM table t
where exists(
SELECT * FROM table
where group = 11
and t.id = id
)
and exists(
SELECT * FROM table
where group = 12
and t.id = id
)
and not exists(
SELECT * FROM table
where group = 13
and t.id = id
)
group by t.id
One method is conditional aggregation:
select id
from t
group by id
having sum(case when groupid = 1 then 1 else 0 end) > 0 and
sum(case when groupid = 2 then 1 else 0 end) > 0 and
sum(case when groupid in (1, 2) then 1 else 0 end) = 0 ;
You can use GROUP BY with HAVING and a conditional COUNT:
SELECT id
FROM table_name
GROUP BY ID
HAVING COUNT( CASE Group_ID WHEN 11 THEN 1 END ) > 0
AND COUNT( CASE Group_ID WHEN 12 THEN 1 END ) > 0
AND COUNT( CASE WHEN Group_ID NOT IN ( 11, 12 ) THEN 1 END ) = 0
Or you can use collections:
CREATE TYPE int_list IS TABLE OF NUMBER(8,0);
and:
SELECT id
FROM table_name
GROUP BY id
HAVING int_list( 11, 12 ) SUBMULTISET OF CAST( COLLECT( group_id ) AS int_list )
AND CARDINALITY( CAST( COLLECT( group_id ) AS int_list )
MULTISET EXCEPT int_list( 11, 12 ) ) = 0
(Using collections has the advantage that you can pass the collection of required values as a single bind parameter whereas using conditional aggregation is probably going to require dynamic SQL if you want to pass a variable number of items to the query.)
Both output:
| ID |
| -: |
| 1 |
db<>fiddle here
Use joins:
SELECT DISTINCT c11.ID
FROM (SELECT ID FROM WORK_TABLE WHERE GROUPID = 11) c11
INNER JOIN (SELECT ID FROM WORK_TABLE WHERE GROUPID = 12) c12
ON c12.ID = c11.ID
LEFT OUTER JOIN (SELECT ID FROM WORK_TABLE WHERE GROUPID NOT IN (11, 12)) co
ON co.ID = c11.ID
WHERE co.ID IS NULL;
The INNER JOIN between the first two subqueries ensures that rows exist for both GROUPID 11 and 12, and the LEFT OUTER JOIN and WHERE verify that there are no rows for any other GROUPIDs.
dbfiddle here

Check for same ID in 3 different tables

Hi all I have the following query to check if an ID is present in a table:
SELECT CASE WHEN EXISTS (SELECT 1 FROM (SELECT TOP 1 RequestID FROM tT
UNION
SELECT TOP 1 RequestID FROM tET
UNION
SELECT TOP 1 RequestID FROM tE) AS idSearcher
WHERE
idSearcher.RequestID = 120) THEN 'y' ELSE 'n' END AS alreadyHasID
This works but it doesn't seem to see if ANY of the 3 tables have the same ID. The above query only seems to check if ALL of the 3 tables has that value.
As an example the output for the query above:
|alreadyHasID |
+-------------+
n
When it should be 'y' since, out of the 3 tables, 1 table DOES have 120.
Running each of the 3 tables separately gives this as the output:
+----------+
|tT |
+----------+
no records
+----------+
|tET |
+----------+
no records
+----------+
|tE |
+----------+
120
How can this be modified in order to show a "y" if it finds the ID in ANY of the 3 tables?
Use inner joins to filter where there is a match for ALL:
SELECT CASE WHEN EXISTS(
SELECT *
FROM (tT INNER JOIN tET ON tT.RequestID = tET.RequestID)
INNER JOIN tE ON tT.RequestID = tE.RequestID
WHERE tT.RequestID = 120
) THEN 'y' ELSE 'n' END AS alreadyHasID
For ANY you do it this way:
SELECT CASE WHEN EXISTS(SELECT * FROM tT WHERE [RequestID] = 120) THEN 'y'
WHEN EXISTS(SELECT * FROM tET WHERE RequestID = 120) THEN 'y'
WHEN EXISTS(SELECT * FROM tE WHERE RequestID = 120) THEN 'y' ELSE 'n' END as [alreadyHasID]
select case
when exists (select 1 from tT where RequestID = 120)
or exists (select 1 from tTE where RequestID = 120)
or exists (select 1 from tE where RequestID = 120)
then 'y'
else 'n'
end
I assume you meant "to show a y if it finds the ID in ALL tables".
May be something like this:
select case when (
select count(case when RequestID = 120 then 1 end)
from (
select * from (select top 1 RequestID
from tT
order by ??)
union all
(select top 1 RequestID
from tET
order by ??)
union all
(select top 1 RequestID
from tE
order by ??)
) as t
) = 3 then 'y' else 'n' end as alreadyHasID
Note the added order by. You should always add these when using TOP queries.
This is so messed up
TOP is non deterministic
You are checking idSearcher.RequestID = 120 outside
SELECT CASE WHEN EXISTS (SELECT 1 FROM ( SELECT TOP 1 RequestID FROM tT
UNION
SELECT TOP 1 RequestID FROM tET
UNION
SELECT TOP 1 RequestID FROM tE
) AS idSearcher
WHERE idSearcher.RequestID = 120
)
THEN 'y' ELSE 'n' END AS alreadyHasID

Any other alternative to write this SQL query

I need to select data base upon three conditions
Find the latest date (StorageDate Column) from the table for each record
See if there is more then one entry for date (StorageDate Column) found in first step for same ID (ID Column)
and then see if DuplicateID is = 2
So if table has following data:
ID |StorageDate | DuplicateTypeID
1 |2014-10-22 | 1
1 |2014-10-22 | 2
1 |2014-10-18 | 1
2 |2014-10-12 | 1
3 |2014-10-11 | 1
4 |2014-09-02 | 1
4 |2014-09-02 | 2
Then I should get following results
ID
1
4
I have written following query but it is really slow, I was wondering if anyone has better way to write it.
SELECT DISTINCT(TD.RecordID)
FROM dbo.MyTable TD
JOIN (
SELECT T1.RecordID, T2.MaxDate,COUNT(*) AS RecordCount
FROM MyTable T1 WITH (nolock)
JOIN (
SELECT RecordID, MAX(StorageDate) AS MaxDate
FROM MyTable WITH (nolock)
GROUP BY RecordID)T2
ON T1.RecordID = T2.RecordID AND T1.StorageDate = T2.MaxDate
GROUP BY T1.RecordID, T2.MaxDate
HAVING COUNT(*) > 1
)PT ON TD.RecordID = PT.RecordID AND TD.StorageDate = PT.MaxDate
WHERE TD.DuplicateTypeID = 2
Try this and see how the performance goes:
;WITH
tmp AS
(
SELECT *,
RANK() OVER (PARTITION BY ID ORDER BY StorageDate DESC) AS StorageDateRank,
COUNT(ID) OVER (PARTITION BY ID, StorageDate) AS StorageDateCount
FROM MyTable
)
SELECT DISTINCT ID
FROM tmp
WHERE StorageDateRank = 1 -- latest date for each ID
AND StorageDateCount > 1 -- more than 1 entry for date
AND DuplicateTypeID = 2 -- DuplicateTypeID = 2
You can use analytic function rank , can you try this query ?
Select recordId from
(
select *, rank() over ( partition by recordId order by [StorageDate] desc) as rn
from mytable
) T
where rn =1
group by recordId
having count(*) >1
and sum( case when duplicatetypeid =2 then 1 else 0 end) >=1

SQL: Get multiple line entries linked to one item?

I have a table:
ID | ITEMID | STATUS | TYPE
1 | 123 | 5 | 1
2 | 123 | 4 | 2
3 | 123 | 5 | 3
4 | 125 | 3 | 1
5 | 125 | 5 | 3
Any item can have 0 to many entries in this table. I need a query that will tell me if an ITEM has all it's entries in either a state of 5 or 4. For example, in the above example, I would like to end up with the result:
ITEMID | REQUIREMENTS_MET
123 | TRUE --> true because all statuses are either 5 or 4
125 | FALSE --> false because it has a status of 3 and a status of 5.
If the 3 was a 4 or 5, then this would be true
What would be even better is something like this:
ITEMID | MET_REQUIREMENTS | NOT_MET_REQUIREMENTS
123 | 3 | 0
125 | 1 | 1
Any idea how to write a query for that?
Fast, short, simple:
SELECT itemid
,count(status = 4 OR status = 5 OR NULL) AS met_requirements
,count(status < 4 OR status > 5 OR NULL) AS not_met_requirements
FROM tbl
GROUP BY itemid
ORDER BY itemid;
Assuming all columns to be integer NOT NULL.
Builds on basic boolean logic:
TRUE OR NULL yields TRUE
FALSE OR NULL yields NULL
And NULL is not counted by count().
->SQLfiddle demo.
SELECT a.ID FROM (SELECT ID, MIN(STATUS) AS MINSTATUS, MAX(STATUS) AS MAXSTATUS FROM TABLE_NAME AS a GROUP BY ID)
WHERE a.MINSTATUS >= 4 AND a.MAXSTATUS <= 5
One way of doing this would be
SELECT t1.itemid, NOT EXISTS(SELECT 1
FROM mytable t2
WHERE itemid=t1.itemid
AND status NOT IN (4, 5)) AS requirements_met
FROM mytable t1
GROUP BY t1.itemid
UPDATE: for your updated requirement, you can have something like:
SELECT itemid,
sum(CASE WHEN status IN (4, 5) THEN 1 ELSE 0 END) as met_requirements,
sum(CASE WHEN status IN (4, 5) THEN 0 ELSE 1 END) as not_met_requirements
FROM mytable
GROUP BY itemid
simple one:
select
"ITEMID",
case
when min("STATUS") in (4, 5) and max("STATUS") in (4, 5) then 'True'
else 'False'
end as requirements_met
from table1
group by "ITEMID"
better one:
select
"ITEMID",
sum(case when "STATUS" in (4, 5) then 1 else 0 end) as MET_REQUIREMENTS,
sum(case when "STATUS" in (4, 5) then 0 else 1 end) as NOT_MET_REQUIREMENTS
from table1
group by "ITEMID";
sql fiddle demo
WITH dom AS (
SELECT DISTINCT item_id FROM items
)
, yes AS ( SELECT item_id, COUNT(*) AS good_count FROM items WHERE status IN (4,5) GROUP BY item_id
)
, no AS ( SELECT item_id, COUNT(*) AS bad_count FROM items WHERE status NOT IN (4,5) GROUP BY item_id
)
SELECT d.item_id
, COALESCE(y.good_count,0) AS good_count
, COALESCE(n.bad_count,0) AS bad_count
FROM dom d
LEFT JOIN yes y ON y.item_id = d.item_id
LEFT JOIN no n ON n.item_id = d.item_id
;
Can be done with an outer join, too:
WITH yes AS ( SELECT item_id, COUNT(*) AS good_count FROM items WHERE status IN (4,5) GROUP BY item_id)
, no AS ( SELECT item_id, COUNT(*) AS bad_count FROM items WHERE status NOT IN (4,5) GROUP BY item_id)
SELECT COALESCE(y.item_id, n.item_id) AS item_id
, COALESCE(y.good_count,0) AS good_count
, COALESCE(n.bad_count,0) AS bad_count
FROM yes y
FULL JOIN no n ON n.item_id = y.item_id
;
Nevermind, it was actually easy to do:
select ITEM_ID ,
sum (case when STATUS >= 3 then 1 else 0 end ) as met_requirements,
sum (case when STATUS < 3 then 1 else 0 end ) as not_met_requirements
from TABLE as d
group by ITEM_ID

Combining SQL join with count

I have two tables registered and attended, each with two columns: AttendentId and SessionId. I would like to query the count of AttendantId from these two tables individually for a particular session id.
Example:
registered
AttendantId SessionId
ID1 SN1
ID2 SN2
ID3 SN1
ID4 SN3
Attended
AttendantId SessionId
ID1 SN1
ID4 SN3
And I want to obtain the following output:
Count(Registered) Count(Attended) Session ID
2 1 SN1
1 0 SN2
1 1 SN3
You could use a FULL OUTER JOIN:
select
coalesce(a.sessionid, r.sessionid) sessionid,
count(r.AttendantId) countRegistered,
count(a.AttendantId) countAttended
from registered r
full outer join attended a
on r.sessionid = a.sessionid
and r.AttendantId = a.AttendantId
group by coalesce(a.sessionid, r.sessionid);
See SQL Fiddle with Demo
Try:
select count(distinct registered),
count(distinct attended),
SessionId
from (select AttendantId registered, null attended, SessionId
from registered
union all
select null registered, AttendantId attended, SessionId
from Attended) sq
group by SessionId
SELECT
ISNULL(ACount, 0),
ISNULL(RCount, 0),
X.SessionId
FROM
(
SELECT SessionId FROM Registered
UNION -- implies DISTINCT
SELECT SessionId FROM Attended
) X
LEFT JOIN
(SELECT COUNT(*) AS RCount, SessionId
FROM Registered
GROUP BY SessionId) R ON X.SessionId = R.SessionId
LEFT JOIN
(SELECT COUNT(*) AS ACount, SessionId
FROM Attended
GROUP BY SessionId) A ON X.SessionId = A.SessionId
SQLFiddle
Try this query
select
a.sessionId,
case when aCnt is null then 0 else acnt end,
case when bCnt is null then 0 else bcnt end
from
(select
sessionId,
count(*) aCNt
from
tbl1
group by
sessionid) a
full join
(select
sessionId,
count(*) bCnt
from
tbl2
group by
sessionid) b
on
a.sessionId = b.sessionid
SQL FIDDLE:
| SESSIONID | COLUMN_1 | COLUMN_2 |
-----------------------------------
| SN1 | 2 | 1 |
| SN2 | 1 | 0 |
| SN3 | 1 | 1 |
Hope this helps....
Assuming all session ID's exist in registered table
SELECT Sum(CASE
WHEN a.attendentid IS NOT NULL THEN 1
ELSE 0
end) AS Count(registered),
Sum(CASE
WHEN b.attendentid IS NOT NULL THEN 1
ELSE 0
end) AS Count(attended),
a.sessionid
FROM registered a
INNER JOIN attended b
ON a.sessionid = b.sessionid
GROUP BY a.sessionid