get last record of one to many relation - mysql

I have two tables
users : id,name
health : id,status,test_date,user_id
health table containing user health history
now i want to get the last health test and user info of a specific user
I tried this query
SELECT users.*, health.* FROM users INNER JOIN health ON users.id=health.user_id
having (max(health.id)) order by users.id desc limit 50
but i failed

Try this:
SELECT users.*, health.* FROM users
INNER JOIN health
ON health.id = (SELECT id FROM health WHERE health.id = users.id ORDER BY id DESC LIMIT 1)

This is an other option:
SELECT U.*, H.*
FROM users AS U
INNER JOIN (
SELECT user_id, MAX(id) AS id
FROM health
GROUP BY user_id
) AS D
ON D.user_id = U.user_id
INNER JOIN health AS H
ON H.id = D.id
NOTE: "D" will result in a DERIVED table and therefore in a faster execution in MySQL 5.7+ or MariaDB 10+.
Reference:
Optimizing Derived Tables and View References
MySQL 5.7: Improved Performance of Queries with Derived Tables

Related

Combing a select statement with a count on a different table

I have two tables: users and lessons.
Currently I select all the users using:
SELECT * FROM users WHERE user_type = 1;
Then use PHP to loop through them and count their number of lessons using
SELECT COUNT(*) FROM lessons WHERE student_id=users.user_id;
I would like to combine this into a single query and I'm pretty sure this is possible with a JOIN but it is beyond my basic SQL knowledge.
Do this all in one query. If you want a count per user:
select u.user_id, count(l.student_id)
from users u left join
lessons l
on u.user_id = l.student_id
where u.user_type = 1
group by u.user_id
You can use a join, or a correlated subquery:
select
u.*,
(select count(*) from lessons l where l.student_id = u.user_id) no_lessons
from users u
The upside of the subquery solution is that it does not require aggregation in the outer query. With an index on lessons(student_id), this should be an efficient option.
You can write the following query for this:
SELECT * FROM users u LEFT JOIN lessons l ON u.user_id=l.student_id WHERE u.user_type=1 GROUP BY u.user_id
This will join both the tables (users and lessons) based on the id of both tables and the GROUP BY clause will group all the records of same id as you just want the number of lessons per user.

I want to improve my search performance mysql

I want to improve my search performance. I want to search the user against the services and specialty. user in listed as per the filter applied either by specialty or by services or can be both.There are 5 tables i want to get the data from all the table
I have below table structure
1) users :- id ,first_name,last_name
2) users_services :- id ,user_id,service_id
3) users_speciality :- id,user_id,specility_id
4) mst_services :- id,name
5) mst_speciality :- id,name
I have used this query to get the result and it works fine.
select u.id,first_name,last_name,location,services.name as service_name,speciality.name as specility_name from users as u
inner join users_services on u.id =users_services.user_id
Inner join mst_services as services on services.id=users_services.service_id
inner join users_speciality on u.id =users_speciality.user_id
Inner join mst_speciality as speciality on speciality.id=users_speciality.service_id WHERE speciality.name ="specificity one"
as per the normalization it seems correct.But when data is more than 1,00,000 that time joining too many table causes may create problem.
what should i do for filter the user according to services and specialty ?
Have you tried like this ?
SELECT users.id,first_name,last_name, mst_services.name as service, mst_speciality.name AS speciality
FROM users
LEFT JOIN users_services ON users.id = users_services.user_id
LEFT JOIN users_speciality ON users.id = users_speciality.user_id
LEFT JOIN mst_services ON service_id = mst_services.id
LEFT JOIN mst_speciality ON speciality_id = mst_speciality.id
WHERE users.id IN
(SELECT user_id FROM users_services
WHERE service_id = (SELECT id FROM mst_services WHERE name = "service one")
UNION ALL
SELECT user_id FROM users_speciality
WHERE speciality_id = (SELECT id FROM mst_speciality WHERE name = "spercial one"))
ORDER BY first_name,last_name,service,speciality

Double JOIN to get two different parameters?

Consider 2 tables: USERS and TASKS for a task delivery app
USERS
userID | email | name
TASKS
userID | designator_userUNIQUE | destination_userUNIQUE
To get the destination_user full name from the users table I am using the following query that works fine:
SELECT *
FROM tasks
INNER JOIN users ON users.userID = tasks.destination_userUNIQUE
WHERE tasks.client='$clientUNIQUE'
ORDER BY tasks.date DESC, tasks.time DESC
However, if I want to get the full name from the designator_userUNIQUE using the below query, it simply generates an sql error (mysql_numrows() expects parameter 1 to be resource).
SELECT *
FROM tasks
INNER JOIN users ON users.userID = tasks.destination_userUNIQUE
INNER JOIN users ON users.name = tasks.designator_user UNIQUE
WHERE tasks.client='$clientUNIQUE'
ORDER BY tasks.date DESC, tasks.time DESC
Any ideas?
You need to give different aliases to each instance of the users table that you join with, to prevent ambiguity.
SELECT *
FROM tasks AS t
INNER JOIN users AS dest ON dest.userID = t.destination_userUNIQUE
INNER JOIN users AS desig ON desig.userID = t.designator_userUNIQUE
WHERE t.client='$clientUNIQUE'
ORDER BY t.date DESC, t.time DESC

MySQL order by with count and group by issues

My Tables look like:
# Table user
user_id PK
...
# Table buy
buy_id PK
user_id FK
...
# Table offert
offert_id
user_id
...
Well i need to know the last 'buy' of 1 'user' and get the count of 'offert' this 'user' has, I tried something like:
select b.buy_id,count(distinct c.offert_id) as cv from user a
inner join buy b using(user_id) left join offert c using(user_id) where a.user_id=4
group by a.user_id order by b.buy_id desc
but it always returns the first 'buy' not the last, look like this order by doesn't make any effect
I know that i can do it with sub queries but i would like know if is there a way to do it whout use sub queries, maybe using max functions but idk how to do it.
thanks.
Your approach is simply not guaranteed to work. One big reason is that the group by is processed before the order by.
Assuming that you mean the biggest buy_id for each user, you can do this as:
select u.user_id, u.last_buy_id, count(distinct o.offert_id)
from (select u.*,
(select buy_id from buy b where u.user_id = u.user_id order by buy_id desc limit 1
) last_buy_id
from user u
) left outer join
offert o
on o.user_id = u.user_id
group by u.user_id;
The first subquery uses a correlated subquery to get the last buy id for each user. It then joins in offert and does the aggregation. Note that this version includes the user_id in the aggregation.

mySQL slow counting primary keys with subquery

The following query is taking 5.5 seconds. Since this is a simple primary key count and both tables have <5000 records, I'm quite surprised at how slow it is. Is there any workaround to increase performance?
SELECT COUNT(*)
FROM users
WHERE (SELECT COUNT(*)
FROM clients
WHERE userID=users.id)=0
I'm counting the number of users who have no clients.
Try this query
SELECT
COUNT(*)
FROM
users
WHERE NOT EXISTS
(SELECT
userID
FROM
clients
WHERE
userID=users.id)
Alternatively you can try this
SELECT
count(*)
FROM
users u
LEFT JOIN
clients c
ON
u.id = c.userId
WHERE
c.userId IS null
Create index on id columns
Hope this helps
Try this:
SELECT COUNT(*) FROM users u
left join clients c
on u.id = c.userID
where u.id not in (select userID from clients)
DEMO HERE