I have a table that contains 2 ids that I want to retrieve in a query, and I want them to be distinct. However I want them to be distinct between columns, so:
| column1 | column2 |
| 2 | 3 | row1
| 2 | 4 | row2
| 3 | 2 | row3
| 3 | 2 | row3
should return row1 and row2. Any help?
EDIT:
Sorry, that was sorta vague and I was in a rush when posting.
So both of these columns are IDs that reference the same table. Let's think of this as a message between users, where the table in question is a message and the IDs are the user IDs (one for sender, one for receiver). So the messages table looks a little like this:
| sender_id | receiver_id |
| 2 | 3 | message1
| 2 | 3 | message2
| 3 | 2 | message3
Each user can message any other as much as they want, so I can have many messages between each user. I want to know which users a given user has sent a message to. So I need a query to find who the user with ID 2 has messaged. So this theoretical query, given user ID 2, should only return user ID 3. I don't actually care about these messages that I'm searching through, just the users associated with them, who are not the user I'm searching against, and I want a list with unique values.
I've tried something like:
SELECT sender_id, receiver_id FROM messages
WHERE sender_id = 2 OR receiver_id = 2
GROUP BY sender_id
GROUP BY receiver_id
and
SELECT DISTINCT sender_id, receiver_id FROM messages
WHERE sender_id = 2 OR receiver_id = 2
but these only return a distinct list for one of the IDs. (ie. it would return user ID 3 twice ). I hope this is enough info to help.
**
UPDATE:
**
this is the query I ended up using:
SELECT DISTINCT other_user_id FROM (
SELECT sender_id AS some_user_id,
receiver_id AS other_user_id FROM messages
UNION
SELECT receiver_id AS some_user_id,
sender_id AS other_user_id FROM messages
)
WHERE some_user_id = whatever_value
Assuming that column1 and column2 are both numeric data types and you are comparing 2 columns you can use the following
SELECT DISTINCT
LEAST( column1 , column2 ) AS leastColumn,
GREATEST( column1 , column2 ) AS greatestColumn
FROM yourTable
I'm not entirely sure why you would wish to do this though, as if the two columns store the same data then this table is part of an n:m relationship and so this could be further normalised. Additionally this would be come horribly inefficient as the dataset grows but for your specific question this fits the bill.
Figured it out. This is the query I ended up using:
SELECT DISTINCT other_user_id FROM (
SELECT sender_id AS some_user_id,
receiver_id AS other_user_id FROM messages
UNION
SELECT receiver_id AS some_user_id,
sender_id AS other_user_id FROM messages
)
WHERE some_user_id = whatever_value
Related
First, thank you so much for your help.
I have 2 tables: a conversation table and a message table, and a third table assoc_message__conversation that associates the messages to a conversation.
I need to get the latest message_id and message sent for each conversation specified, along with the conversation_id it is associated with.
Here is a db-fiddle: https://www.db-fiddle.com/f/kxRQeGUYYgQ7FTwi96hbLp/0
As you can see in this example, there are two conversations with conversation_id of 1 and 2, and there are three messages associated to each conversation. Messages 1, 2, and 3 are associated to conversation 1, and messages 4, 5 and 6 are associated to conversation 2.
I need to be able to specify the conversation_id's in the assoc_message__conversation table (IDs 1 and 2), and retrieve the latest message_id, message and the associated conversation_id sent from the message table for each conversation specified.
So the rows it should pull are:
conversation_id | message_id | message
------------------------------------------------
1 | 3 | "Latest message"
------------------------------------------------
2 | 6 | "Latest message"
------------------------------------------------
Thank you so much for your help!
In older versions of MySQL (< 8.0.2), we can use Derived Tables. In a Derived table, we can get the latest send_datetime value for each conversation_id. Also, it is noteworthy that you can provide your filters for conversation_id in the WHERE clause of this subquery.
We can then use this subquery`s result-set and join back to the main tables appropriately, to get the row corresponding to the latest message in a conversation.
Schema (MySQL v5.7)
View on DB Fiddle
Query #1
SELECT
amc.conversation_id,
m.message_id,
m.message
FROM
assoc_message__conversation AS amc
JOIN message AS m
ON m.message_id = amc.message_id
JOIN
(
SELECT
amc1.conversation_id,
MAX(m1.send_datetime) AS latest_send_datetime
FROM
assoc_message__conversation AS amc1
JOIN message AS m1
ON m1.message_id = amc1.message_id
WHERE amc1.conversation_id IN (1,2) -- Here you provide your input filters
GROUP BY amc1.conversation_id
) AS dt
ON dt.conversation_id = amc.conversation_id AND
dt.latest_send_datetime = m.send_datetime;
Result
| conversation_id | message_id | message |
| --------------- | ---------- | -------------- |
| 1 | 3 | Latest message |
| 2 | 6 | Latest message |
In MySQL 8.0.2 and above, we can use Row_Number() functionality. Within a partition of conversation_id, we will determine Row Number for every message, sorted in descending order of send_datetime. In this subquery, you can provide your filters for conversation_id in the WHERE clause.
We will then use this result-set as a Derived Table, and consider only those rows, where Row Number value is 1 (as it will belong to latest send_datetime).
Schema (MySQL v8.0)
View on DB Fiddle
Query #2
SELECT
dt.conversation_id,
dt.message_id,
dt.message
FROM
(
SELECT
amc.conversation_id,
m.message_id,
m.message,
ROW_NUMBER() OVER (PARTITION BY amc.conversation_id
ORDER BY m.send_datetime DESC) AS row_no
FROM
assoc_message__conversation AS amc
JOIN message AS m
ON m.message_id = amc.message_id
WHERE amc.conversation_id IN (1,2) -- Here you provide your input filters
) AS dt
WHERE dt.row_no = 1;
Result
| conversation_id | message_id | message |
| --------------- | ---------- | -------------- |
| 1 | 3 | Latest message |
| 2 | 6 | Latest message |
Assuming that amc_id increments for each new message, I would recommend a correlated subquery in the where clause:
select amc.*, m.message
from message m join
assoc_message__conversation amc
on amc.message_id = m.message_id
where amc.amc_id = (select max(amc.amc_id)
from assoc_message__conversation amc2
where amc2.conversation_id = amc.conversation_id
);
If you actually need to use send_datetime, then an additional join is necessary:
where m.send_datetime = (select max(m2.send_datetime)
from message m2 join
assoc_message__conversation amc2
on amc2.message_id = m2.message_id
where amc2.conversation_id = amc.conversation_id
)
I have this data
+----+--------+---------------------------------+
| id | fromid | message |
+----+--------+---------------------------------+
| 1 | 1213 | this is just an example |
+----+--------+---------------------------------+
| 2 | 1992 | other random message |
+----+--------+---------------------------------+
| 3 | 1364 | example number three |
+----+--------+---------------------------------+
I need to search data where fromid='1992' or message LIKE '%example%'
if there is any result where fromid matches 1992 value, return this result, and ignore second condition
but if there is no result from first condition (fromid='1992'), return the result from second condition
can I do that on single query?
One way to do it is using a marker, and then limiting the results to the first row. An example using UNION:-
SELECT 1 AS match_type, my_table.*
from my_table
where fromid = 1992
UNION
SELECT 2 AS match_type, my_table.*
from my_table
WHERE message like '%example%'
ORDER BY match_type
LIMIT 1
Down side of this is that it still forces a relatively slow non indexed query using LIKE with a leading wildcard.
The following is an alternative. In most circumstance it would be pointless, but should the check on id be expected to bring back a record almost always, and the check on the LIKE just a very occasional condition (with only a single record ever returned) then it might be worthwhile as it would avoid having the do the slow LIKE condition searched unless necessary.
SELECT COALESCE(id, SELECT MAX(id) from my_table WHERE message like '%example%') AS id,
COALESCE(fromid, SELECT MAX(formed) from my_table WHERE message like '%example%') AS formed,
COALESCE(message, SELECT MAX(message) from my_table WHERE message like '%example%') AS message
FROM
(
SELECT id, fromid, message
FROM (SELECT 1 AS a_cnt) sub0
LEFT OUTER JOIN
(
SELECT id, fromid, message
from my_table
where fromid = 1992
) sub1
) sub2
There are a lot of questions posted on stackoverflow that are almost same like my problem but I found no working solution. I have a table message and entries are:
id | Message | Status
1 | Hello | 1
2 | Hiiii | 0
4 | Works | 1
I have another table which gives ids 1,2,3 and I want to find the status of all these entries from message table. What I want is if an id doesn't exist in message table then it should return null for that id if I use IN clause to find all status. I want following result:
id | Status
1 | 1
2 | 0
3 | null
I have been using IN clause but didn't get working output. Then I got a solution on stackoverflow and tried this query
SELECT `id`,`status` FROM ( SELECT 1 AS ID UNION ALL SELECT 2 AS ID UNION ALL SELECT 3) ids LEFT OUTER JOIN message ON ids.ID = message.id
But this query is not giving the expected results. Any help would be much appreciated.
I don't see how your query would work. The column id should be ambiguous in the select (unless your database is case sensitive). Try this:
SELECT ids.ID, m.status
FROM ( SELECT 1 AS ID UNION ALL SELECT 2 AS ID UNION ALL SELECT 3
) ids LEFT OUTER JOIN
message m
ON ids.ID = m.id;
I am trying to select jobs that are not currently assigned to a user.
Users table: id | name
Jobs: id | name
Assigned: id | user_id | job_id | date_assigned
I want to select all the jobs that are not currently taken. Example:
Users:
id | name
--------------
1 | Chris
2 | Steve
Jobs
id | name
---------------
1 | Sweep
2 | Skids
3 | Mop
Assigned
id | user_id | job_id | date_assigned
-------------------------------------------------
1 | 1 | 1 | 2012-01-01
2 | 1 | 2 | 2012-01-02
3 | 2 | 3 | 2012-01-05
No two people can be assigned the same job. So the query would return
[1, Sweep]
Since no one is working on it since Chris got moved to Skids a day later.
So far:
SELECT
*
FROM
jobs
WHERE
id
NOT IN
(
SELECT
DISTINCT(job_id)
FROM
assigned
ORDER BY
date_assigned
DESC
)
However, this query returns NULL on the same data set. Not addressing that the sweep job is now open because it is not currently being worked on.
SELECT a.*
FROM jobs a
LEFT JOIN
(
SELECT a.job_id
FROM assigned a
INNER JOIN
(
SELECT MAX(id) AS maxid
FROM assigned
GROUP BY user_id
) b ON a.id = b.maxid
) b ON a.id = b.job_id
WHERE b.job_id IS NULL
This gets the most recent job per user. Once we have a list of those jobs, we select all jobs that aren't on that list.
You can try this variant:
select * from jobs
where id not in (
select job_id from (
select user_id, job_id, max(date_assigned)
from assigned
group by user_id, job_id));
I think you might want:
SELECT *
FROM jobs
WHERE id NOT IN (SELECT job_id
from assigned
where user_id is not null
)
This assumes that re-assigning someone changes the user id on the original assignment. Does this happen? By the way, I also simplified the subquery.
First you need to be looking at a list of only current job assignments. Ordering isn't enough. The way you have it set up, you need a distinct subset of job assignments from Assigned that are the most recent assignments.
So you want a grouping subquery something like
select job_id, user_id, max(date_assigned) last_assigned from assigned group by job_id, user_id
Put it all together and you get
select id, name from jobs
where id not in (
select job_id as id from (
select job_id, user_id, max(date_assigned) last_assigned from assigned
group by job_id, user_id
)
)
As an extra feature, you could pass up the value of "last_assigned" and it would tell you how long a job has been idle for.
I need to count the number of duplicate emails in a mysql database, but without counting the first one (considered the original). In this table, the query result should be the single value "3" (2 duplicate x#q.com plus 1 duplicate f#q.com).
TABLE
ID | Name | Email
1 | Mike | x#q.com
2 | Peter | p#q.com
3 | Mike | x#q.com
4 | Mike | x#q.com
5 | Frank | f#q.com
6 | Jim | f#q.com
My current query produces not one number, but multiple rows, one per email address regardless of how many duplicates of this email are in the table:
SELECT value, count(lds1.leadid) FROM leads_form_element lds1 LEFT JOIN leads lds2 ON lds1.leadID = lds2.leadID
WHERE lds2.typesID = "31" AND lds1.formElementID = '97'
GROUP BY lds1.value HAVING ( COUNT(lds1.value) > 1 )
It's not one query so I'm not sure if it would work in your case, but you could do one query to select the total number of rows, a second query to select distinct email addresses, and subtract the two. This would give you the total number of duplicates...
select count(*) from someTable;
select count(distinct Email) from someTable;
In fact, I don't know if this will work, but you could try doing it all in one query:
select (count(*)-(count(distinct Email))) from someTable
Like I said, untested, but let me know if it works for you.
Try doing a group by in a sub query and then summing up. Something like:
select sum(tot)
from
(
select email, count(1)-1 as tot
from table
group by email
having count(1) > 1
)