SQL Count number of values given compound condition on row - sql

I have a table as follows:
User ID Service
1 2
1 3
2 1
2 3
3 5
3 3
4 3
5 2
How could I construct a query where I would count all the user ids that have a service of 3 and at least one other service?
In the above table, the query I'm interested in would return 3 because user ids 1, 2 and 3 have service of 3 and at least one other service.
Thanks!

In MS SQL:
select count(*)
from UserService
where
ServiceId = 3 and
UserId in (select UserId from UserService where ServiceId != 3)

EDIT In response to comments:
select count(*) from (
select userId
from theTable
group by userId
having sum(case when service = 3 then 1 else 0 end) > 0
and sum(case when service <>3 then 1 else 0 end) > 0
) x

Related

How to check the count of each values repeating in a row

I have two tables. Data in the first table is:
ID Username
1 Dan
2 Eli
3 Sean
4 John
Second Table Data:
user_id Status_id
1 2
1 3
4 1
3 2
2 3
1 1
3 3
3 3
3 3
. .
goes on goes on
These are my both tables.
I want to find the frequency of individual users doing 'status_id'
My expected result is:
username status_id(1) status_id(2) status_id(3)
Dan 1 1 1
Eli 0 0 1
Sean 0 1 2
John 1 0 0
My current code is:
SELECT b.username , COUNT(a.status_id)
FROM masterdb.auth_user b
left outer join masterdb.xmlform_joblist a
on a.user1_id = b.id
GROUP BY b.username, b.id, a.status_id
This gives me the separate count but in a single row without mentioning which status_id each column represents
This is called pivot and it works in two steps:
extracts the data for the specific field using a CASE statement
aggregates the data on users, to make every field value lie on the same record for each user
SELECT Username,
SUM(CASE WHEN status_id = 1 THEN 1 END) AS status_id_1,
SUM(CASE WHEN status_id = 2 THEN 1 END) AS status_id_2,
SUM(CASE WHEN status_id = 3 THEN 1 END) AS status_id_3
FROM t2
INNER JOIN t1
ON t2.user_id = t1._ID
GROUP BY Username
ORDER BY Username
Check the demo here.
Note: This solution assumes that there are 3 status_id values. If you need to generalize on the amount of status ids, you would require a dynamic query. In any case, it's better to avoid dynamic queries if you can.

SQL Query to get multiple resultant on single column

I have a table that looks something like this:
id name status
2 a 1
2 a 2
2 a 3
2 a 2
2 a 1
3 b 2
3 b 1
3 b 2
3 b 1
and the resultant i want is:
id name total count count(status3) count(status2) count(status1)
2 a 5 1 2 2
3 b 4 0 2 2
please help me get this result somehow, i can just get id, name or one of them at a time, don't know how to put a clause to get this table at once.
Here's a simple solution using group by and case when.
select id
,count(*) as 'total count'
,count(case status when 3 then 1 end) as 'count(status1)'
,count(case status when 2 then 1 end) as 'count(status3)'
,count(case status when 1 then 1 end) as 'count(status2)'
from t
group by id
id
total count
count(status3)
count(status2)
count(status1)
2
5
1
2
2
3
4
0
2
2
Fiddle
Here's a way to solve it using pivot.
select *
from (select status,id, count(*) over (partition by id) as "total count" from t) tmp
pivot (count(status) for status in ([1],[2],[3])) pvt
d
total count
1
2
3
3
4
2
2
0
2
5
2
2
1
Fiddle

Best way to by column and aggregation on another column

I want to create a rank column using existing rank and binary columns. Suppose for example a table with ID, RISK, CONTACT, DATE. The existing rank is RISK, say 1,2,3,NULL, with 3 being the highest. The binary-valued is CONTACT with 0,1 or FAILURE/SUCESS. I want to create a new RANK that will order by RISK once a certain number of successful contacts has been exceeded.
For example, suppose the constraint is a minimum of 2 successful contacts. Then the rank should be created as follows in the two instances below:
Instance 1. Three ID, all have a min of two successful contacts. In that case the rank mirrors the risk:
ID risk contact date rank
1 3 S 1 3
1 3 S 2 3
1 3 F 3 3
1 3 F 4 3
2 2 S 1 2
2 2 S 2 2
2 2 F 3 2
2 2 F 4 2
3 1 S 1 1
3 1 S 2 1
3 1 S 3 1
Instance 2. Suppose ID=1 has only one successful contact. In that case it is relegated to the lowest rank, rank=1, while ID=2 gets the highest value, rank=3, and ID=3 maps to rank=2 because it satisfies the constraint but has a lower risk value than ID=2:
ID risk contact date rank
1 3 S 1 1
1 3 F 2 1
1 3 F 3 1
1 3 F 4 1
2 2 S 1 3
2 2 S 2 3
2 2 F 3 3
2 2 F 4 3
3 1 S 1 2
3 1 S 2 2
3 1 S 3 2
This is SQL, specifically Hive. Thanks in advance.
Edit - I think Gordon Linoff's code does it correctly. In the end, I used three interim tables. The code looks like that:
First,
--numerize risk, contact
select A.* ,
case when A.risk = 'H' then 3
when A.risk = 'M' then 2
when A.risk = 'L' then 1
when A.risk is NULL then NULL
when A.risk = 'NULL' then NULL
else -999 end as RISK_RANK,
case when A.contact = 'Successful' then 1
else NULL end as success
Second,
-- sum_successes_by_risk
select A.* ,
B.sum_successes_by_risk
from T as A
inner join
(select A.person, A.program, A.risk, sum(a.success) as sum_successes_by_risk
from T as A
group by A.person, A.program, A.risk
) as B
on A.program = B.program
and A.person = B.person
and A.risk = B.risk
Third,
--Create table that contains only max risk category
select A.* ,
B.max_risk_rank
from T as A
inner join
(select A.person, max(A.risk_rank) as max_risk_rank
from T as A
group by A.person
) as B
on A.person = B.person
and A.risk_rank = B.max_risk_rank
This is hard to follow, but I think you just want window functions:
select t.*,
(case when sum(case when contact = 'S' then 1 else 0 end) over (partition by id) >= 2
then risk
else 1
end) as new_risk
from t;

SQL QUERY MERGE TWO ROW DATA

Suppose My Database is like this :
MemberName MemberID ResultsEligibilityID
Thuso 2 1
Thuso 2 1
Maubane 3 2
Maubane 3 1
CDeveloper 5 2
CDeveloper 5 2
Now is it possible to write a query to display (The Below output) based on this:
if both ResultsEligibilityID for a single Member is 1 then Eligibile,
Otherwise Non-Eligible.
OUTPUT
MemberName MemberID ResultsEligibilityID Results
Thuso 2 1 Eligible
Maubane 3 2 Non-Eligible
CDeveloper 5 2 Non-Eligible
Thanks in advance for the help.
Please try:
select
MemberName,
MemberID,
MAX(ResultsEligibilityID) ResultsEligibilityID ,
(case when sum(case when ResultsEligibilityID=1 then 1 else 0 end)= COUNT(*)
then 'Eligible' else 'Non-Eligible' end) Results
From
YourTable
group by MemberName,MemberID

Grouping multiple counts with each row representing one ID

I have a table that logs activity. It has an int activity column that indicates what type of activity it was and a corresponding admin_id that indicates which admin performed the action. Each row in the table represents one action, so for example, an administrator would cancel a user's account, and a row with that admin's id and activity=[int representing cancel] would be created. I need a way to a select a result which groups each admin's activity such that the row corresponding to the admin would have the # of each activity
The result would look like
admin_id | numActivated | numCanceled | numRenewed
1 1 1 2
2 1 3 0
With numActivated, numCanceled, and numRenewed all coming from the count for each activity.
This is what the original table would have looked like:
admin_id | activity ...
1 2
1 1
1 3
1 3
2 2
2 2
2 2
2 1
(activity 1: activate, 2: cancel, 3: renew)
Thanks
This should do what you're asking for:
select admin_id,
sum(case when activity = 1 then 1 else 0 end) as numActivated,
sum(case when activity = 2 then 1 else 0 end) as numCanceled,
sum(case when activity = 3 then 1 else 0 end) as numRenewed
from YourTable
group by admin_id
This should do it:
SELECT
T.admin_id,
sum(T.activate) AS numActivated,
sum(T.canceled) AS numCanceled,
sum(T.renew) AS numRenewed
FROM (
SELECT
admin_id,
CASE activity WHEN 1 THEN 1 ELSE 0 END AS activate,
CASE activity WHEN 2 THEN 1 ELSE 0 END AS canceled,
CASE activity WHEN 3 THEN 1 ELSE 0 END AS renew
FROM Table
) AS T
GROUP BY T.admin_id