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.
Related
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.
So, I've got two tables: users and tasks;
users:
user_id username password first_name last_name isAdmin
tasks:
task_id name description assessment assigned_user_id fk creator_id fk created_on last_modified status
What I want to do is replace assigned_user_id and creator_id with first_name + last_name from users table. So I execute the following query:
SELECT `task_id`, `description`,`assessment`,
(SELECT CONCAT(`first_name`, ' ',`last_name`) WHERE `Tasks`.`assigned_user_id` = `Users`.`user_id`) AS assigned_user,
(SELECT CONCAT(`first_name`, ' ',`last_name`) WHERE `Tasks`.`creator_id`=`Users`.`user_id`) AS creator_user,
`created_on`,`last_modified`,`status`
FROM `Tasks`
LEFT JOIN `Users`
ON Tasks.assigned_user_id = Users.user_id
OR Tasks.creator_id = Users.user_id
WHERE task_id=2
The problem is that it returns 2 rows. One is with assigned_user_id null and filled creator_id and the other is the other way around:
task_id description assessment assigned_user creator_user created_on last_modified status
2 SHA SA PII LI 24 NULL Petyo Chuliuv 2016-07-22 2016-07-22 1
2 SHA SA PII LI 24 Gosho Toshov NULL 2016-07-22 2016-07-22 1
Question is: How to return a single row with assigned_user and creator_user filled and where I did wrong? Thanks in advance!
I tested this on SQL Server and reproduced the same issue as you so hopefully I can be of help.
When I did the test the two SELECT CONCAT statements were using the same user_id both times. So the issue seems that it is not checking for both ids at once but both ids at separate times. So if I were to use your example it first uses Petyo's id in both of the SELECT CONCAT statements (only filling the creator_user role so the other one becomes false) and then it uses Gosho's id in both of the SELECT CONCAT statements which also only fills one field (the assigned_user field) and making the other one null.
So what you need to do is JOIN the Users table again. One for the assigned, one for the create. Something like this...
SELECT `task_id`, `description`,`assessment`,
(SELECT CONCAT(`U1.first_name`, ' ',`U1.last_name`)) AS assigned_user,
(SELECT CONCAT(`U2.first_name`, ' ',`U2.last_name`)) AS creator_user,
`created_on`,`last_modified`,`status`
FROM `Tasks`
LEFT JOIN `Users` U1
ON Tasks.assigned_user_id = U1.user_id
LEFT JOIN `Users` U2
ON Tasks.creator_id = U2.user_id
WHERE task_id=2
Before you had an OR. It does not look at one side, look for the id, then look at the other one, look for the id, then use it at once. It is exactly that. If the current user_id it is looking for happens to be one of those two then it uses that single user_id.
You need to join to your users table twice and alias them...
Somethin like...
SELECT `task_id`, `description`,`assessment`,
(SELECT CONCAT(`assignedUsers.first_name`, ' ',`assignedUsers.last_name`) AS assigned_user,
(SELECT CONCAT(`createdUsers.first_name`, ' ',`createdUsers.last_name`) AS creator_user,
`created_on`,`last_modified`,`status`
FROM `Tasks`
LEFT JOIN `Users` assignedUsers ON Tasks.assigned_user_id = assignedUsers .user_id
LEFT JOIN `Users` createdUsers ON Tasks.creator_id = createdUsers .user_id
WHERE task_id=2
Since you have two foreign keys and you want to fetch the corresponding data you just have to INNER JOIN the users table twice.
You used LEFT JOIN which will fetch all the data from the first table,in your case tasks, even if no match is found in the second table (in your case this did not made any difference but in cases where an id is not set or the user does not exist anymore maybe this is a problem that is up to you to decide...) and you also used OR in the JOIN conditions which resulted in duplicate tasks in the results.
So you must INNER JOIN twice.One time to get the assigned user and one to get the creator.
Havent tested but this should work :
SELECT t.`task_id`, t.`description`,t.`assessment`,
CONCAT(u1.`firstname`,' ',u1.`lastname`) as creator,
CONCAT(u2.`firstname`,' ',u2.`lastname`) as assigned_user,
t.`created_on`,t.`last_modified`,t.`status`
FROM `tasks` t
INNER JOIN `users` u1 ON t.creator_id=u1.id
INNER JOIN `users` u2 ON t.assigned_user_id=u2.id
WHERE t.`task_id`=2
Thank you all guys but I fixed it by doing:
SELECT `task_id`, `description`,`assessment`,
(SELECT CONCAT(`first_name`, ' ', `last_name`)
FROM `Users`
WHERE `Tasks`.`assigned_user_id` = `Users`.`user_id`) AS assigned_user,
(SELECT CONCAT(`first_name`, ' ', `last_name`)
FROM `Users`
WHERE `Tasks`.`creator_id`=`Users`.`user_id`) AS creator_user,
`created_on`,`last_modified`,`status`
FROM `Tasks`
WHERE task_id=3
I just added FROM Users and WHERE clauses in each inner SELECT,
so I didn't have to do any joins... As always it was way more simple than I thought. Thanks again, much appreciated!
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 */
);
I'm have trouble counting/grouping the results of an inner join
I have two tables
results_dump: Which has two columns: email and result (the result value can be either open or bounce)
all_data: Which has three columns: email, full_name and address
The first goal is to query the result_dump table and count and group the number of times the result is "open" for a specific email.
This query works great:
SELECT `email`, COUNT(*) AS count
FROM `result_dump`
WHERE `date` = "open"
GROUP BY `email`
HAVING COUNT(*) > 3
ORDER BY count DESC
The second goal it to take those results (anyone who "open" more then 3 time) and pull in the 'full_name' and 'address' so I will have details on who opened an email 3+ times.
I have this query and it works as far as getting the data together - But I can't figure out how to get the COUNT, HAVING and ORDER to work with the INNER JOIN?
SELECT *
FROM all_data
INNER JOIN result_dump ON
all_data.email = result_dump.email
where `result` = "open"
SELECT email,name,count(*)
FROM all_data
INNER JOIN result_dump ON
all_data.email = result_dump.email
where `result` = "open"
group by result_dump.email
having count(*)>3
ORDER by count DESC
Nothing wrong with this one I think.
Try with following query:
SELECT * FROM all_data AS a
INNER JOIN
(SELECT * FROM result_dump where email IN
(SELECT `email`
FROM `result_dump`
WHERE `date` = "open"
GROUP BY `email`
HAVING count(email) >3
ORDER BY count(email) DESC)) AS b
ON a.email = b.email
WHERE b.`result` = "open"
This is Works Fine...! Try to this..
SELECT title.title
,count(*)
,title.production_year
,title.id as movie_id
,title.flag as language
,movie_info.info
FROM title INNER JOIN movie_info ON title.id=movie_info.movie_id;
I have two tables users and work_orders.
Now I need to get work_orders results by filtering from users table field.
My following query did not return result.
SELECT `work_orders`.`id` as id, `work_orders`.`type` as type
, `work_orders`.`title` as title, `work_orders`.`status` as status
, `work_orders`.`publish_date` as publish_date
, `work_orders`.`priority` as priority, `work_orders`.`assigned_to` as assigned_to
, `work_orders`.`client_id` as client_id, `work_orders`.`due_date` as due_date
FROM (`work_orders`)
LEFT JOIN `users`
ON `users`.`id` = `work_orders`.`assigned_to`
WHERE `work_orders`.`status` != 'closed'
AND users.manager_id = '143'
ORDER BY `work_orders`.`id` DESC
LIMIT 30
Is your WHERE clause filtering out all results?
Also, if you want to display work_orders that only pertain to certain users, change your LEFT JOIN to an INNER JOIN, or use EXISTS.
Try this...
SELECT field1, field2, ...
FROM work_orders
WHERE EXISTS (
SELECT 1
FROM users
WHERE users.id = work_orders.assigned_to
AND manager_id='143'
)