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.
Related
I've successfully created a SQL query that finds duplicates in my table like this:
SELECT email, COUNT(*) c FROM subscribers GROUP BY email HAVING c > 1 ;
This table also has columns "unsubscribed" and "bounced" and "complaint".
The default is '0' for these columns with a '1' when a users has opted out.
If one of the duplicates found in my SELECT has a '1' in any of these columns, I need to update the other duplicate records with a '1' for that column, in that record.
You could do something like this with a self-join:
select *
from subscribers s
inner join subscribers i
on s.email = i.email
and s.id <> i.id
where 1 in (s.unsubscribed,s.bounced,s.complaint)
and (
i.unsubscribed<>s.unsubscribed
or i.bounced<>s.bounced
or i.complaint<>s.complaint
)
For the update I would probably just do this to cover all cases where 1 record might be unsubscribed and another record for the same email might be a complaint, etc:
update subscribers s
inner join (
select
email
, max(unsubscribed) as unsubscribed
, max(bounced) as bounced
, max(complaint) as complaint
from subscribers as i
group by email
having count(*)>1
) as a
on a.email = s.email
set s.unsubscribed = a.unsubscribed
, s.bounced = a.bounced
, s.complaint = a.complaint;
rextester demo: http://rextester.com/RGOG61470
You could change the having to:
having count(*)>1
and (min(unsubscribed)<>max(unsubscribed)
or min(bounced) <>max(bounced)
or min(complaint) <>max(complaint)
)
To further restrict the update to only those that have different values for at least one of those three columns.
We're going through a bit of a clean-up exercise and I need to remove duplicate data that has accidentally been added to our database table. The ID is obviously different, but other fields are the same.
I can use the following query to select the duplicate data sets:
SELECT user_id, start_datetime, count(id) AS dup_count
FROM our_table
WHERE status = 1
GROUP BY user_id, start_datetime
HAVING count(id) > 1;
What I need to do is create a query that would take each of the duplicate IDs APART FROM THE FIRST and use that to update the status to 0.
I'm not sure I can do this is one query, but I think the steps are as follows:
Run a query similar to the one above
Extract all the IDs for the duplicate sets
Ignore the first in the list as we don't want to alter the correctly added first record
Run the update on the remaining set of IDs
Am I out of luck here - or is it possible to do?
Many thanks!
You can do this with an update/join:
UPDATE our_table ot JOIN
(SELECT user_id, start_datetime, count(id) AS dup_count, min(id) as minid
FROM our_table
WHERE status = 1
GROUP BY user_id, start_datetime
HAVING count(id) > 1
) dups
ON ot.user_id = dups.user_id and
ot.start_datetime = dups.start_datetime and
ot.id > dups.minid
SET ot.status = 0;
You can use this update query that will join OUR_TABLE with itself:
UPDATE
our_table o1 INNER JOIN our_table o2
ON o1.status=1
AND o2.status=1
AND o1.user_id = o2.user_id
AND o1.start_datetime = o2.start_datetime
AND o1.ID > o2.ID
SET
o1.status = 0
Please see an example fiddle here.
How do you insert this query with an update? I've tried all possible combinations and nothing seems to work!
UPDATE Test_table2
SET Pledge_Status = closed
WHERE (
SELECT SUM(pledgers.Pledge_payment_amt) AS pledged
FROM Test_table2 AS recipients
LEFT JOIN Test_table2 AS pledgers
ON recipients.GIFT_NO = pledgers.PLEDGE_GIFT_NO
GROUP BY recipients.GIFT_NO
HAVING recipients.Pledge_Amt >= pledged
ORDER BY recipients.CRSID ASC
);
Schema (all varchar):
ID, Name, Pledge_Amount , Pledge_payment_amt , Gift_No, Pledge_Gift_No, Pledge_Open/Closed
Thank you so very much!
Parallel to this question I suggest that you use WHERE ... IN SELECT. Also, you need to pass the ID of each row from the subselect to the main query, and since you didn't provide a table schema, we can only guess here:
UPDATE Test_table2
SET Pledge_Status = closed
WHERE [your id column] IN (
SELECT [your id column]
FROM Test_table2 AS recipients
LEFT JOIN Test_table2 AS pledgers
ON recipients.GIFT_NO = pledgers.PLEDGE_GIFT_NO
GROUP BY recipients.GIFT_NO
HAVING recipients.Pledge_Amt >= SUM(pledgers.Pledge_payment_amt)
/*ORDER BY recipients.CRSID ASC <-- no need to order anything here */
);
What I want to do is to set every patient its unique patient code which starts with 1 and it's not based on row id. Id only specifies order. Something like this:
patient_id patient_code
2 1
3 2
4 3
This is my query:
UPDATE patients p1
SET p1.patient_code = (
SELECT COUNT( * )
FROM patients p2
WHERE p2.patient_id <= p1.patient_id
)
But it is throwing error:
#1093 - You can't specify target table 'p1' for update in FROM clause
I found this thread: Mysql error 1093 - Can't specify target table for update in FROM clause.But I don't know how to apply approved answer this to work with subquery WHERE which is necessary for COUNT.
UPDATE
patients AS p
JOIN
( SELECT
p1.patient_id
, COUNT(*) AS cnt
FROM
patients AS p1
JOIN
patients AS p2
ON p2.patient_id <= p1.patient_id
GROUP BY
p1.patient_id
) AS g
ON g.patient_id = p.patient_id
SET
p.patient_code = g.cnt ;
I found working solution, but this is just workaround:
SET #code=0;
UPDATE patients SET patient_code = (SELECT #code:=#code+1 AS code)
Try this,
UPDATE patients p1 INNER JOIN
(
SELECT COUNT(*) as count,patient_id
FROM patients
group by patient_id
)p2
SET p1.patient_code=p2.count
WHERE p2.patient_id <= p1.patient_id
SQL_LIVE_DEMO
Thanks to Mari's answer I found a solution to my similar problem. But I wanted to add a bit of an explanation which for me at first wasn't too clear from his answer.
What I wanted to do would have been as simple as the following:
UPDATE my_comments AS c
SET c.comment_responses = (
SELECT COUNT(c1.*) FROM my_comments AS c1
WHERE c.uid = c.parent_uid
);
Thanks to Mari I then found the solution on how to achieve this without running into the error You can't specify target table 'p1' for update in FROM clause:
UPDATE my_comments AS c
INNER JOIN (
SELECT c1.parent_uid, COUNT(*) AS cnt
FROM my_comments AS c1
WHERE c1.parent_uid <> 0
GROUP BY c1.parent_uid
) AS c2
SET c.comment_responses = c2.cnt
WHERE c2.parent_uid = c.uid;
My problems before getting to this solution were 2:
the parent_uid field doesn't always contain an id of a parent which is why I added the WHERE statement in the inner join
I didn't quite understand why I would need the GROUP BY until I executed the SELECT statement on it's own and the answer is: because COUNT groups the result and really counts everything. In order to prevent this behavior the GROUP BY is needed. In my case I didn't have to group it by uid though but the parent_uid to get the correct count. If I grouped it by uid the COUNT would always be 1 but the parent_uid existed multiple times in the result. I suggest you check the SELECT statement on it's own to check if it's the result you expect before you execute the full UPDATE statement.
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