I am trying to do following:
SELECT
customer_entity.email,
customer_entity.website_id
FROM
customer_entity
INNER JOIN
(SELECT
email
FROM
customer_entity
GROUP BY email
HAVING COUNT(email) > 1) dupes
ON customer_entity.email = dupes.email
GROUP BY customer_entity.`entity_id`
ORDER BY customer_entity.email ;
Above query returns the result below:
email website_id
abe#abc.com 1
abe#abc.com 2
abe#abc.com 3
abe#abc.com 4
test#abc.com 1
test#abc.com 2
test#abc.com 4
xyz#abc.tv 1
xyz#abc.tv 2
xyz#abc.tv 3
But I want data in below format:
email website1 website2 website3 website4
abe#abc.com 1 2 3 4
test#abc.com 1 2 null 4
xyz#abc.tv 1 2 3 null
is it possible in this case?
Thanks
You can do conditional aggregation:
select
email,
max(website_id = 1) website_1,
max(website_id = 2) website_2,
max(website_id = 3) website_3,
max(website_id = 4) website_4
from customer_entity
group by email
having count(*) > 1
order by email
Note that this simplifies your original query - a self-join is not needed here.
Also, this puts 0/1 values in each column that indicates whether the given email exists for this website - I find that it is more meaningful than repeating the website id in the column.
Related
I'm trying to run an UPDATE query that uses the same table and I'm getting an error saying "1093 - Table 'queues_monitor_times' is specified twice, both as a target for 'UPDATE' and as a separate source for data".
UPDATE queues_monitor_times
SET queue_id = IF((
SELECT id
FROM queues_monitor_times
INNER JOIN(
SELECT pcc_group, pcc, gds, queue, category, `name`
FROM queues_monitor_times
GROUP BY pcc_group, pcc, gds, queue, category, `name`
HAVING COUNT(id) > 1
)temp ON queues_monitor_times.pcc_group = temp.pcc_group AND
queues_monitor_times.pcc = temp.pcc AND
queues_monitor_times.gds = temp.gds AND
queues_monitor_times.queue = temp.queue AND
queues_monitor_times.category = temp.category AND
queues_monitor_times.`name` = temp.`name`), 1, id)
WHERE
id NOT IN (SELECT MIN(id) FROM queues_old GROUP BY pcc_group, pcc, gds, queue, category, `name`);
I ran the select query by itself and it showed all the rows that were duplicates, which is what I wanted. I want queue_id to be set with the lowest duplicate row's id if the row is a duplicate or the row id if it is not.
Example of what the query should do:
id dup_id name value
1 1 John 13
2 2 John 13
3 3 Sally 6
4 4 Frank 4
5 5 Sally 6
And after running the query it will turn into
id dup_id name value
1 1 John 13
2 1 John 13
3 3 Sally 6
4 4 Frank 4
5 3 Sally 6
Please advise and thank you for your help.
I was able to solve my problem. Thanks for all your help!
UPDATE queues_monitor_times
SET queue_id = (
SELECT
id
FROM
queues_old
WHERE
queues_old.pcc_group = queues_monitor_times.pcc_group
AND queues_old.pcc = queues_monitor_times.pcc
AND queues_old.gds = queues_monitor_times.gds
AND queues_old.queue = queues_monitor_times.queue
AND queues_old.category = queues_monitor_times.category
AND queues_old.`name` = queues_monitor_times.`name`
GROUP BY pcc_group, pcc, gds, queue, category, `name`
HAVING COUNT(id) > 1)
WHERE
id NOT IN (SELECT MIN(id) FROM queues_old GROUP BY pcc_group, pcc, gds, queue, category, `name`);
For those that will want to use this in the future, queues_monitor_times table and queues_old table have the exact same data.
I am stuck in a problem explained below:
id | user_id | admin
1 | 1 | 2
2 | 1 | 5
3 | 2 | 5
4 | 2 | 5
5 | 3 | 5
6 | 4 | 5
I need to write a query to get all the users who are associated with an admin id passed in WHERE clause AND has more than one transactions(can be with another admin).
If user_id is 5 then result should come
user_id
1
2
Have tried
SELECT
user_id ,
COUNT(*) count
FROM
table
WHERE admin = 5
GROUP BY
user_id
Having
count > 1
but this above-mentioned query skips the user_id that has only one transaction with admin = 5 and has another row with another admin.
Suggestions?
Aggregate on the user_id and assert that:
The admin of interest (e.g. 5) appears,
Two or more admins of any type appear
SELECT user_id
FROM yourTable
GROUP BY user_id
HAVING SUM(CASE WHEN admin = 5 THEN 1 ELSE 0 END) > 0 AND
COUNT(*) > 1;
Demo
Try this
SELECT user_id from `tablename` WHERE admin IN( SELECT admin FROM `tablename` GROUP BY admin HAVING count(*) > 1)
In your query filter on admin=5 and with a separate subquery on those users, who have more than 1 transactions. You need to have the count in a subquery because the admin=5 criterion does not apply to the count.
SELECT DISTINCT user_id
FROM yourTable
WHERE admin_id=5
AND user_id IN (SELECT user_id FROM yourTable GROUP BY user_id HAVING COUNT(*)>1)
SELECT user_id
FROM test_table1
GROUP BY user_id
HAVING SUM(CASE WHEN admin = &n THEN 1 ELSE 0 END ) > 0
AND
COUNT(user_id) > 1;
If I understand your question, you want to ask:
Show me all users who have more than one admin associated
Also allow me to identify which admin specifically is in question (in your example 5)
This would give you a list of every user (user id only)
SELECT DISTINCT table.user_id
FROM table
JOIN (
SELECT user_id
,COUNT(DISTINCT admin_id) num_admins
FROM table
GROUP BY user_id
HAVING COUNT(DISTINCT admin_id) > 1
) multi_admin
ON table.user_id = multi_admin.user_id
WHERE admin = 5 -- if you want to look only at user_ids somehow associated with
I have a Table that tracks followers
FollowerUserId, FollowingUserId
1 2
2 1
3 1
4 1
1 5
I want to get all user that given Id follows and is followed by or Both.
for example for UserId 1,I want result to be: (FG: Following, FD: Followed, B: Both ways)
2,B
5,FG
3,FD
4,FD
i can easily get FG and FD by doing union
Select FollowerUserId, 'FD' From Table Where FollowingUserId =1
Union
Select FollowingUserId, 'FG' From Table Where FollowerUserId =1;
with above i get user 2 as
2,FG
2,FD
from above but I really need 2,B without UserId 2 duplicated.
How can this be done efficiently?
You can use aggregation on your basic query:
SELECT UserId,
(CASE WHEN COUNT(DISTINCT which) = 1 THEN MIN(which)
ELSE 'B'
END)
FROM (Select FollowerUserId as UserId, 'FD' as which From Table Where FollowingUserId = 1
Union ALL
Select FollowingUserId, 'FG' From Table Where FollowerUserId = 1
) f
GROUP BY UserId;
I have user1 who exchanged messages with user2 and user4 (these parameters are known). I now want to select the latest sent or received message for each conversation (i.e. LIMIT 1 for each conversation).
SQLFiddle
Currently my query returns all messages for all conversations:
SELECT *
FROM message
WHERE (toUserID IN (2,4) AND userID = 1)
OR (userID IN (2,4) AND toUserID = 1)
ORDER BY message.time DESC
The returned rows should be messageID 3 and 6.
Assuming that higher id values indicate more recent messages, you can do this:
Find all messages that involve user 1
Group the results by the other user id
Get the maximum message id per group
SELECT *
FROM message
WHERE messageID IN (
SELECT MAX(messageID)
FROM message
WHERE userID = 1 -- optionally filter by the other user
OR toUserID = 1 -- optionally filter by the other user
GROUP BY CASE WHEN userID = 1 THEN toUserID ELSE userID END
)
ORDER BY messageID DESC
Updated SQLFiddle
You can do this easily by separating it into two queries with ORDER BY and LIMIT then joining them with UNION:
(SELECT *
FROM message
WHERE (toUserID IN (2,4) AND userID = 1)
ORDER BY message.time DESC
LIMIT 1)
UNION
(SELECT *
FROM message
WHERE (userID IN (2,4) AND toUserID = 1)
ORDER BY message.time DESC
LIMIT 1)
The parenthesis are important here, and this returns messages 2 and 6, which seems correct, not 3 and 6.
It also seems like you could use UNION ALL for performance instead of UNION because there won't be duplicates between the two queries, but it's better if you decide that.
Here's your data:
MESSAGEID USERID TOUSERID MESSAGE TIME
1 1 2 nachricht 1 123
2 1 2 nachricht 2 124
3 2 1 nachricht 3 125
4 3 2 nachricht wrong 1263
5 2 4 nachricht wrong 1261
6 4 1 nachricht sandra 126
The below works as required:
SELECT m1.*
FROM Message m1
LEFT JOIN Message m2
ON LEAST(m1.toUserID, m1.userID) = LEAST(m2.toUserID, m2.userID)
AND GREATEST(m1.toUserID, m1.userID) = GREATEST(m2.toUserID, m2.userID)
AND m2.time > m1.Time
WHERE m2.MessageID IS NULL
AND ( (m1.toUserID IN (2,4) AND m1.userID = 1)
OR (m1.userID IN (2,4) AND m1.toUserID = 1)
);
To simplify how this works, imagine you just wanted the latest message sent by userid 1, rather than having to match the to/from tuples as this adds clutter to the query that doesn't help. To get this I would use:
SELECT m1.*
FROM Message AS m1
LEFT JOIN Message AS m2
ON m2.UserID = m1.UserID
AND m2.time > m1.time
WHERE m1.UserID = 1
AND m2.MessageID IS NULL;
So, we are joining similar messages, stipulating that the second message (m2) has a greater time than the first, where m2 is null it means there is not a similar message with a later time, therefore m2 is the latest message.
Exactly the principal has been applied in the solution, but we have a more complicated join to link conversations.
I have used LEAST and GREATEST in the join, the theory being that since you have 2 members in your tuple (UserID, ToUserID), then in any combination the greatest and the least will be the same, e.g.:
From/To | Greatest | Least |
--------+-----------+-------+
1, 2 | 2 | 1 |
2, 1 | 2 | 1 |
1, 4 | 4 | 1 |
4, 1 | 4 | 1 |
4, 2 | 4 | 2 |
2, 4 | 4 | 2 |
As you can see, in similar From/To the greatest and the least will be the same, so you can use this to join the table to itself.
There are two parts of your query in the following order:
You want the latest outgoing or incoming message for a conversation between two users
You want these latest messages for two different pairs of users, i.e. conversations.
So, lets get the latest message for a conversation between UserID a and UserID b:
SELECT *
FROM message
WHERE (toUserID, userID) IN ((a, b), (b, a))
ORDER BY message.time DESC
LIMIT 1
Then you want these to be combined for the two conversations between UserIDs 1 and 2 and UserIDs 1 and 4. This is where the union comes into play (we do not need to check for duplicates, thus we use UNION ALL, thanks to Marcus Adams, who brought that up first).
So a complete and straightforward solution would be:
(SELECT *
FROM message
WHERE (toUserID, userID) IN ((2, 1), (1, 2))
ORDER BY message.time DESC
LIMIT 1)
UNION ALL
(SELECT *
FROM message
WHERE (toUserID, userID) IN ((4, 1), (1, 4))
ORDER BY message.time DESC
LIMIT 1)
And as expected, you get message 3 and 6 in your SQLFiddle.
I have a table of services that have been provided to clients. I'm trying to make a query that selects all the clients who received a service that WEREN'T provided by a certain user.
So consider this table...
id_client | id_service | id_user
--------------------------------
5 | 3 | 2
7 | 4 | 2
7 | 4 | 1
9 | 4 | 2
8 | 4 | 1
If I write the query like this:
SELECT id_client FROM table WHERE id_service=4 AND id_user<>1
I still end up getting id_client 7. But I don't want to get client 7 because that client HAS received that service from user 1. (They're showing up because they've also received that service from user 2)
In the example above I would only want to be returned with client 9
How can I write the query to make sure that clients that have EVER received service 4 from user 1 don't show up?
Try this:
SELECT DISTINCT id_client
FROM yourtable t
WHERE id_service = 4 AND id_client NOT IN
(SELECT DISTINCT id_client
FROM yourtable t
WHERE id_user = 1
)
I'd write it like this:
SELECT DISTINCT id_client
FROM mytable t1
LEFT OUTER JOIN mytable t2
ON t1.id_client = t2.id_client AND t2.id_user = 1
WHERE t2.id_client IS NULL
When the conditions of a left outer join are not met, the row on the left side is still returned, and all the columns for the row on the right side are null. So if you search for cases of null in a column that would be certain to be non-null if there were a match, you know the outer join found no match.
SELECT id_client
FROM table
WHERE id_service = 4
GROUP BY id_client
HAVING MAX(CASE
WHEN id_user = 1 THEN 2
ELSE 1
END) = 1