I have a table like this
The first table is the "Appointments" table with 'doctor_id' the second table is the "Doctors" table also with 'doctor_id'. The duplicate values from the appointments table represents how many appointment each doctor has. for example the doctor_id(50) has 6 appointments.
doctor_id(Appointments) | doctor_id(Doctors)
50 | 50
50 | 51
50 | 52
52 | 53
50 |
50 |
52 |
53 |
50 |
Now, my question is how would I show this table below (ps excluding the doctor id '51)
doctor_id (Doctors) | Count
50 | 6
52 | 2
53 | 1
This what i have tried so far, i have managed to work out how many doctors have appointments
using :
SELECT COUNT(*) FROM appointment
WHERE doctor_id > 50
I have executed other queries as well but with no luck.
Assuming doctor_id is unique in the doctors table...
SELECT d.doctor_id AS doctor_id
, COUNT(a.doctor_id) AS appointment_count
FROM doctors d
LEFT
JOIN appointments a
ON a.doctor_id = d.doctor_id
GROUP BY d.doctor_id
To get the count by doctor_id, you need to GROUP BY the doctor_id. You don't have to include doctor_id in the SELECT list, but if you only return the COUNT(), you'd just get a list of counts:
appointment_count
-----------------
6
0
2
1
With no indication of which appointment_count goes with which doctor_id. So, the usual pattern is to include what you GROUP BY in the SELECT list.
In order to return the 0 count for doctor 51, we want an outer join to the appointments table, so the row from the doctors table is returned even when there is no match.
If you only want rows for doctors that have one or more appointments, then you can just query
the appointments table. (This query won't return any "zero" count for a doctor.)
SELECT a.doctor_id
, COUNT(1) AS appointment_count
JOIN appointments a
GROUP BY a.doctor_id
Again, the GROUP BY clause is necessary to get a count of rows for each distinct value of doctor_id. That effectively collapses the rows for each doctor_id into a single row. The aggregate function operates on the "group" of rows.
You may use Group By doctor_id for counting how many times each doctor_id repeats itself :
SELECT doctor_id,COUNT(doctor_id) FROM appointment GROUP BY doctor_id
SELECT d.doctor_id, COUNT(a.doctor_id) FROM doctors d
Join appointment a
d.doctor_id =a.doctor_id
WHERE d.doctor_id > 50
GROUP BY a.doctor_id
try this code and hope this works. I am grouping the doctors appointments by the id
Given the information in the question this can be achieved using just the appointment table:
SELECT appointments.doctor_id, Count(appointments.doctor_id)
FROM appointments
GROUP BY appointments.doctor_id;
However as the question specifically asked about a JOIN the following code will work:
SELECT d.doctor_id, COUNT(a.doctor_id) AS appointment_count
FROM doctors d
JOIN appointments a
ON a.doctor_id = d.doctor_id
GROUP BY d.doctor_id;
A working sample is available at SQLFiddle: SQLFiddle
Related
I have two MySQL tables, called "accounts" and "events".
Accounts
ID
name
1
Pete
2
Josh
3
Harry
Events
ID
date
account_id
1
2021-10-11
1
2
2021-10-23
1
3
2021-10-23
2
4
2021-11-06
1
The account_id in the events table is linked to the id in the accounts table.
My question is: which query can I use to count only saturdays (date YYYY-mm-dd format) for each unique user in the accounts table? So I get the next result:
Name
Saturdays
Josh
1
Pete
2
Harry
0
I've tried many queries (with i.e. the (inner) JOIN, DISTINCT and GROUP BY keywords) but I don't get the exact result. Can you please help me?
Many thanks in advance!
I think something like this should work (untested):
SELECT
`a`.`name` AS `Name`,
COUNT(`e`.`ID`) AS `Saturdays`
FROM `accounts` AS `a`
LEFT JOIN `events` AS `e` ON `e`.`account_id` = `a`.`ID` AND WEEKDAY(`e`.`date`) = 5
GROUP BY `a`.`ID`
I have one table of tbl_order where columns are :
id_order
username
delivery_date etc...
and other table tbl_order_foods where columns are :
id_order
id_food
food_quantity etc
Here the sample pic of tbl_order table
and tbl_order_foods table
Here i am want to select all the foods with quantity with price and unit based on delivery_date.
Ex: Suppose On 2020-04-18 there are 3 orders where foods: tomato's are ordered total 15 kg (3 orders * 5 quantity * food_min_unit_amount * unit) and so on others foods.
how i can get the foodlist of total ordered quantity based on delivery_date
The method you need is the GROUP BY keyword.
SQL grouping works on the given attributes. According to your question this would be the delivery_date. In the projection (the attributes following the SELECT keyword) you then can use attributes you state after the GROUP BY keyword and aggregation functions e.g. SUM and MAX.
Based on your question you could get the total price for all orders on the given date like this:
SELECT order.delivery_date, SUM(food.food_quantity) as amount, SUM(food.food_total_price) as revenue
FROM tbl_order as order
INNER JOIN tbl_order_foods as food ON order.id_order = food.id_order
GROUP BY order.delivery_date
This would result in a list like this:
date | amount | revenue
------------------------
05.01| 10 | 800.00
I don't know whether this is what you wish. If you also want to split it into order positions then you would GROUP BY the food_name as well and add it to the projection which then results in the SUM grouped by the orders on a given day of a given product.
SELECT order.delivery_date, food.food_name, SUM(food.food_quantity) as amount, SUM(food.food_total_price) as revenue
FROM tbl_order as order
INNER JOIN tbl_order_foods as food ON order.id_order = food.id_order
GROUP BY order.delivery_date, food.food_name
Which would result in something like this.
date | food_name | amount | revenue
-----------------------------------
05.01| Tomato | 10 | 800.00
05.01| Apple | 5 | 400.00
Check out GROUP BY with the SUM function, a good article from Tutorialspoint.com is here. The only difference is that you are performing a JOIN to create the resulting table.
SELECT DISTINCT [f.id_food], [o.delivery_date], SUM(f.food_quantity) as [delivered_sum_qty], SUM(f.food_total_price) as [sum_food_total_price]
FROM tbl_order o LEFT JOIN tbl_order_foods f
ON [o.id_order] = [f.id_order]
GROUP BY [o.delivery_date];
If you need it, a pretty clear explanation of how to use JOIN is here as well.
I have two tables in the datbase to store client basic info (name, location, phone number) and another table to store client related transactions (date_sub, profile_sub,isPaid,date_exp,client_id) and i have an html table to view the client basic info and transaction if are available, my problem that i can't get a query to select the client info from table internetClient and from internetclientDetails at the same time, because query is only resulting when client have trans in the detail table. the two table fields are as follow:
internetClient
--------------------------------------------------------
id full_name location phone_number
-------------------------------------------------------
4 Joe Amine beirut 03776132
5 Mariam zoue beirut 03556133
and
internetclientdetails
--------------------------------------------------------------------------
incdid icid date_sub date_exp isPaid sub_price
----------------------------------------------------------------------------
6 4 2018-01-01 2018-01-30 0 2000
7 5 2017-01-01 2017-01-30 0 1000
8 4 2018-03-01 2018-03-30 1 50000
9 5 2018-05-01 2019-05-30 1 90000
// incdid > internetClientDetailsId
// icid> internetClientId
if client have trans in orderdetails, the query should return value like that:
client_id full_name date_sub date_exp isPaid sub_price
-------------------------------------------------------------------------------------
4 Joe Amine 2018-03-01 2018-03-30 1 50000
5 Mariam zoue 2018-05-01 2019-05-30 1 90000
else if the client has no id in internetOrederDetails
--------------------------------------------------------
icid full_name location phone_number
-------------------------------------------------------
4 Joe Amine beirut 03776132
5 Mariam zoue beirut 0355613
Thanks in advance
try with left join. It will display all records from internetClient and related record from internetclientdetails
Select internetClient.id, internetClient.full_name
, internetClient.location, internetClient.phone_number
, internetclientdetails.incdid, internetclientdetails.icid
, internetclientdetails.date_sub, internetclientdetails.date_exp
, internetclientdetails.isPaid, internetclientdetails.sub_price
from internetClient
left join internetclientdetails
on internetClient.id=internetclientdetails.icid group by internetclientdetails.icid order by internetclientdetails.incdid desc
if you want to get records of, only paid clients then you can try the following
Select internetClient.id, internetClient.full_name
, internetClient.location, internetClient.phone_number
, internetclientdetails.icid, internetclientdetails.incdid
, internetclientdetails.date_sub, internetclientdetails.date_exp
, internetclientdetails.isPaid, internetclientdetails.sub_price
from internetClient
left join internetclientdetails
on internetClient.id=internetclientdetails.icid
and internetclientdetails.isPaid=1 group by internetclientdetails.icid
order by internetclientdetails.incdid desc
SUMMARY
We generate a dataset containing just the ICID and max(date_sub) (alias:ICDi) We join this to the InternetClientDetails (ICD) to obtain just the max date record per client. Then left join this to the IC record; ensuring we keep all InternetClient(IC) records; and only show the related max Detail Record.
The below approach should work in most mySQL versions. It does not use an analytic which we could use to get the max date instead of the derived table provided the MySQL version you use supported it.
FINAL ANSWER:
SELECT IC.id
, IC.full_name
, IC.location
, IC.phone_number
, ICD.icid
, ICD.incdid
, ICD.date_sub
, ICD.date_exp
, ICD.isPaid
, ICD.sub_price
FROM internetClient IC
LEFT JOIN (SELECT ICDi.*
FROM internetclientdetails ICDi
INNER JOIN (SELECT max(date_sub) MaxDateSub, ICID
FROM internetclientdetails
GROUP BY ICID) mICD
ON ICDi.ICID = mICD.ICID
AND ICDi.Date_Sub = mICD.MaxDateSub
) ICD
on IC.id=ICD.icid
ORDER BY ICD.incdid desc
BREAKDOWN / EXPLANATION
The below gives us a subset of max(date_Sub) for each ICID in clientDetails. We need to so we can filter out all the records which are not the max date per clientID.
(SELECT max(date_sub) MaxDateSub, ICID
FROM internetclientdetails
GROUP BY ICID) mICD
Using that set we join to the details on the Client_ID's and the max date to eliminate all but the most recent detail for each client. We do this because we need the other detail attributes. This could be done using a join or exists. I prefer the join approach as it seems more explicit to me.
(SELECT ICDi.*
FROM internetclientdetails ICDi
INNER JOIN (SELECT max(date_sub) MaxDateSub, ICID
FROM internetclientdetails
GROUP BY ICID) mICD
ON ICDi.ICID = mICD.ICID
AND ICDi.Date_Sub = mICD.MaxDateSub
) ICD
Finally the full query joins the client to the detail keeping client even if there is no detail using a left join.
COMPONENTS:
You wanted all records from InternetClient (FROM internetClient IC)
You wanted related records from InternetClientDetail (LEFT Join InternetClientDetail ICD) while retaining teh records from InternetClient.
You ONLY wanted the most current record from InternetClientDetail (INNER JOIN InternetClientDetail mICD as a derived table getting ICID and max(date))
Total record count should = total record count in InternetClient which means all relationships must be a 1:1o on the table joins -- one-to-one Optional.
I have the following three tables to look after support tickets in a small web application, but I need some help getting the data I need.
Table 1 (ticket):
user_ID site_ID support_ID timestamp priority title
12 25 3 2014-09-26 14:09:25 0 A Test Row
12 26 4 2014-09-27 09:41:18 0 A 2nd Test Row
Table 2 (ticket_reply):
reply_ID support_ID user_ID support_reply reply_timestamp
3 3 12 some really boring text 2014-09-26 14:09:25
4 3 25 some really boring reply 2014-09-26 15:35:18
5 4 12 some really boring text 2014-09-27 09:41:18
Table 3 (ticket_status):
ticket_status_ID support_ID status_ID status_timestamp
3 3 40 2014-09-26 14:09:25
4 3 41 2014-09-26 15:35:18
5 4 40 2014-09-27 09:41:18
The 1st table holds the key ticket information, the 2nd, any replies made to the corresponding ticket, and the third tracks the change in status (statuses are held in another table, but don't need anything from there).
What I need to do is get the number of tickets where the latest status is == 40, and if this is greater than 0, get the latest reply along with the data from the first table.
I've tried multiple ways of doing this, but I am stuck. Don't really want to paste them here as they will likely confuse people, and I doubt they are even close.
This one was rather tricky, however here is a working solution for you.
This query will get the most recent support_reply value for all tickets where the most recent status_ID is 40.
SELECT
ticket_status_ID,
support_ID,
status_ID,
status_timestamp,
reply_ID,
support_reply,
reply_timestamp,
`timestamp` ticket_timestamp,
`priority` ticket_priority,
title
FROM (
SELECT * FROM (
SELECT * FROM (
SELECT
ticket_status.ticket_status_ID,
ticket_status.support_ID,
ticket_status.status_ID,
ticket_status.status_timestamp,
ts1.reply_ID,
ts1.user_ID,
ts1.support_reply,
ts1.reply_timestamp
FROM
ticket_status
INNER JOIN (SELECT * FROM ticket_reply ORDER BY reply_timestamp DESC) ts1 ON ts1.support_ID = ticket_status.support_ID
GROUP BY support_ID, status_ID
ORDER BY status_timestamp DESC
) ts2
GROUP BY ts2.support_ID
) ts3
INNER JOIN (SELECT support_ID as `ticket_support_ID`, site_ID, `timestamp`, priority, title FROM ticket) ts4 ON ts4.ticket_support_ID = ts3.support_ID
WHERE ts3.status_ID = 40
) ts5
From the example given, it looks that all timestamp are equivalent, so a query like this should be enough:
SELECT
ticket.*,
ticket_reply.*
FROM
(SELECT support_ID, MAX(status_timestamp) as max_timestamp
FROM ticket_status
GROUP BY support_ID) m
INNER JOIN ticket
ON m.support_ID=ticket.support_ID
AND m.max_timestamp=ticket.`timestamp`
INNER JOIN ticket_reply
ON m.support_ID=ticket_reply.support_ID
AND m.max_timestamp=ticket_reply.reply_timestamp
INNER JOIN ticket_status
ON m.support_ID=ticket_status.support_ID
AND m.max_timestamp=ticket_status.status_timestamp
WHERE
status_ID=40;
but depending on the logic of your application, it might happen that the last row in a table has a timestamp of 2014-09-27 09:41:18 and the last in another has for example 2014-09-27 09:41:19.
In this case, you should use a query like this one:
SELECT
ticket.*,
ticket_reply.*
FROM
(SELECT support_ID, MAX(status_timestamp) AS max_status_timestamp
FROM ticket_status
GROUP BY support_ID) m_status
INNER JOIN
(SELECT support_ID, MAX(reply_timestamp) AS max_reply_timestamp
FROM ticket_reply
GROUP BY support_ID) m_reply
ON m_status.support_ID=m_reply.support_ID
INNER JOIN
(SELECT support_ID, MAX(`timestamp`) AS max_ticket_timestamp
FROM ticket
GROUP BY support_ID) m_ticket
ON m_status.support_ID=m_ticket.support_ID
INNER JOIN ticket_status
ON ticket_status.support_ID=m_status.support_ID
AND ticket_status.status_timestamp=m_status.max_status_timestamp
INNER JOIN ticket_reply
ON ticket_reply.support_ID=m_reply.support_ID
AND ticket_reply.reply_timestamp=m_reply.max_reply_timestamp
INNER JOIN ticket
ON ticket.support_ID=m_ticket.support_ID
AND ticket.`timestamp`=m_ticket.max_ticket_timestamp
WHERE
ticket_status.status_ID=40;
Please see fiddle here.
You can try this one:
SELECT t.*, tr.support_reply, ts.status_timestamp
FROM ticket_status as ts
left join ticket_reply as tr on(ts.support_ID=tr.support_ID)
left join ticket as t on(t.support_ID=tr.support_ID)
where status_ID=40
order by status_timestamp desc
limit 1;
I'm trying to LEFT JOIN two tables and GROUP BY a field of the first table in MySQL.
If there are multiple rows in the second table for each record in the first one, a GROUP BY eliminates all records but one of the right table. Is there a way to determine which one it is?
To give you a specific example, I want to get a list of users, joined with the IDs of their (for example) most expensive purchases (or most recent purchases, or whatever..) It seems like an easy task, but I'm frustrated and have asolutely no idea how to do it!
Table 1: Users
userId, userName
1 Frank
2 Sarah
3 Tim
Table 2: Purchases
orderId, userId, value
1 3 14.99
2 2 9.99
3 3 79.99
4 1 2.99
5 2 14.99
SELECT * FROM Users LEFT JOIN Purchases ON Users.userId = Purchases.userId
will return:
userId, userName, orderId, value
1 Frank 4 2.99
2 Sarah 2 9.99
2 Sarah 5 14.99
3 Tim 1 14.99
3 Tim 3 79.99
Now if I GROUP BY userId the result will be:
userId, userName, orderId, value
1 Frank 4 2.99
2 Sarah 2 9.99
3 Tim 1 14.99
Is there a way to decide in this case which orderId is kept or is there a completely other and better way to do this?
I have tried some things like MAX() but this will always only return the highest value of the whole table, not individually for each user.
Thank you in advance, you awesome stackoverflow-community!
best Florian
In strict SQL this Query would not be valid as in a Group by context u should select only fields contained in the group by clause or aggregates.
Mysql however allows this syntax and handles it as "i dont care about this fields", you can not define which of the rows values is selected then.
But you can do it with a query like this:
SELECT u.*,p.* FROM Users u LEFT JOIN
( SELECT userId, max(value) as max_value FROM Purchases GROUP BY userId) p ON u.userId = p.userId
If you want to get the max() pusrchase per user preserving the order like orderid etc you may need to do this
select
u.userId,
u.userName,
p.orderId,
p.value
from Users u
inner join Purchases p on
p.userId = u.userId
inner join
(
select orderId,
max(value) as value,
userId
from Purchases
group by userId
)o
on o.userId = p.userId
AND o.value = p.value
group by u.userId
http://sqlfiddle.com/#!2/1afda/9