Search on specific users in the table - mysql

I have following query:
SELECT *
FROM users
WHERE id NOT
IN (
SELECT user_id
FROM `bids`
WHERE DATE_SUB( DATE_ADD( CURDATE( ) , INTERVAL 7
DAY ) , INTERVAL 14
DAY ) <= created
)
AND id NOT
IN (
SELECT user_id
FROM coupon_used WHERE code = 'ACTNOW'
)
AND id
IN (
SELECT user_id
FROM accounts
)
I just want to take specific users and search on them, instead of searching on all users in the table. Like I have the list of users with id 1,2,3,4,5 I only want to search on these users

Just add a WHERE clause using IN()
SELECT *
FROM users
WHERE id IN(1,2,3,4,5)

I believe using left outer joins will simplify your query and hopefully improve performance
SELECT users.*
FROM users
LEFT OUTER JOIN bids on bids.user_id = users.id AND DATE_SUB(DATE_ADD(CURDATE(), INTERVAL 7 DAY), INTERVAL 14 DAY) <= bids.created
LEFT OUTER JOIN coupon_used on coupon_used.user_id = users.id AND coupon_used.code = 'ACTNOW'
INNER JOIN accounts on accounts.user_id = users.id
WHERE bids.id is null AND coupon_used.id is null
AND users.id in (1,2,3,4,5)

Related

How to use "group by" before "join" on mysql?

I have the following query which contains duplicate user_id's. I don't want to see a user more than one. I decided to use group by as in the following:
SELECT u.`ID`, u.`user_login`, u.`user_registered`, u.`display_name`
FROM purchase_key p
LEFT JOIN users u ON p.user_id = u.id
WHERE ( `product_id` = 1 OR `product_id` = 2 )
AND `create_date` <= '2015-09-20' AND `create_date` >= '2014-09-01'
group by p.user_id order by p.`create_date` asc
But is there a way to do that grouping before join ?
Not sure why you want to do this. Unlikely that it will help with performance unless the indexes are very poor.
Not sure the LEFT OUTER JOIN is useful, as the only fields your query returns are ones from the left joined table. Hence if no user were found for some purchases you would just have a load of null rows returned.
But doing the sub query to get the list of users, with the max create date for that user within the required date range (while not strictly necessary for MySQL most flavours of SQL would give an error if you return fields that are not in the group by clause and not aggregate fields), and using an INNER JOIN gives you the following:-
SELECT u.ID, u.user_login, u.user_registered, u.display_name
FROM
(
SELECT user_id, MAX(create_date) AS max_create_date
FROM purchase_key
WHERE product_id IN (1, 2)
AND create_date BETWEEN '2014-09-01' AND '2015-09-20'
GROUP BY user_id
) p
INNER JOIN users u ON p.user_id = u.id
ORDER BY p.max_create_date ASC
SELECT u.`ID`, u.`user_login`, u.`user_registered`, u.`display_name`
FROM (SELECT * FROM purchase_key GROUP BY user_id ) p
LEFT JOIN users u ON p.user_id = u.id
WHERE ( `product_id` = 1 OR `product_id` = 2 )
AND `create_date` <= '2015-09-20' AND `create_date` >= '2014-09-01'
order by p.`create_date` asc
Hope this helps
If you don't want to see user more than once, try to use DISTINCT in your first line.

How to count the number of rows from left join with conditioning

This is my query:
SELECT claims.id,
COUNT(CASE WHEN claims.sold_at BETWEEN (now() - INTERVAL '7.day') AND now() END FROM claims) week1
FROM users
LEFT JOIN claims ON claims.sales_user_id = users.id
WHERE users.office_id = 2
What I am trying to do is join the claims table but then also get a count of how many were sold_at within a certain date period
This query is giving an error but not sure how to fix/approach it properly?
Try this:
select
users.id, count(claims.id) as count
from users
left join claims on claims.sales_user_id = users.id
where
users.office_id = 2 and
claims.sold_at between (current_timestamp - interval '7' day) and current_timestamp
group by users.id;
Your query should look like this
SELECT claims.id,
COUNT(CASE
WHEN claims.sold_at BETWEEN (now() - INTERVAL '7.day') AND now()
THEN claims.id
ELSE null END) week1
FROM users
LEFT JOIN claims ON claims.sales_user_id = users.id
WHERE users.office_id = 2
Normally for this kind of thing you use SUM(IF()).
Something like this:-
SELECT claims.id,
SUM(IF(claims.sold_at BETWEEN DATE_SUB(now(), INTERVAL 7 DAY) AND now(), 1, 0)) AS week1
FROM users
LEFT JOIN claims ON claims.sales_user_id = users.id
WHERE users.office_id = 2
But this on its own this makes no sense. Why is claims.id being brought back? The aggregate function without a GROUP BY clause will mean only a single row is returned, so the claims.id returned will be random. But I expect the id is the unique key so grouping by it seems wrong in this case.
select
users.id,
count(
claims.sold_at between (now() - interval '7.day') and now()
or null
) week1
from
users
left join
claims on claims.sales_user_id = users.id
where users.office_id = 2
group by users.id

Mysql : get count and percent for all entries of a table with given conditions

Let's say we have the following model :
A table of exercises (id, name)
A table of users (id, name, email)
A tables of exams (id, user_id, day)
A join table between exams and exercises (exam_id, exercise_id)
Each user can make one exam each day.
Each exam is made of many exercises (has and belongs to many).
For each user (when logged), I would like to display a table displaying all the exercises and for each the count and percent of exams the user made during a given period of time (using the day attribute of the exams).
At this time I can display all the exercises the user made and their percent during a given period with the following query.
SELECT
o.user_id,
o.exercise_id,
o.exercise_name,
COUNT(*) AS nb_exams,
(COUNT(*) * 100 / (
SELECT COUNT(*)
FROM exams
LEFT JOIN users ON exams.user_id = users.id
WHERE users.id = 1 AND exams.day >= "2015-09-01" AND exams.day <= "2015-09-07"
)) AS percent
FROM (
SELECT
exercises.id AS exercise_id,
exercises.name AS exercise_name,
exams.id AS exam_id,
users.id AS user_id
FROM exercises
LEFT JOIN exercises_exams ON exercises_exams.exercise_id = exercises.id
LEFT JOIN exams ON exercises_exams.exam_id = exams.id
LEFT JOIN users ON exams.user_id = users.id
WHERE users.id = 1 AND exams.day >= "2015-09-01" AND exams.day <= "2015-09-07"
GROUP BY exercise_id,exam_id
) AS o
GROUP BY exercise_id;
But I also want to display the exercises he did not make yet with a value of 0.
Is it possible to do this with one query of mysql ?
EDIT-
Here is the sqlfiddle for the current query. You will notice that only the 3 exercises which are used in exams are returned. I would like to list all the exercises with the count and percent (even the fourth with 0)
Firstly, you should remove the where condition to the join clause in the inner query to see a 0 result when an exercise wasn't performed. Getting a user_id corresponding to the 0 result doesn't make sense. This is the closest solution i can think of.
Fiddle
SELECT
o.user_id,
o.exercise_id,
o.exercise_name,
sum(case when exam_id is null then 0 else 1 end) AS nb_exams,
(sum(case when exam_id is null then 0 else 1 end) * 100 / (
SELECT COUNT(*)
FROM exams
LEFT JOIN users ON exams.user_id = users.id
where exams.day >= "2015-09-01" AND exams.day <= "2015-09-07"
)) AS percent
FROM (
SELECT
exercises.id AS exercise_id,
exercises.name AS exercise_name,
exams.id AS exam_id ,
users.id AS user_id
FROM exercises
LEFT JOIN exercises_exams ON exercises_exams.exercise_id = exercises.id
LEFT JOIN exams ON exercises_exams.exam_id = exams.id
and exams.day >= "2015-09-01" AND exams.day <= "2015-09-07"
LEFT JOIN users ON exams.user_id = users.id
) AS o
GROUP BY exercise_id,exercise_name

Using mysql GROUP_CONCAT and WHERE

I have the following tables in my DB
EVENT
ID, TITLE, ...
VOTES
ID, TYPE, ID_EVENT
COMMENTS
ID, COMMENT, ID_EVENT
DATES
ID, DATE, ID_EVENT
One EVENT has many actions, has many comments and has many dates.
I'm using following query to retrieve info from EVENTS table, and for each event retrieve the number of votes, the number of comments and each one of the dates. For events with one of their date = tomorrow (2015-04-03)
SELECT events.id,
events.title,
GROUP_CONCAT(dates.date) AS dates,
COUNT(distinct votes.id) AS votes,
COUNT(distinct comments.id) AS comments
FROM events
LEFT JOIN dates on dates.post_id = events.id
LEFT JOIN votes on votes.post_id = events.id AND votes.type = 1
LEFT JOIN comments on comments.votes_id = votes.id
WHERE dates.date = CURDATE() + INTERVAL 1 DAY
GROUP BY events.id
Result looks like this
id title dates votes comment
33 Event33 2015-04-03,2015-04-03,2015-04-03 4 0
39 Event39 2015-04-03 9 1
Why the dates column repeats the same date (tomorrow)??? The dates of Event33 should be 2015-04-01, 2015-04-02, 2015-04-03.
What is wrong?
You want the events that have one of their dates tomorrow. Your query does that but it also cuts off all other dates.
You need an extra join or an EXISTS subquery.
You also need a DISTINCT on the GROUP_CONCAT(), the same way you used it at the COUNT() aggregate:
SELECT events.id,
events.title,
GROUP_CONCAT(DISTINCT dates.date) AS dates,
COUNT(DISTINCT votes.id) AS votes,
COUNT(DISTINCT comments.id) AS comments
FROM events
LEFT JOIN dates ON dates.post_id = events.id
LEFT JOIN votes ON votes.post_id = events.id AND votes.type = 1
LEFT JOIN comments ON comments.votes_id = votes.id
WHERE EXISTS
( SELECT 1
FROM dates AS dd
WHERE dd.date = CURDATE() + INTERVAL 1 DAY
AND dd.post_id = events.id
)
GROUP BY events.id ;
Another way would be using inline subqueries. No need for GROUP BY or DISTINCT in this. A minor disadvantage in your case, is that the join to comments is through votes, so one subquery has an extra join:
SELECT e.id,
e.title,
( SELECT GROUP_CONCAT(d.date)
FROM dates AS d
WHERE d.post_id = e.id
) AS dates,
( SELECT COUNT(v.id)
FROM votes AS v
WHERE v.post_id = e.id AND v.type = 1
) AS votes,
( SELECT COUNT(c.id)
FROM comments AS c
JOIN votes AS v ON c.votes_id = v.id
WHERE v.post_id = e.id AND v.type = 1
) AS comments
FROM events AS e
WHERE EXISTS
( SELECT 1
FROM dates AS dd
WHERE dd.date = CURDATE() + INTERVAL 1 DAY
AND dd.post_id = events.id
) ;

How to correct and rewrite this query?

SELECT *
FROM users
WHERE id
IN ( 2024 )
AND id NOT IN (
SELECT user_id
FROM `used`
WHERE DATE_SUB( DATE_ADD( CURDATE( ) , INTERVAL 7 DAY ) , INTERVAL 14 DAY ) <= created)
AND id NOT IN (
SELECT user_id
FROM coupon_used
WHERE code = 'XXXXX')
AND id IN (
SELECT user_id
FROM accounts)
I have id 2024 in users table, but this id 2024 is there in used tables. So when I run this query, it shows me 2024 id also, which should be filtered out. I run the query where I selected specific users, and then I want these user to be filter out that they should not be in used table. But above query is not giving me the desire result. Desire Result is that I want to Select Users by following conditions: Take Specific Users, and check that they are not in used table and not in coupon_used table but they should be in accounts table.
I would use left joins for the exclusion conditions and a regular join for the inclusions:
SELECT users.*
FROM users
INNER JOIN accounts ON accounts.user_id = users.id
LEFT JOIN used ON used.user_id = users.id AND DATE_SUB(CURDATE(), INTERVAL 7 DAY) <= used.created)
LEFT JOIN coupon_used ON coupon_used.user_id = users.id AND coupon_used.code = 'XXXX'
WHERE id IN (2024) AND used.user_id IS NULL AND coupon_used.user_id IS NULL
I've edited the date manipulation as well; +7 -14 would be -7 :)
I would recommend using a JOIN on accounts and LEFT OUTER JOINs on the other two tables. A JOIN on accounts means it must be in the accounts table. LEFT OUTER JOINS on the coupon_used and used means it will return a record no matter if they're in that table or not. Filtering down to c.user_id IS NULL means that there is NOT a record in that table.
SELECT users.*
FROM users
JOIN accounts ON users.id = accounts.user_id
LEFT OUTER JOIN coupon_used c ON users.id = c.user_id AND c.code = 'XXXXX'
LEFT OUTER JOIN `used` u ON users.id = u.user_id AND DATE_SUB( DATE_ADD( CURDATE( ) , INTERVAL 7 DAY ) , INTERVAL 14 DAY ) <= u.created
WHERE id IN ( 2024 )
AND c.user_id IS NULL
AND u.user_id IS NULL
Firstly, try something like this using joins. Which should be easier to read and (depending on the version of MySQL) faster
SELECT DISTINCT users.*
FROM users
INNER JOIN accounts ON users.id = accounts.user_id
LEFT OUTER JOIN coupon_used ON users.id = coupon_used.user_id AND coupon_used.code = 'XXXXX'
LEFT OUTER JOIN `used` ON users.id = `used`.user_id AND DATE_SUB( DATE_ADD( CURDATE( ) , INTERVAL 7 DAY ) , INTERVAL 14 DAY ) <= `used`.created
WHERE id IN ( 2024 )
AND coupon_used.user_id IS NULL
AND `used`.user_id IS NULL
EDIT - Simplifying the date check:-
SELECT DISTINCT users.*
FROM users
INNER JOIN accounts ON users.id = accounts.user_id
LEFT OUTER JOIN coupon_used ON users.id = coupon_used.user_id AND coupon_used.code = 'XXXXX'
LEFT OUTER JOIN `used` ON users.id = `used`.user_id AND DATE_SUB( CURDATE( ) , INTERVAL 7 DAY ) <= `used`.created
WHERE id IN ( 2024 )
AND coupon_used.user_id IS NULL
AND `used`.user_id IS NULL