LEFT Join query issues with - mysql

I'm having some issues getting the following query to work - in all cases I only seem to get a small subset of users back rather than the entire list.
I have the following two tables:
Users:
- UserId
- email
Updates:
- UserId
- Status
- LastUpdated
What I want to be returned is a list of all users (from Users), their status if it was updated today (based on LastUpdated field) otherwise BLANK or NULL if it wasn't updated today.
I've got as far as this:
SELECT users.userid,
updates.status
FROM users
LEFT JOIN updates
ON users.userid = updates.userid
WHERE Date(lastupdated) = Date(Now())

You can use a CASE statement to only get updates.status if the date is today:
SELECT users.userId,
CASE WHEN DATE(LastUpdated) = DATE(NOW()) THEN updates.status ELSE NULL END AS status
FROM users
LEFT JOIN updates ON users.userId=updates.userId
Or, better yet, if you don't need anything else from the updates row just do:
SELECT users.userId, updates.status
FROM users
LEFT JOIN updates ON users.userId=updates.userId
AND DATE(LastUpdated) = DATE(NOW())

SELECT users.userId, updates.status
FROM users
LEFT JOIN updates
ON updates.userId = users.userId
AND DATE(updates.LastUpdated) = DATE(NOW())
Put your condition within the join, otherwise it's forcing every join to be interrogated. Alternatively, you can check for LastUpdated to be NULL I believe (since the LEFT join will only include located information).

Try this
SELECT users.userId, case when DATE(LastUpdated) = DATE(NOW())
updates.status else null end as status
FROM users
LEFT JOIN updates ON users.userId=updates.userIdd

I think what you are after is the left outer join to return all users and not only the one that have updates today. That should be something like this:
SELECT users.userid, updates.lastupdated
FROM users
LEFT OUTER JOIN (
SELECT * FROM updates WHERE DATE(lastupdated) = DATE(NOW())
) updates ON users.userid = updates.userid

Related

Order by inside the LEFT JOIN

I am trying to write a query. I got it work half way, but I am having problems with the LEFT JOIN.
I have three tables:
user
user_preferences
user_subscription_plan
User will always have one user_preference, but it can have many or no entries in the user_subscription_plan
If the user has no entry in the user_subscription_plan, or if he has only one then my sql works. If I have more then one, then I have issue. In the case of two entries, how can I make it to return the last one entered? I tried playing with ORDER statement, but it does not work as expected. Somehow I get empty rows.
Here is my query:
SELECT u.id AS GYM_USER_ID, subscription_plan.id AS subscriptionId, up.onboarding_completed AS CompletedOnboarding,
(CASE
WHEN ((up.onboarding_completed = 1)
AND (ISNULL(subscription_plan.id)))
THEN 'freemiun'
WHEN (ISNULL(up.onboarding_completed)
AND (ISNULL(subscription_plan.id)))
THEN 'not_paying'
END) AS subscription_status
FROM user AS u
INNER JOIN user_preferences up ON up.user_id = u.id
LEFT JOIN (
SELECT * FROM user_subscription_plan AS usp ORDER BY usp.id DESC LIMIT 1
) AS subscription_plan ON subscription_plan.user_id = u.id
GROUP BY u.id;
If I run it as it is, then subscription_plan.id AS subscriptionId is always empty.
If I remove the LIMIT clause, then its not empty, but I am still getting the first entry, which is wrong in my case
I have more CASE's to cover, but I can't process until I solve this problem.
Please try to use "max(usp.id)" that "group by subscription_plan.user_id" instead of limit 1.
If you limit 1 in the subquery, the subquery's result will always return only 1 record (if the table has data).
So the above query can be rewritten like this.
Sorry, I didn't test, because I don't have data, but please try, hope this can help.
SELECT
u.id AS GYM_USER_ID,
subscription_plan.id AS subscriptionId,
up.onboarding_completed AS CompletedOnboarding,
(CASE
WHEN
((up.onboarding_completed = 1)
AND (ISNULL(subscription_plan.id)))
THEN
'freemiun'
WHEN
(ISNULL(up.onboarding_completed)
AND (ISNULL(subscription_plan.id)))
THEN
'not_paying'
END) AS subscription_status
FROM
user AS u
INNER JOIN
user_preferences up ON up.user_id = u.id
LEFT JOIN
(SELECT
usp.user_id, MAX(usp.id)AS id
FROM
user_subscription_plan AS usp
GROUP BY usp.user_id) AS subscription_plan ON subscription_plan.user_id = u.id;

Left join sql query

I want to get all the data from the users table & the last record associated with him from my connection_history table , it's working only when i don't add at the end of my query
ORDER BY contributions DESC
( When i add it , i have only the record wich come from users and not the last connection_history record)
My question is : how i can get the entires data ordered by contributions DESC
SELECT * FROM users LEFT JOIN connections_history ch ON users.id = ch.guid
AND EXISTS (SELECT 1
FROM connections_history ch1
WHERE ch.guid = ch1.guid
HAVING Max(ch1.date) = ch.date)
The order by should not affect the results that are returned. It only changes the ordering. You are probably getting what you want, just in an unexpected order. For instance, your query interface might be returning a fixed number of rows. Changing the order of the rows could make it look like the result set is different.
I will say that I find = to be more intuitive than EXISTS for this purpose:
SELECT *
FROM users u LEFT JOIN
connections_history ch
ON u.id = ch.guid AND
ch.date = (SELECT Max(ch1.date)
FROM connections_history ch1
WHERE ch.guid = ch1.guid
)
ORDER BY contributions DESC;
The reason is that the = is directly in the ON clause, so it is clear what the relationship between the tables is.
For your casual consideration, a different formatting of the original code. Note in particular the indented AND suggests the clause is part of the LEFT JOIN, which it is.
SELECT * FROM users
LEFT JOIN connections_history ch ON
users.id = ch.guid
AND EXISTS (SELECT 1
FROM connections_history ch1
WHERE ch.guid = ch1.guid
HAVING Max(ch1.date) = ch.date
)
We can use nested queries to first check for max_date for a given user and pass the list of guid to the nested query assuming all the users has at least one record in the connection history table otherwise you could use Left Join instead.
select B.*,X.* from users B JOIN (
select A.* from connection_history A
where A.guid = B.guid and A.date = (
select max(date) from connection_history where guid = B.guid) )X on
X.guid = B.guid
order by B.contributions DESC;

UPDATE TABLE WITH CASE

I have two tables user_master and login_history. In User_master I want to update the status column as A(absent) or P(Present) if user has logged in in current date from login history.the code I am trying but it updates all the rows. All I want is if the user has logged in , it should match both the tables and update user_master status column as P or A. Hope My question is clear. Help would be really appreciated. here is my MySQL query
UPDATE User_master a
INNER JOIN
(
SELECT DISTINCT user_name FROM login_history WHERE DATE(`login_time`)=CURRENT_DATE()
) b
SET a.`user_status` = CASE
WHEN a.`user_name`=B.`user_name` THEN 'P'
WHEN a.`user_name`!=B.`user_name` THEN 'A'
END
Hmmm, I am thinking LEFT JOIN:
UPDATE User_master m
LEFT JOIN Login_History lh
ON m.user_name = lh.user_name AND
DATE(lh.login_time) = CURRENT_DATE()
SET m.user_status = (CASE WHEN lh.user_name IS NULL THEN 'A' ELSE 'P' END);
It occurs to me that there might be more than one login on a given date. The result is additional updates on the same row. You can prevent this by doing:
UPDATE User_master m LEFT JOIN
(SELECT lh.user_name, 'P' as user_status
FROM Login_History lh
WHERE lh.login_time >= CURRENT_DATE() AND
lh.login_tie < DATE_ADD(CURRENT_DATE(), INTERVAL 1 DAY)
GROUP BY lh.user_name
) lh
ON m.user_name = lh.user_name
SET m.user_status = COALESCE(lh.user_status, 'A');
Notice that I changed the date arithmetic as well. This version should make better use of an index.
Might be easier with two queries:
Set everyone absent (update ... set user_status='A')
Set the present people to P with a select set.
Like so:
update user_master set user_status='A';
update user_master set user_status='P'
where user_name in (select distinct user_name from login_history...);
A join is somewhat quicker, but this is a pretty clean, understandable approach.

MySQL query to filter only expired users from table

I'm trying to export af list of subscribers from a database, and while my sql call works, it's missing some logic. My problem is - I want to export users with an expired membership. Some users might have both an expired membership and an active one. I don't want those users on my list.
I'm thinking I need a subquery to filter users with both an expired and an active membership, but I'm not sure how.
Here's my current query:
SELECT
jos_users.name,
jos_users.email,
jos_users.username,
jos_payplans_subscription.expiration_date,
jos_payplans_subscription.status,
jos_payplans_subscription.total,
jos_payplans_subscription.subscription_id
FROM jos_payplans_subscription
LEFT JOIN jos_users ON jos_payplans_subscription.user_id = jos_users.id
LEFT JOIN jos_payplans_user ON jos_payplans_user.user_id = jos_users.id
WHERE (jos_payplans_subscription.expiration_date BETWEEN '2015-09-01 00:00:00' AND '2015-10-31 00:00:00')
I'll appreciate any help.
Thanks :-)
You could join back to the subscriptions table to find the active subs, and ignore those:
select q.name, q.email, q.username, q.expiration_date, q.status, q.total, q.subscription_id
from (
SELECT
jos_users.name,
jos_users.email,
jos_users.username,
jos_payplans_subscription.expiration_date,
jos_payplans_subscription.status,
jos_payplans_subscription.total,
jos_payplans_subscription.subscription_id
FROM jos_payplans_subscription
LEFT JOIN jos_users ON jos_payplans_subscription.user_id = jos_users.id
LEFT JOIN jos_payplans_user ON jos_payplans_user.user_id = jos_users.id
WHERE (jos_payplans_subscription.expiration_date BETWEEN '2015-09-01 00:00:00' AND '2015-10-31 00:00:00')
) q
join jos_payplans_subscription s on s.user_id = q.user_id
group by q.name, q.email, q.username, q.expiration_date, q.status, q.total, q.subscription_id
having min(s.expiration_date) is not null;
Edit: Forgot the is
having min(s.expiration_date) not null
to
having min(s.expiration_date) is not null
A similar thing I've done before was create a junction table between a users and membership table called "activeMembership." This had a composite PK of userID and membershipID and a column called "membershipSatus"
Then you could easily do:
SELECT username, email, membershipType, membershipStatus FROM
activeMembership
JOIN users on users.userID = activeMembership.userID
JOIN membership ON membership.membershipID = activeMembership.membershipID
WHERE membershipStatus='Inactive';
Maybe altering the schema and creating a many to many relationship would solve your problem?

Display results between dates sql

I am wanting to display results where the date stored in the table is not between the dates specified in the query.
Here is the current SQL query
SELECT accounts_cstm.statusdescription_c,
users.user_name,
accounts.name,
accounts_cstm.account_number_c,
DATE_FORMAT(MAX(calls.date_modified),'%Y/%m/%d')
FROM accounts
LEFT OUTER JOIN calls ON accounts.id = calls.parent_id
LEFT OUTER JOIN users ON accounts.assigned_user_id = users.id
LEFT OUTER JOIN accounts_cstm ON accounts.id = accounts_cstm.id_c
AND accounts.deleted <> 1
WHERE
(SELECT DATE_FORMAT(MAX(calls.date_modified),'%Y/%m/%d')
FROM calls) NOT BETWEEN '2014/06/25' AND '2014/07/02'
AND users.user_name = 'CBennet'
AND accounts_cstm.chkcustomer_c = '1'
GROUP BY accounts.name
I get a full list of results but I get results that shouldn't appear ie results with calls.date_modified that is in between the dates specified.
See below for an example of a wrong result, you can see that the date to the right is in between the dates 2014/06/25 and 2014/07/02 therefore this shouldn't appear.
Can someone let me know what i'm doing wrong here?
Within the table calls, date_modified is stored in the following format 2014-06-10 10:55:47
try this
SELECT *
FROM `test`
WHERE (date NOT BETWEEN '2012-01-30 14:15:55' AND '2014-09-29 10:15:55')
I created test table with some test values at sqlfiddle and got desired output http://sqlfiddle.com/#!2/5ffb7/4