Select distinct values in same column by group - sql

Using Microsoft Access and having a hard time figuring out how to get the desired results. I have two linked tables with an inner join on [bed] with the following data in both tables. I am looking to select [room number] that has a [gender] mismatch (has more than one distinct value per room number). I have searched stackoverflow and haven't found anything that seems to both group AND select distinct by group [room number].
Table 1
-----------------
Room Number | Bed
101 | 101a
101 | 101b
101 | 101c
102 | 102a
102 | 102b
103 | 103a
103 | 103b
Table 2
-----------------
Bed | Gender
101a | Male
101b | Male
101c | Female
102a | Male
102b | Male
103a | Female
103b | Undefined
With this data set, I would expect it to return Room 101 and 103 with the associated genders.
SQL Query
SELECT ROOM_NO
FROM RMGT_T_ROOMS INNER JOIN RMGT_T_ROOM_CONFIGS ON RMGT_T_ROOMS.PK_BED_SPACE = RMGT_T_ROOM_CONFIGS.CK_BED_SPACE
GROUP BY RMGT_T_ROOMS.FK_ROOM_NO
HAVING COUNT(DISTINCT GENDER) > 1

You could join that table on the bed's id and count the distinct number of genders:
SELECT room_number
FROM t1
JOIN t2 ON t1.bed = t2.bed
GROUP BY room_number
HAVING COUNT(DISTINCT gender) > 1

How about inner join and group by?
select t1.room
from t1 inner join
t2
on t1.bed = t2.bed
group by t1.room
having min(gender) <> max(gender);
If you know there are two genders only, you can add min(gender) and max(gender) to the select.
Another method uses exists:
select t1.room, t2.gender
from t1 inner join
t2
on t1.bed = t2.bed
where exists (select 1
from t1 as t11 inner join
t2 as t22
on t11.bed = t22.bed
where t22.gender <> t2.gender
);

Related

How to create a table with multiple calculations?

If I do one calculation with one join:
SELECT
SUM(friends_made) as calc1, table2.group_id
FROM
friends_made_table as table1
INNER JOIN
grouped_users as table2 ON table1.user_id = table2.user_id
GROUP BY
table2.group_id
The result I get is:
calc1 | group_id
-----------------
400 | 1
320 | 2
330 | 3
But I also need another calculation (calc2) with the same inner join on table1 but with a different table (table3)
SELECT
SUM(request_accept) AS calc2, table1.group_id
FROM
friends_accept_table AS table3
INNER JOIN
grouped_users as table1 ON table1.user_id = table3.user_id
GROUP BY
table1.group_id
Result is:
calc2 | group_id
-----------------
100 | 1
150 | 2
120 | 3
How can I join these two queries and create a new table showing both of the calculations (calc1, calc2)?
calc1 |calc2 | group_id
-----------------------
400 | 100 | 1
320 | 150. | 2
330 | 120. | 3
EDITED to show tables/results and take out rounding
A join will suffice as long as there is a common set of group_ids across the two results. You may otherwise need a left/right join or full join.
with data1 as (
SELECT SUM(friends_made) as calc1, table2.group_id
FROM friends_made_table as table1 INNER JOIN grouped_users as table2
ON table1.user_id = table2.user_id
GROUP BY table2.group_id
), data2 as (
SELECT SUM(request_accept) as calc2, table1.group_id
FROM friends_accept_table as table3 INNER JOIN grouped_users as table1
ON table1.user_id = table3.user_id
GROUP BY table1.group_id
)
select calc1, calc2, d1.group_id
from data1 d1 inner join data2 d2 on d2.group_id = d1.group_id;
This does assume that your platform supports CTE syntax. If it doesn't there are probably similar rewrites.

SQL - How to pick the best available value for each column for each ID from multiple tables?

I have two tables with the same variables referring to attributes of a person.
How can I combine data from two such tables picking the best available value for each column from each table for each field?
Requirements:
For each field, I would like to fill it with a value from either one of the tables, giving a preference to table 1.
Values can be NULL in either table
In the combined table, the value for column 1 could come from table 2 (in case table 1 is missing a value for that person) and the value for column 2 could from table 1 (because both tables had a value, but the value from table 1 is preferred).
In my real example, I have many columns, so an elegant solution with less code duplication would be preferred.
Some users may exist in only one of the tables.
Example:
Table 1:
user_id | age | income
1 | NULL| 58000
2 | 22 | 60000
4 | 19 | 35000
Table 2:
user_id | age | income
1 | 55 | 55000
2 | 19 | NULL
3 | 22 | 33200
Desired output:
user_id | age | income
1 | 55 | 58000
2 | 22 | 60000
3 | 22 | 33200
4 | 19 | 35000
I think that's a full join and priorization logic with colaesce():
select user_id,
coalesce(t1.age, t2.age) as age,
coalesce(t1.income, t2.income) as income
from table1 t1
full join table2 t2 using(user_id)
Use full outer join if user_id in each table is unique.
SELECT
COALESCE(t1.user_id, t2.user_id) AS user_id,
GREATEST(t1.age, t2.age) AS age,
GREATEST(t1.income, t2.income) AS income
FROM t1
FULL OUTER JOIN t2 ON t1.user_id = t2.user_id
try like below using coalesce()
select t1.user_id, coalesce(t1.age,t2.age),
t1.income>t2.income then t1.income else t2.income end as income
table1 t1 join table2 t2 on t1.usesr_id=t2.user_id
You can use below code:
With TableA(Id,age,income) as
( --Select Common Data
select table_1.id,
--Select MAX AGE
case
when table_1.age> table_2.age or table_2.age is null then table_1.age else table_2.age
end,
--Select MAX Income
case
when table_1.income>table_2.income or table_2.income is null then table_1.income else table_2.income
end
from table_1 inner join table_2 on table_2.id=table_1.id
union all
-- Select Specific Data of Table 2
select table_2.id,table_2.age,table_2.income
from table_2
where table_2.id not in (select table_1.id from table_1)
union all
-- Select Specific Data of Table 1
select table_1.id,table_1.age,table_1.income
from table_1
where table_1.id not in (select table_2.id from table_2)
)select * from TableA

How to count occurence of IDs and show this amount with name of item with this ID from other table in SQL?

if I have tables
Person: ID_Person, Name
Profession: ID_Prof, Prof_Name, ID_Person
If ID_Person appears multiple times in second table and I want to show all Person names with number of their professions how can I do this?
I know that if I want to count something I can write
SELECT ID_Person, count(*) as c
FROM Profession
GROUP BY ID_Person;
but don't know how to link it with column from other table in order to proper values.
Here is one way (MySQL InnoDB)
Person
+-----------+-------+
| ID_Person | Name |
+-----------+-------+
| 1 | bob |
| 2 | alice |
+-----------+-------+
Profession
+---------+--------------------+-----------+
| ID_Prof | Prof_Name | ID_Person |
+---------+--------------------+-----------+
| 1 | janitor | 1 |
| 2 | cook | 1 |
| 3 | computer scientist | 2 |
| 4 | home maker | 2 |
| 7 | astronaut | 2 |
+---------+--------------------+-----------+
select Name, count(Prof_Name)
from Person left join Profession
on (Person.ID_Person=Profession.ID_Person)
group by Name;
+-------+------------------+
| Name | count(Prof_Name) |
+-------+------------------+
| alice | 3 |
| bob | 2 |
+-------+------------------+
Hope this helps.
To just show those with multiple Profession then you would join the two tables, and aggregate with count() using group by and filter using having():
select pe.ID_Person, pe.Name, count(*) as ProfessionCount
from Person pe
inner join Profession pr
on pe.ID_Person = pr.ID_Person
group by pe.ID_Person, pe.Name
having count(*)>1
If you want to show the professions for those people as well:
select
multi.ID_Person
, multi.Name
, multi.ProfessionCount
, prof.ID_Prof
, prof.Prof_Name
from (
select pe.ID_Person, pe.Name, count(*) as ProfessionCount
from Person pe
inner join Profession pr
on pe.ID_Person = pr.ID_Person
group by pe.ID_Person, pe.Name
having count(*)>1
) multi
inner join Profession prof
on multi.ID_Person = prof.ID_Person
you can probably try something like this below. However, you will have to think about whether or not you need to left join versus inner join. You would want to left join if there is potentially someone who has not had any professions and therefore does not exist in the professions table.
SELECT pe.Name
, Professions = COUNT(pr.Prof_Name)
FROM dbo.Person (NOLOCK) pe
JOIN dbo.Profession (NOLOCK) pr ON pe.ID_Person = pr.ID_Person
GROUP BY pe.Name
You're looking for something like this I believe. The left join will bring in all the data and won't exclude any users.
The join can also be a inner join. Inner join would then only show users that exist in both tables.
LEFT
select x.ID_Person, count(x.ID_Person) as [count] from table1 x
left join table2 y on y.ID_Person= x.ID_Person
where x.ID_Person <> null
group by x.ID_Person
INNER
select x.ID_Person, count(y.ID_Person) from table1 x
inner join table2 y on y.ID_Person= x.ID_Person
group by x.ID_Person
The easiest solution is probably counting in a subquery:
select
id_person,
name,
(select count(*) from profession pr where pr.id_person = p.id_person) as profession_count
from person p;
You can achieve the same with an outer join:
select
p.id_person,
p.name,
coalesce(pr.cnt, 0) as profession_count
from person p
left join (select id_person, count(*) as cnt from profession group by id_person) pr
on pr.id_person = p.id_person;
It's usually a good idea to aggregate before joining. Anyway, this is how to join first and aggregate then:
select
p.id_person,
p.name,
coalesce(count(pr.id_person), 0) as profession_count
from person p
left join profession pr on pr.id_person = p.id_person
group by p.id_person, p.name;
As per standard SQL it would suffice to group by p.id_person, as the name functionally depends on the id (i.e. the id uniquely defines a person, so it's one single name belonging to it). Some DBMS however don't fully comply with the standard here and demand you to either put the name in the group by clause as shown or dummy-aggregate it in the select clause (e.g. max(p.name)) instead.

SQL COUNT conditional on the results another table

I am new to SQL and I'm having hard time with this scenario.
Table 1 contains information when a player was on a team.
Table1
playerID | yearID
----------------------
Player1 | 2000
Player1 | 2001
Player2 | 2000
Table 2 contains ballot information with the Hall of Fame. A player can be on multiple times.
playerID | BallotYear | Inducted
---------------------------------
Player1 | 2010 | N
Player1 | 2011 | N
Player2 | 2010 | Y
I am trying to count how many times a player shows up on Table1 conditional on them never being inducted based on Table2.
Desired Result
playerID | Count on Table 1
---------------------------
Player1 | 2
The issue I have come across is that Player1 returns 4, instead of 2. I have looked into a number of different functions, but I keep getting the same results. This is what I have come up with.
select Table1.playerID, count(Table1.playerID)
from Table1
join Table2 on Table1.playerID = Table2.playerID
where Table2.inducted = 'N'
group by Table1.playerID;
If I understand correctly, you want to filter out players that have ever had a 'Y' in the inducted field. If so:
select t1.playerId, count(*)
from table1 t1
where not exists (select 1
from table2 t2
where t2.playerID = t1.playerID and t2.inducted = 'Y'
)
group by t1.playerId;
You can achieve this using Left join which will also add to performance of query
select t1.playerId, count(*)
from table1 t1 left join
(select distinct playerID
from table2
where inducted = 'Y'
) t2 on t1.playerID = t2.playerID
where t2.playerId is null
group by t1.playerId

SQL Query - Get count of two columns from two tables

Table 1:
TicketNumber | Rules
---------------------------
PR123 | rule_123
PR123 | rule_234
PR123 | rule_456
PR999 | rule_abc
PR999 | rule_xyz
Table2:
TicketNumber | Rules
---------------------------
PR123 | rule_123
PR123 | rule_234
PR999 | rule_abc
NOTE: Both tables have the same structure: same column names but different count.
NOTE: Both tables have same set of TicketNumber values
CASE 1:
If I need ticket and rules count of each ticket from table1, the query is:
Select [TicketNo], COUNT([TicketNo]) AS Rules_Count from [Table1] group by TicketNo
This will give me output in format :
ticketNumber | Rules_Count
---------------------------
PR123 | 3
PR999 | 9
CASE 2: (NEED HELP WITH THIS)
Now, the previous query gets the ticket and the count of the ticket of only 1 table. I need the count of the same ticket (since both have same set of tkt nos) in table2 also.
I need result in this way:
ticketNumber | Count(ticketNumber) of table1 | Count(ticketNumber) of table2
---------------------------------------------------------------------------------
PR123 | 3 | 2
PR999 | 2 | 1
Both Table1 and table2 have the same set of ticket nos but different counts
How do i get the result as shown above?
A simpler solution from a "statement point of view" (without COALESCE that maybe it's not so easy to understand).
Pay attention to the performances:
Select T1.TicketNumber,T1.Rules_Count_1,T2.Rules_Count_2
FROM
(
Select [TicketNumber], COUNT([TicketNumber]) AS Rules_Count_1
from [Table1] T1
group by TicketNumber) T1
INNER JOIN
(
Select [TicketNumber], COUNT([TicketNumber]) AS Rules_Count_2
from [Table2] T2
group by TicketNumber
) T2
on T1.TicketNumber = T2.TicketNumber
SQL Fiddle Demo
You can do this with a full outer join after aggregation (or an inner join if you really know that both tables have the same tickets:
select coalesce(t1.TicketNo, t2.TicketNo) as TicketNo,
coalesce(t1.Rules_Count, 0) as t1_Rules_Count,
coalesce(t2.Rules_Count, 0) as t2_Rules_Count
from (Select [TicketNo], COUNT([TicketNo]) AS Rules_Count
from [Table1]
group by TicketNo
) t1 full outer join
(Select [TicketNo], COUNT([TicketNo]) AS Rules_Count
from [Table2]
group by TicketNo
) t2
on t1.TicketNo = t2.TicketNo;
SELECT A.center,
A.total_1st,
B.total_2nd
FROM (SELECT a.center,
Count (a.dose1) AS Total_1st
FROM table_1 a
GROUP BY a.center) A
INNER JOIN (SELECT b.center,
Count (b.dose2) AS Total_2nd
FROM table_2 b
GROUP BY b.center) B
ON a.center = b.center
ORDER BY A.center