mySQL slow counting primary keys with subquery - mysql

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

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.

How can I get customer data based on the number of users they have?

I want to get customer data from all the businesses with more than 1 user.
For this I think I need a subquery to count more than 1 user and then the outer query to give me their emails.
I have tried subqueries in the WHERE and HAVING clause
SELECT u.mail
FROM users u
WHERE count IN (
SELECT count (u.id_business)
FROM businesses b
INNER JOIN users u ON b.id = u.id_business
GROUP BY b.id, u.id_business
HAVING COUNT (u.id_business) >= 2
)
I believe that you do not need a subquery, everything can be achieved in a joined aggregate query with a HAVING clause, like :
SELECT u.mail
FROM users u
INNER JOIN businesses b on b.id = u.id_business
GROUP BY u.id, u.email
HAVING COUNT (*) >= 2
NB : in case several users may have the same email, I have added the primary key of users to the GROUP BY clause (I assumed that the pk is called id) : you may remove this if email is a unique field in users.

Easy SQL Query - Aligning foreign keys

I'm taking longer than I expected with an easy Query in MySQL. I think it's gonna be a nested query but I don't see it easily.
I have 3 tables: Users, Comments, and Businesses. Comments have business_id, and user_id as foreign keys.
So I want the result of users.name and comments.review, having the number of the business.
So my First (and wrong) attempt was:
SELECT users.name, users.image, comments.review
FROM reviews JOIN users JOIN businesses
WHERE reviews.user_id=users.id AND reviews.business_id=4;
I want to set that PrimaryKey.user_id is equal to ForeignKey.users.id.
From all of the comments, I want to take these which are from the business_id=4.
It gives me failiure with both 'WHERE' clauses. So not sure if I could fix this with a nested query or maybe with a JOIN clause?
Any help will be appreciated!
Thank you all. [Edited Query]
Try this out and let me know in case of any queries.
select c.name,c.image,a.review
from
comments a
inner join
(select * from buisnesses where buisness_id = 4) b
on a.buisness_id = b.buisness_id
inner join
users c
on a.user_id = c.user_id;
or
select b.name,b.image,a.review
from
comments a
inner join
users b
on a.user_id = b.user_id
where a.buisness_id = 4;
Give this a try:
SELECT
users.name,
users.image,
comments.review
FROM reviews
JOIN users
ON reviews.user_id = users.id
JOIN businesses
ON reviews.business_id = business.business_id
WHERE reviews.business_id=4;
Since you're not using any of the columns from the business table, you could probably drop it from the query:
SELECT
users.name,
users.image,
comments.review
FROM reviews
JOIN users
ON reviews.user_id = users.id
WHERE reviews.business_id=4;

get last record of one to many relation

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

Single MySQL query which checks another table for rows

I have a Users table and a Payments table. I need a query which lists the users who DO NOT have a record in the payments table where the field PaymentCompleted=1.
These are the columns in the tables (simplified):
Users: UserID, UserName
Payments: PaymentID, UserID, PaymentCompleted
The query should select the field UserName.
select distinct UserName
from Users left outer join Payments on Users.UserID = Payments.UserID
where PaymentCompleted is NULL or PaymentCompleted != 1
SELECT UserName
FROM Users u
WHERE NOT EXISTS(Select 1
from Payments p
Where p.UserId = u.UserId
AND p.PaymentCompleted = 1)
select * from t_users T where T.userid not exists (select p.userid from t_payments t where PaymentCompleted=1).
One note: "not in" clauses can be computationally inefficient for large numbers of records. If you start seeing performance issues, you may want to do some refactoring/redesign.