I have two tables, named supplier and contacts.
The data in the contact table corresponds to a record on the supplier table.
Data of supplier
ID
Name
1
Hp
2
Huawei
Data for the contact
id
supplierId
Contact
1
1
John
2
1
Smith
3
1
Will
4
2
Doe
5
2
Wick
Now, I want to make a query that should return the following result
ID
Name
Contact
1
Hp
John, Smith, Will
2
Huawei
Doe, Wick
or should return the following result
ID
Name
Contact
Contact
Contact
1
Hp
John
Smith
Will
2
Huawei
Doe
Wick
You can use MySQL GROUP_CONCAT aggregation function to get your first output table. It's own ORDER BY clause will allow you to check the order of concatenation for the rows.
SELECT s.ID,
s.Name,
GROUP_CONCAT(c.Contact ORDER BY c.id)
FROM Supplier s
INNER JOIN Contact c
ON s.ID = c.supplierId
GROUP BY s.ID,
s.Name
You can use the window function ROW_NUMBER to assign a rank to each row inside the Contact table by partitioning on the supplier. Then split the contacts into three columns using an IF statement that will check for the three possible values of the ranking. The MAX aggregation function will allow you to remove the nulls.
SELECT s.ID,
s.Name,
MAX(IF(c.rn = 1, c.Contact, NULL)) AS Contact1,
MAX(IF(c.rn = 2, c.Contact, NULL)) AS Contact2,
MAX(IF(c.rn = 3, c.Contact, NULL)) AS Contact3
FROM Supplier s
INNER JOIN (SELECT *, ROW_NUMBER() OVER(PARTITION BY supplierId
ORDER BY id) AS rn
FROM Contact ) c
ON s.ID = c.supplierId
GROUP BY s.ID,
s.Name;
This second query may not work if you have more than three customers per supplier. In that case you either modify the query to contain the possible maximum amount of suppliers, or you use a prepared statement. If you really need such a solution, leave a comment below.
For a better understanding, you can play with these solutions here. The first solution will work on any MySQL version while the second one will work with MySQL 8.
Query to show the table like you want :
SELECT supplier.ID, supplier.Name, contact.Contact
FROM supplier
INNER JOIN contact
ON supplier.ID = contact.supplierId;
Related
I need to make one SQL command.
From table with comments i'll get comment id, then
with this ID I need to get count of reactions with the same comment ID and user's names.
So for example I have this 2 tables:
Comments:
ID
Comm_text
1
Example text
2
Another example
and Reactions:
ID
comm_id
usr
etc..
1
1
Peter
another
2
1
John
collon
3
1
Dog
cuz
4
2
Cat
why not
I need to get this:
ID
Comm_text
Reactions_Count
Users
1
Example text
3
Peter, John, Dog
2
Another example
1
Cat
I tried this:
SELECT k.id, k.comm, COUNT(r.id) as reactions, r.usr
FROM `comms` k
INNER JOIN `reactions` r ON r.id=k.id
It's just one row with one comment and count of all rows in reaction table.
Thanks.
Try this query that makes the same output:
select comments.id as ID , comments.Comm_text as Comm_text ,
(select count(id) from Reactions where comm_id = comments.id) as Reactions_Count ,
(select coalesce(GROUP_CONCAT(usr ORDER BY usr DESC) , '') from Reactions WHERE comm_id = comments.id) as Users
from comments group by comments.id
You should use group by to group the comments and have just one row then use query to count and gather the data, based on each row of the group.
The GROUP_CONCAT attach the output with , and the coalesce set the output to a given string if the output was empty.
Read more about:
GROUP BY
GROUP_CONCAT
COALESCE
subquery
According to the names that u set in the example, this will work. Just fix the table names for your database structure.
SELECT `Comments`.`ID`, `Comments`.`Comm_text`, count(`Reactions`.`comm_id`) as react, `Reactions`.`usr`
FROM `Comments`
INNER JOIN `Reactions`
ON `Comments`.`ID`=`Reactions`.`comm_id`
GROUP BY `Reactions`.`comm_id`
I have two tables
Foods
----------
ID Name
----------
1. Chips
2. Pizza
3. Fish
4. Pasta
People
-----------------
ID Name FoodID
-----------------
1. Robert 3
2. Norman 2
3. Leonard 4
4. Huey 3
What I'm supposed to do is retrieve any food that belongs to more than one person. Now I'm supposed to do this by means of COUNT and WHERE. Possibly might need to do an INNER JOIN.
It should be simple but I'm just not seeing it.
You want to join the tables on the common field. The WHERE is serving as an inner join. Then GROUP BY Foods.name aggregate rows by food type. Count is an aggregate operator that always works with the GROUP BY.
SELECT Foods.Name, Count(*)
FROM Foods, People
WHERE Foods.ID = People.Food
GROUP BY Foods.Name
HAVING COUNT(*)>1;
Omit the first count(*) if you just want the list of foods.
select f.name, p.name, count(f.id) as total from foods f, people p
where (f.id = p.FoodID)
group by f.name
having total > 1
order by f.name
http://sqlfiddle.com/#!9/b2a63/10
select * from food inner join people on people.id = food.id group by people.name
I am not a databases guy,but I have been given the "fun" job of cleaning up someone else's database. We have many duplicate record in our databases and some of customers are getting double or triple billed every month.
Given the following Database example
:
Table: Customers
ID Name Phone DoNotBill
1 Acme Inc 5125551212 No
2 ABC LLC 7138221661 No
3 Big Inc 4132229807 No
4 Acme 5125551212 No
5 Tree Top 2127657654 No
Is it possible to write a query that Identifies the all duplicate phone numbers (in this case records 1 and 4) and then marks and duplicate records yes by updating the DoNotBill column. But leaves the first record unmarked.
In this example case we would be left with:
ID Name Phone DoNotBill
1 Acme Inc 5125551212 No
2 ABC LLC 7138221661 No
3 Big Inc 4132229807 No
4 Acme 5125551212 Yes
5 Tree Top 2127657654 No
something like this?
UPDATE
customers cust,
(SELECT
c1.ID,
c1.name,
c1.phone,
c1.DoNotBill
FROM customers c
LEFT JOIN
(SELECT
cc.ID
FROM customers cc
) as c1 on c1.phone = c.phone
) dup
SET cust.DoNotBill = 'Yes' WHERE cust.id=dup.id ;
To begin with I assume that the DoNotBill column only has two possible values; yes and no. In that case it should be bool instead of varchar, meaning it would be either true or false.
Furthermore I don't get the meaning of the DoNotBill column. Why wouldn't you just use something like this?
select distinct phone from customers
SQL SELECT DISTINCT
That would give you the phone numbers without duplicates and without the need for an extra column.
This depends on ur data amount
You can do it in steps and make use some tools like excel...
This qrt
SELECT a.id,b.id,a.phone FROM clients a , clients b WHERE
A.phone =b.phone
And a.id!=b.id
The result is all duplicated records.
Add
Group by a.phone
And u will get 1 record for each 2 duplicates.
if you like the records and they are whT u need. ChNge select to select a.id and
Use this qry as subqry to an update sql statement
UPDATE clients SET billing='no' WHERE id IN ( sql goes here)
UPDATE customers c SET c.DoNotBill="Yes";
UPDATE customers c
JOIN (
SELECT MIN( ID ) ID, Phone
FROM customers
GROUP BY Phone
) u ON c.ID = u.ID AND c.Phone = u.Phone
SET c.DoNotBill="No";
That way not only duplicates are eliminated, but all multiple entries are dealt with.
Not sure if I have phrased the title properly, but here it goes. I have these two tables:
table:staff
id Name groupId Status
1 John Smith 1 1
2 John Doe 1 1
3 Jane Smith 2 1
4 Jerry Smith 1 1
table:jobqueue
id job_id staff_id jobStatus
1 1 1 1
2 2 1 1
3 5 2 1
4 7 3 0
Now, what I need to do is to find the staff with the least amount of job assigned to him which I am able to do by querying the jobqueue table.
SELECT min(cstaff),tmp.staff_id FROM (SELECT t.staff_id, count(staff_id) cstaff from jobqueue t join staff s on t.staff_id=s.id join group g on s.groupId=g.id where g.id=26 GROUP BY t.id ) tmp
This works fine, but the problem is if a staff is not assigned to any job at all, this query wont get them, because it only queries the jobqueue table, where that particular staff won't have any entry. I need to modify the query to include the staff table and if a staff is not assigned any job in the jobqueue then I need to get the staff details from the staff table. Basically, I need to find staff for a group who are not assigned any job and if all staffs are assigned job then find staff with the least amount of jobs assigned. Could use some help with this. Also, tagging as Yii as I would like to know if this is doable with Yii active-records. But I am okay with a plain sql query that will work with Yii sql commands.
not sure that it is optimal query, but it works:
select d.groupId, d.name, (select count(*) from jobqueue as e where e.staff_id=d.id) as jobassigned
from staff as d
where d.id in (
select
(
select a.id
from staff as a
left outer join
jobqueue as b
on (a.id = b.staff_id)
where a.groupId = c.groupId
group by a.id
order by count(distinct job_id) asc
limit 1
) as notassigneduserid
from (
select distinct groupId from staff
) as c)
maybe need some comments:
c query is needed to get all distinct groupId - if you have separate table for this, you can replace it
notassigneduserid statement for each groupId select user with minimal job count
d query is needed to fetch actual user names, groupId for all found "unassigned users" and present it
here is the results for data from question:
Group Staff Jobs assigned
1 Jerry Smith 0
2 Jane Smith 1
with
counts as (
select s.groupId
, s.id
, (select count(*) from jobqueue where staff_id = s.id) count
from staff s
group by s.id, s.groupId),
groups as (
select groupId, min(count) mincount
from counts
group by groupId)
select c.groupId, c.id, c.count
from counts c
join groups g on c.groupId = g.groupId
where c.count = g.mincount
This SQL will give you all the staff with the minimum number of jobs in each group. It might be that more than one staff has the same minimum number of jobs. The approach is to use common table expressions to build first a list of counts, and then to retrieve the minimum count for each group. Finally I join the counts and groups tables and retrieve the staff that have the minimum count for each group.
I tested this on SQL Server, but the syntax should work for MySQL as well. To your data I added:
id Name groupId Status
5 Bubba Jones 2 1
6 Bubba Smith 1 1
and
id job_id staff_id jobStatus
5 4 5 1
Results are
group name count
1 Bubba Smith 0
1 Jerry Smith 0
2 Bubba Jones 1
2 Jane Smith 1
BTW, I would not try to do this with active record, it is far too complex.
As Ilya Bursov said this answer wasn't respond exactly what was asked. So here is a more optimized solution:
SELECT *
FROM (
SELECT s.id as id_staff, s.Name, s.groupId, count(distinct t.id) as jobsXstaff
FROM staff s
LEFT JOIN jobqueue t ON s.id=t.staff_id
GROUP BY s.id, s.groupId
ORDER BY s.groupId, jobsXstaff
) tmp
GROUP BY groupId
Old answer below.
This works but without table group which I don't create. You can simply join table groups as you did:
SELECT min(cstaff),tmp.id
FROM (
SELECT s.id, count( staff_id ) cstaff
FROM jobqueue t
RIGHT JOIN staff s ON t.staff_id = s.id
GROUP BY t.id
) tmp
As you see you need to get all values from table staff (right join) and select the id staff from it's own table (s.id instead of t.staff_id). Also you have to get tmp.id instead of staff_id now.
I am using SQL Server 2008 and I have 4 tables StudentAbsentees, Students, StudentSections and Sections
In the StudentAbsentees table I am storing the studentId that are absent (absentees only) on a particular day like,
StudentId Time Date
----------- ------ ------
1 10:00 2012-04-13
and in the StudentSections I am storing the studentId in a particular section like
StudentId SectionId
---------- ------------
1 1
2 1
3 1
and in the Students table I am storing student details, likewise in Sections table I have section details like name and capacity of that section.
I need to join these tables and display whether the student is present/absent on a particular day... the result should be
StudentId Status
--------- ------
1 Absent
2 Present
3 Present
I can get the absentees list from these tables, I dunno how to display whether they are present/absent....can anyone help me here
select * from (
select s.id,
case
when sa.date = '2012-01-01'
then 'absent'
else 'present'
end as status,
ROW_NUMBER() OVER (PARTITION BY s.id ORDER BY CASE WHEN sa.date = '2012-01-01' THEN 1 ELSE 2 END) AS RowNumber
from students s
left outer join studentabsentees sa on s.id = sa.studentid
)
as a where a.RowNumber = 1
You're query to show the status of all students for a particular day would look like:
select s.id, s.name, a.status
from student s
left join studentabsentees a on s.id = a.studentid
where a.date = ?
Obviously you have to supply a date.
Note: Your question uses "inner join" in the title. I think left is a better fit because it would show for all students. But if you really wanted just the ones that have a record in the absentee table then you could just change the word "left" in the query to "inner".
Note2: My query assumes a status field. If you don't have one then look at juergen d's answer.
No need for joins, you can just use set operators:
SELECT StudentID, 'Absent'
FROM StudentsAbsentees
WHERE [date] = ...
UNION
(
SELECT StudentID, 'Present'
FROM Students
EXCEPT
SELECT StudentID, 'Present'
FROM StudentsAbsentees
WHERE [date] = ...
)
You can display 'Present' and 'Absent' by just selecting them as constant. It's easy to get the list of all the absent students. Then union this with all the present students. Present students are found by taking the complete student list and using the except operator on the missing students. But in this except part make sure you select the absent students as present so they subtract nicely from the list of all students with present next to their name that you have just created.