MySQL selecting latest occurrences of a GROUP BY'd field [duplicate] - mysql

This question already has answers here:
SQL select only rows with max value on a column [duplicate]
(27 answers)
Closed 6 years ago.
I have a table that has a non-unique id, id, a status, status (which I'm trying to get), and a timestamp of when it was inserted, inserted. I need to select the most recent occurrence of each id ordered by the id. I have the following table ordered by inserted:
id | status | inserted
------------------------------------
4 | approved | 2016-08-09 15:51:52
5 | denied | 2016-08-09 15:52:36
5 | pending | 2016-08-09 15:55:05
The results I need are:
id | status | inserted
------------------------------------
4 | approved | 2016-08-09 15:51:52
5 | pending | 2016-08-09 15:55:05
I have the following SELECT:
SELECT * FROM table
GROUP BY id
ORDER BY inserted
and I'm getting these results:
id | status | inserted
------------------------------------
4 | approved | 2016-08-09 15:51:52
5 | denied | 2016-08-09 15:52:36
The solution is probably an easy one, but I've racked my brain on this long enough trying things such as inner selects and whatnot. Any help would be appreciated.
EDIT:
I had to use the third option from the linked duplicate question to get the results I expected (the one with the LEFT JOIN). I assume it was because I was using a DATETIME type, but I'm unsure.

select t.*
from
<T> t inner join
(select id, max(inserted) as max_inserted from <T> group by id) as m
on m.id = t.id and m.max_inserted = t.inserted
or
select
id,
(
select status from <T> t2
where t2.id = t.id and t2.inserted = max_inserted
) as status,
max(inserted) as max_inserted
from <T>
group by id
You can try searching for "mysql latest per group" or something like that for alternatives more specific to MySQL.

If you want the most recent record for each id, then don't use group by. Instead:
select t.*
from table t
where t.inserted = (select max(t2.inserted) from table t2 where t2.id = t.id);

Related

How to find all the opposite combinations between two columns in SQL

I am making a web dating app that needs to match users and let them chat with each other.
I want to figure out how to find all the matches for a particular user.
Right now I have a table called follows that has 2 columns.
UserID | MatchUserID
--------------------
1 | 2
2 | 1
1 | 3
1 | 4
1 | 5
4 | 1
5 | 4
The idea is that for two users to match they need to follow one another. The table above shows which user follows which.
Assuming that the user who is currently logged on is UserID = 1.
I need a query that will return from the MatchUserID table the following results:
2, 4
In a way, I am looking to find all the opposite combinations between the two columns.
This is the code I use to create the table.
CREATE TABLE Match
(
UserID INT NOT NULL,
MatchUserID INT NOT NULL,
PRIMARY KEY (UserID, MatchUserID)
);
You can do it with a self join:
select m.MatchUserID
from `Match` m inner join `Match` mm
on mm.MatchUserID = m.UserId
where
m.UserId = 1
and
m.MatchUserID = mm.UserId
See the demo.
Results:
| MatchUserID |
| ----------- |
| 2 |
| 4 |
The simplest way possibly is to use EXISTS and a correlated subquery that searches for the other match.
SELECT t1.matchuserid
FROM elbat t1
WHERE t1.userid = 1
AND EXISTS (SELECT *
FROM elbat t2
WHERE t2.matchuserid = t1.userid
AND t2.userid = t1.matchuserid);

MYSQL IN Clause Issue

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;

Finding a single row for an identity [duplicate]

This question already has answers here:
Retrieving the last record in each group - MySQL
(33 answers)
Closed 8 years ago.
I have a table that has multiple values for an identity, and I want to find just one row for each identity where the date field is the newest and then if there is still duplicates just select the one with the lowest id.
My table looks kine of like this:
UniqueID | CustomerId | Name | Address | InspDate
1 1 Bob 123 2013/08/05 00:00:00
2 1 Bob 123 2013/08/05 00:00:00
3 1 Bob 123 2013/03/01 00:00:00
So I only want the row with uniqueid of 1 to show up to show up in this example.
Also I want to limit it to only Customers with inspdates done within the last year if that is possible.
SELECT t.*
FROM
yourtable t INNER JOIN (
SELECT MIN(UniqueID) as Min_ID
FROM
yourtable t1 INNER JOIN (SELECT CustomerID, MAX(InspDate) Max_date
FROM yourtable
GROUP BY CustomerID) m
ON t1.CustomerID=m.CustomerID AND t1.InspDate=m.Max_date
GROUP BY t1.CustomerID) mi
ON t.UniqueID = mi.Min_ID
Please see fiddle here.

MySQL How can I add values of a column together and remove the duplicate rows?

Good day,
I have a MySQL table which has some duplicate rows that have to be removed while adding a value from one column in the duplicated rows to the original.
The problem was caused when another column had the wrong values and that is now fixed but it left the balances split among different rows which have to be added together. The newer rows that were added must then be removed.
In this example, the userid column determines if they are duplicates (or triplicates). userid 6 is duplicated and userid 3 is triplicated.
As an example for userid 3 it has to add up all balances from rows 3, 11 and 13 and has to put that total into row 3 and then remove rows 11 and 13. The balance columns of both of those have to be added together into the original, lower ID row and the newer, higher ID rows must be removed.
ID | balance | userid
---------------------
1 | 10 | 1
2 | 15 | 2
3 | 300 | 3
4 | 80 | 4
5 | 0 | 5
6 | 65 | 6
7 | 178 | 7
8 | 201 | 8
9 | 92 | 9
10 | 0 | 10
11 | 140 | 3
12 | 46 | 6
13 | 30 | 3
I hope that is clear enough and that I have provided enough info. Thanks =)
Two steps.
1. Update:
UPDATE
tableX AS t
JOIN
( SELECT userid
, MIN(id) AS min_id
, SUM(balance) AS sum_balance
FROM tableX
GROUP BY userid
) AS c
ON t.userid = c.userid
SET
t.balance = CASE WHEN t.id = c.min_id
THEN c.sum_balance
ELSE 0
END ;
2. Remove the extra rows:
DELETE t
FROM
tableX AS t
JOIN
( SELECT userid
, MIN(id) AS min_id
FROM tableX
GROUP BY userid
) AS c
ON t.userid = c.userid
AND t.id > c.min_id
WHERE
t.balance = 0 ;
Once you have this solved, it would be good to add a UNIQUE constraint on userid as it seems you want to be storing the balance for each user here. That will avoid any duplicates in the future. You could also remove the (useless?) id column.
SELECT SUM(balance)
FROM your_table
GROUP BY userid
Should work, but the comment saying fix the table is really the best approach.
You can create a table with the same structure and transfer the data to it with this query
insert into newPriceTable(id, userid, balance)
select u.id, p.userid, sum(balance) as summation
from price p
join (
select userid, min(id) as id from price group by userid
) u ON p.userid = u.userid
group by p.userid
Play around the query here: http://sqlfiddle.com/#!2/4bb58/2
Work is mainly done in MSSQL but you should be able to convert the syntax.
Using a GROUP BY UserID you can SUM() the Balance, join that back to your main table to update the balance across all the duplicates. Finally you can use RANK() to order your duplicate Userids and preserve only the earliest values.
I'd select all this into a new table and if it looks good, deprecate your old table and rename then new one.
http://sqlfiddle.com/#!3/068ee/2

MySQL - Exclude rows from Select based on duplication of two columns

I am attempting to narrow results of an existing complex query based on conditional matches on multiple columns within the returned data set. I'll attempt to simplify the data as much as possible here.
Assume that the following table structure represents the data that my existing complex query has already selected (here ordered by date):
+----+-----------+------+------------+
| id | remote_id | type | date |
+----+-----------+------+------------+
| 1 | 1 | A | 2011-01-01 |
| 3 | 1 | A | 2011-01-07 |
| 5 | 1 | B | 2011-01-07 |
| 4 | 1 | A | 2011-05-01 |
+----+-----------+------+------------+
I need to select from that data set based on the following criteria:
If the pairing of remote_id and type is unique to the set, return the row always
If the pairing of remote_id and type is not unique to the set, take the following action:
Of the sets of rows for which the pairing of remote_id and type are not unique, return only the single row for which date is greatest and still less than or equal to now.
So, if today is 2011-01-10, I'd like the data set returned to be:
+----+-----------+------+------------+
| id | remote_id | type | date |
+----+-----------+------+------------+
| 3 | 1 | A | 2011-01-07 |
| 5 | 1 | B | 2011-01-07 |
+----+-----------+------+------------+
For some reason I'm having no luck wrapping my head around this one. I suspect the answer lies in good application of group by, but I just can't grasp it. Any help is greatly appreciated!
/* Rows with exactly one date - always return regardless of when date occurs */
SELECT id, remote_id, type, date
FROM YourTable
GROUP BY remote_id, type
HAVING COUNT(*) = 1
UNION
/* Rows with more than one date - Return Max date <= NOW */
SELECT yt.id, yt.remote_id, yt.type, yt.date
FROM YourTable yt
INNER JOIN (SELECT remote_id, type, max(date) as maxdate
FROM YourTable
WHERE date <= DATE(NOW())
GROUP BY remote_id, type
HAVING COUNT(*) > 1) sq
ON yt.remote_id = sq.remote_id
AND yt.type = sq.type
AND yt.date = sq.maxdate
The group by clause groups all rows that have identical values of one or more columns together and returns one row in the result set for them. If you use aggregate functions (min, max, sum, avg etc.) that will be applied for each "group".
SELECT id, remote_id, type, max(date)
FROM blah
GROUP BY remote_id, date;
I'm not whore where today's date comes in, but assumed that was part of the complex query that you didn't describe and I assume isn't directly relevant to your question here.
Try this:
SELECT a.*
FROM table a INNER JOIN
(
select remote_id, type, MAX(date) date, COUNT(1) cnt from table
group by remote_id, type
) b
WHERE a.remote_id = b.remote_id,
AND a.type = b.type
AND a.date = b.date
AND ( (b.cnt = 1) OR (b.cnt>1 AND b.date <= DATE(NOW())))
Try this
select id, remote_id, type, MAX(date) from table
group by remote_id, type
Hey Carson! You could try using the "distinct" keyword on those two fields, and in a union you can use Count() along with group by and some operators to pull non-unique (greatest and less-than today) records!