I created the following tables:
create table people
(
ID varchar(10),
name varchar(35),
CONSTRAINT pk_ID PRIMARY KEY (ID)
);
create table numbers
(
code varchar(10),
ID varchar(10),
number numeric,
CONSTRAINT pk_code PRIMARY KEY (code)
);
I inserted the following datas:
insert into people(ID, name)
values('fdx1','Peter');
insert into people(ID, name)
values('fdx2','Alice');
insert into people(ID, name)
values('fdx3','Louis');
insert into numbers(code, ID, number)
values('001','fdx1',1);
insert into numbers(code, ID, number)
values('002','fdx1',1);
insert into numbers(code, ID, number)
values('003','fdx2',2);
insert into numbers(code, ID, number)
values('004','fdx2',3);
insert into numbers(code, ID, number)
values('005','fdx3',4);
insert into numbers(code, ID, number)
values('006','fdx3',4);
My problem is: how to select people that has the same number. For example "Peter" and "Louis".
By "same number" you mean that there is only one number in numbers for the person. You can do this with group by and having:
select n.id
from numbers n
group by n.id
having min(number) = max(number);
Note: this doesn't take NULL into account. Your question doesn't specify what to do if one of the values is NULL.
If I understand correctly you want to see out of the 2 rows for each user in numbers who has the same number twice? If so you could do.
SELECT p.ID, p.name, p.number, COUNT(DISTINCT n.id) as num
FROM people as p
INNER JOIN numbers as n on n.ID = p.ID
GROUP BY p.ID, n.number
HAVING num > 1
The would return a list of people how have the same number in numbers more than once
In case this was not what you were looking for some other things you could do are:
To return a list of people for a specific number you could do the following
SELECT p.ID, p.name
FROM people as p
INNER JOIN numbers as n on n.ID = p.ID
WHERE n.number = [[X]]
You would replace [[X]] with the number e.g. 1 this would then return a list of people who are linked to number 1 in this case Peter
If you wanted a list of all people and their associated number you could do:
SELECT p.ID, p.name, n.number
FROM people as p
INNER JOIN numbers as n on n.ID = p.ID
ORDER BY n.number
This would return the users ID, Name and their associated number.
You could use this query:
select p.name, n.number, count(*) repeats
from people p
inner join numbers n on n.ID = p.ID
group by p.id, n.number
having count(*) > 1
This lists the names of the people who have a duplicate number. That number is included in the output, and the number of times it occurs.
Output:
| name | number | repeats |
|-------|--------|---------|
| Peter | 1 | 2 |
| Louis | 4 | 2 |
sql fiddle
Related
Consider three tables,
Team
Members (Each member belongs to some team)
Tasks (each task is performed by some member)
Tasks Table
t_id member_id
1 1
2 1
3 2
4 1
Members Table
id name team_id
1 Ali 1
2 Khalil 1
3 Bilal 1
4 John 2
5 Smith 2
Now the result I want is the complete details of the Members Table of A PARTICULAR TEAM along with the Number of Total Tasks each member has performed.
To solve this, I wrote this query,
select m.*, count(t.member_id) as 'Tasks'
from tbl_member m
left join tbl_task t on m.m_id = t.member_id
group by t.member_id
having m.team_id = :team_id
where team_id can be any variable given by the user.
When I run this query for team_id = 1, I get these results (only printing Member Names and his total tasks)
m_name Tasks
Ali 3
Khalil 1
As you can see, it skips Bilal who is also part of Team_ID = 1 but because he has performed 0 Tasks, it doesn't print Bilal (even though I used left join)
Similarly, if I use Team_ID = 2, I get these reuslts,
m_name Tasks
John 0
It now prints John (who has done 0 Tasks) but it doesn't print Smith who also is part of Team 2 but has not done any task.
So, basically, the query is missing all those people who have done 0 tasks (unless all team members have done 0 tasks. In such a case, it only prints the first member of that team and skips the other, like in the case of Team ID = 2)
Can anyone please tell me how do I fix this? I want to print all the members of one team along with their count, even if their total task count is zero. Please note that it is not compulsory that this must be done using Joins. This can also be done with Subqueries but again, I couldn't make the right logic with subqueries either.
You can use subquery to get the number of task done without any left join or group by clause.
DB-Fiddle:
Schema and insert statements:
create table tbl_task(t_id int, member_id int);
insert into tbl_task values(1, 1);
insert into tbl_task values(2, 1);
insert into tbl_task values(3, 2);
insert into tbl_task values(4, 1);
create table tbl_member(id int, name varchar(100), team_id int);
insert into tbl_member values(1, 'Ali' ,1);
insert into tbl_member values(2, 'Khalil' ,1);
insert into tbl_member values(3, 'Bilal' ,1);
insert into tbl_member values(4, 'John' ,2);
insert into tbl_member values(5, 'Smith' ,2);
Query:
select m.*,(select count(t_id)from tbl_task t where t.member_id=m.id) as 'Tasks'
from tbl_member m
where m.team_id=1
Ouput:
id
name
team_id
Tasks
1
Ali
1
3
2
Khalil
1
1
3
Bilal
1
0
db<>fiddle here
Your GROUP BY and HAVING are undoing the LEFT JOIN. Try this:
select m.*, count(t.member_id) as Tasks
from tbl_member m left join
tbl_task t
on m.m_id = t.member_id and
m.team_id = :team_id
group by m.m_id;
Group by the left table columns, m.id ..
select m.*, count(t.member_id) as 'Tasks'
from tbl_member m
left join tbl_task t on m.id = t.member_id
where m.team_id = :team_id
group by m.id, m.name, m.team_id
I'm using column names from your table definitions. Your query uses different naming. Correct it as needed.
Gordon's answer is, of course, correct. There's another approach you might take however. It involves this subquery to count the tasks.
select member_id, count(*) numb
from tbl_task
group by member_id
Then you left join that to your members table.
select m.*, t.numb as 'Tasks'
from tbl_member m
left join ( select member_id, count(*) numb
from tbl_task
group by member_id
) t on m.m_id = t.member_id
where m.team_id = :team_id
This query pattern uses the main LEFT JOIN aggregate pattern, where the aggregate table contains either zero or one row corresponding to the main table. You may get some NULL values from team members who haven't done any tasks. You can fix that with COALESCE().
select m.*, COALESCE(t.numb, 0) as 'Tasks'
I wrote this up because I find the main LEFT JOIN aggregate pattern very useful for various report queries. For example, you might need this to get aggregates by member from two different tables. If you don't use the pattern you'll get a combinatorial explosion and high numbers. Here's an example counting absences as well as tasks.
select m.*, t.numb as 'Tasks', a.numb as 'Absences'
from tbl_member m
left join ( select member_id, count(*) numb
from tbl_task
group by member_id
) t on m.m_id = t.member_id
left join ( select member_id, count(*) numb
from tbl_absence
group by member_id
) a on m.m_id = t.member_id
where m.team_id = :team_id
Your original query didn't work correctly because you can convert a LEFT JOIN into an ordinary JOIN by mentioning columns from the second table in WHERE or HAVING clauses. That's because both NULL = value and NULL <> value always is false, so any WHERE criterion except WHERE (col IS NULL OR col = val) will not be met.
I am not sure how else to ask the question, so I will give an example, to see if this is possible with SQL.
Let's say a customer visits a store, and a System creates a VisitID. Then he places an Order in a table of Orders, linked to the VisitID. Then he fills in a Shipping Form, in a table of ShippingForms, linked to the Visit ID. Then the system generates a Receipt, in a table of ReceiptForms, linked to the VisitID. Then he fills in a Return Form, in a table of ReturnForms, linked to the VisitID.
So, is there a way to query the system to show for a VisitID there is/isn't an Orders record, Shipping Form record, Receipt record, Return record? This would be handy in a DBGrid to show all the activities of the customer on that VisitID. Each table (Orders, ShippingForm, ReceiptForm, ReturnForm, etc.) is of a different structure and different fields, but linked by the Visit ID, and may be present or not present, or may have several Orders during that VisitID.
So -- Select Orders, ShippingForm, ReceiptForm, ReturnForm where VisitID=x.
so I could present the information in a grid such as:
{
VisitID 2315
OrderID 1256
OrderID 1257
OrderID 1258
ReceiptID 5124
ReceiptID 5125
ReceiptID 5126
ShippingID 99023
ReturnID 582812
}
Visits that do not have records in all tables:
Select *
from Visits v -- visit always exists
left join Orders o on o.visitId = v.visitId
left join ShippingForm s on s.visitId = v.visitId
left join ReceiptForm r on r.visitId = v.visitId
left join ReturnForm rf on fr.visitId - v.visitId
where v.VisitID=x
and (o.visitId is null or s.visitId is null or r.visitId is null or fr.visitId is null ) -- it is null when record does not exist
Maybe you are looking for UNION ALL:
select visitid, type, id
from
(
select visitid, 'visit' as type, 1 as sortkey, null as id from visits
union all
select visitid, 'order' as type, 2 as sortkey, orderid as id from orders
union all
select visitid, 'receipt' as type, 3 as sortkey, receiptid as id from receipts
union all
select visitid, 'shipping' as type, 4 as sortkey, shippingid as id from shippings
union all
select visitid, 'return' as type, 5 as sortkey, returnid as id from returns
) data
order by visitid, sortkey;
Or you may want string aggregation. This is DBMS dependent and you haven't mentioned your DBMS. This is for MySQL:
select
visitid,
ord.ids as orderids,
rcp.ids as receiptids,
shp.ids as shippingids,
ret.ids as returnids
from visits v
left join
(select visitid, group_concat(orderid) as ids from orders group by visitid) ord
using (visitid)
left join
(select visitid, group_concat(receiptid) as ids from receipts group by visitid) rcp
using (visitid)
left join
(select visitid, group_concat(shippingid) as ids from shippings group by visitid) shp
using (visitid)
left join
(select visitid, group_concat(returnid) as ids from returns group by visitid) ret
using (visitid)
order by visitid;
Thank you all, you have allowed me to answer the question. For my purposes, I combined the results to make this sol
select
OrderID as FormID,
1 as FormType,
'Order ID' as FormTitle
from Orders O, Visits V where (O.VisitID=V.VisitID) and (V.VisitID=112)
union all
ShippingID as FormID,
2 as FormType,
'Shipping ID' as FormTitle
from Shipping S, Visits V where (S.VisitID=V.VisitID) and (V.VisitID=112)
etc.
so now have a line by line table for VisitID=112:
FormID FormType FormTitle
23 1 Order ID
26 1 Order ID
28 1 Order ID
342 2 Shipping ID
343 2 Shipping ID
367 2 Shipping ID
I have two tables, one is called contacts and the other one is called numbers. One stores contact information and looks like this
contacts
-------------------------------------------------------
| id | fname | lname | email | address | uid | uniqid |
-------------------------------------------------------
My second table which stores phone numbers that belong to specific contact look like this
numbers
---------------------
| id | number | cid |
---------------------
The cid is the same as the uniqid on contact table, how can i get the contact row with its numbers which is on the second table through mysql?
Update
Correction to the correct answer
SELECT id ,fname ,lname ,email ,address , uid, uniqid,number
FROM contacts a
inner join (SELECT cid, GROUP_CONCAT(DISTINCT number SEPARATOR ',') number FROM numbers) b ON b.cid=a.uniqid
It was missing DISTINCT
use join
select id ,fname ,lname ,email ,address , uid, uniqid,number
from contacts a
inner join numbers b on b.cid=a.uniqid
You can use GROUP_CONCAT to get multiple numbers to one row and then when you imply the join you won't get duplicates.
select `id` ,`fname` ,`lname` ,`email` ,`address` , `uid`, `uniqid`,`number`
from `contacts` a
inner join (Select `cid`, GROUP_CONCAT(`number` seperator ',') `number` from `numbers`) b on b.cid=a.uniqid
You can map the two id's make sure you have this as table index, for faster retrieval of data.
SELECT id ,fname ,lname ,email ,address , uid, uniqid, number from contacts a, number b WHERE a.uniqid = b.cid;
Just use inner join with n.cid = c.uniqid
select c.id,c.fname,c.lname,c.email,c.address,c.uid,c.uniqid,n.number
from contacts c
inner join numbers n
on n.cid = c.uniqid
using join is the right choice here:
SELECT con.*,num.* from contacts as con inner join numbers as num on con.uniqid = num.cid
Here we are using the concept of foreign key . Here cid is foreign key of contact table on number table. we have to match primary key of contact table with the foreign key of number table. if both are match then it's show the result.
Select a.id, a.fname, a.lname, a.email, a.address,
a.uid, a.uniqid,b.number from contact a, number b where a.id=b.id;
student table.
| regno | name | lname | address |gender | mobile |
booking table
bookID | regno | tokenNo | CheckIn | CheckOut
I am trying to get the no.of female students present between two given dates. I have tried to get the respective values but it outputs repetitive values as well even with the distinct keyword.
I have also tried to use Union and still it does the right opposite of distinct.
SELECT count(gender) FROM (SELECT distinct regno from student where
gender = 'Male' union SELECT Distinct regno from book) x LEFT OUTER JOIN
student a on x.regno = a.regno LEFT OUTER JOIN book b on x.regno = b.regno
where checkIn >= '2015/7/23' AND checkOut <= '2015/7/31';
this is the other I have tried
SELECT count(gender) FROM (SELECT distinct(regno), gender from student
where gender = 'Male' ) AS A inner JOIN book AS B On A.regno = B.regno
where checkIn >= '2015/7/23' AND checkOut <= '2015/7/31';
It looks like you are over-complicating a simple query. All you need to do is join the tables and filter on date and gender and apply count.
SELECT COUNT(DISTINCT s.regno)
FROM student s
JOIN booking b on s.regno = b.regno
WHERE s.gender = 'Female'
AND b.checkIn >= '2015/7/23' AND b.checkOut <= '2015/7/31';
The distinct keyword makes sure each student is only counted once if the have multiple bookings in the period. If you want to count all bookings just remove the distinct.
I have four tables that contain some fields
1. user(id, name, email, password, .....)
2. policy1(id, userid, ....)
3. policy2(id, userid, ....)
4. policy3(id, userid, ....)
Now this is clear that user's primary key (id) is foreign key in other three tables.
I want to fetch total number of tables against each user that contain user id in that table.
for example:
user id 1 has entry in any two tables,
user id 2 has entry in three tables,
user id 3 has entry in any single table
Result should be like
id total
1 2
2 3
3 1
I tried using Left Join but that I could not get the expected result.
You can use a subquery to get the count for each user and then add the values together to get the final result. I am using a LEFT JOIN to return all user rows even if there is not a matching value in the other tables. If you only want the users with values in the other tables, then you can use an INNER JOIN:
select u.id,
Coalesce(CntP1, 0) + Coalesce(CntP2, 0) + Coalesce(CntP3, 0) TotalCount
from `user` u
left join
(
select count(*) CntP1, userid
from policy1
group by userid
) p1
on u.id = p1.userid
left join
(
select count(*) CntP2, userid
from policy2
group by userid
) p2
on u.id = p2.userid
left join
(
select count(*) CntP3, userid
from policy3
group by userid
) p3
on u.id = p3.userid