MySQL: Two of every set - php

Assuming I have table "Department" (200 row) & "Employees" (300,000 row) each has their own details
And the vector table combining both is "dep_emp"
CREATE TABLE IF NOT EXISTS `dep_emp` (
`id` int(11) NOT NULL DEFAULT '0',
`dep_id` int(11) NOT NULL DEFAULT '0',
`emp_id` int(11) NOT NULL DEFAULT '0'
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
My question is how to make a query to get two of employees (any two employees) for every Department?
My solution so far is this:
(SELECT * FROM `dep_emp` WHERE dep_id=1 LIMIT 2) UNION ALL
(SELECT * FROM `dep_emp` WHERE dep_id=2 LIMIT 2) UNION ALL
(SELECT * FROM `dep_emp` WHERE dep_id=3 LIMIT 2) ........etc
of course this is done {n} times for each Dept so if I wanted to display two Employees for all dept then I will have to write 199 UNION ALL, this is very ugly
Any other solutions
PS. there are a lot of details that I have moved out of the problem to make things a lot simpler

This may work:
SELECT *
FROM Department
JOIN dep_emp USING (dep_id)
JOIN Employees USING (emp_id)
WHERE emp_id IN
(SELECT emp_id
FROM Employees
JOIN dep_emp USING (emp_id)
WHERE dep_emp.dep_id = Department.dep_id LIMIT 2)
I didn't test it though.
OK, that didn't work. Try this instead:
SELECT *
FROM Department
JOIN dep_emp USING (dep_id)
JOIN Employees USING (emp_id)
JOIN (SELECT emp_id, dep_id
FROM dep_emp
GROUP BY dep_id
LIMIT 2) emp_2
USING (emp_id, dep_id)
BTW don't forget to put some indexes on the tables. On dep_emp put a primary key on both columns AND a unique key on the columns in the reverse order! (That way the index works either way.) (You don't need the ID column in dep_emp BTW, at least not in the structure you showed.)

SELECT d.*, e.*
FROM Department AS d
JOIN Employees AS e
ON e.id IN
( SELECT de.emp_id
FROM dep_emp AS de
WHERE de.dep_id = d.id
LIMIT 2
)
Another, very different way to get two emp_id for every department:
SELECT dep_id
, MIN(emp_id) AS emp_id_1
, MAX(emp_id) AS emp_id_2
FROM dep_emp
GROUP BY dep_id
or (to have in separate rows, so this can be joined to the other 2 tables):
SELECT dep_id
, MIN(emp_id) AS emp_id
FROM dep_emp
GROUP BY dep_id
UNION ALL
SELECT dep_id
, MAX(emp_id)
FROM dep_emp
GROUP BY dep_id
The first query does not work in MySQL, because LIMIT is not allowed inside IN subqueries. Here's another approach:
SELECT e.*
FROM
( SELECT DISTINCT dep_id
FROM dep_emp
) AS d
JOIN
dep_emp AS e
ON d.dep_id = e.dep_id
AND e.emp_id <=
COALESCE( ( SELECT de.emp_id
FROM dep_emp AS de
WHERE de.dep_id = d.dep_id
ORDER BY de.emp_id --- OFFSET 1
LIMIT 1 OFFSET 1 --- to get 2 employess
) --- per department
, 9999999 )
ORDER BY e.dep_id
, e.emp_id
Use OFFSET x to get x+1 employees per department.

Related

Subquery order by not working for outer group by query in mysql [duplicate]

There is a table messages that contains data as shown below:
Id Name Other_Columns
-------------------------
1 A A_data_1
2 A A_data_2
3 A A_data_3
4 B B_data_1
5 B B_data_2
6 C C_data_1
If I run a query select * from messages group by name, I will get the result as:
1 A A_data_1
4 B B_data_1
6 C C_data_1
What query will return the following result?
3 A A_data_3
5 B B_data_2
6 C C_data_1
That is, the last record in each group should be returned.
At present, this is the query that I use:
SELECT
*
FROM (SELECT
*
FROM messages
ORDER BY id DESC) AS x
GROUP BY name
But this looks highly inefficient. Any other ways to achieve the same result?
MySQL 8.0 now supports windowing functions, like almost all popular SQL implementations. With this standard syntax, we can write greatest-n-per-group queries:
WITH ranked_messages AS (
SELECT m.*, ROW_NUMBER() OVER (PARTITION BY name ORDER BY id DESC) AS rn
FROM messages AS m
)
SELECT * FROM ranked_messages WHERE rn = 1;
This and other approaches to finding groupwise maximal rows are illustrated in the MySQL manual.
Below is the original answer I wrote for this question in 2009:
I write the solution this way:
SELECT m1.*
FROM messages m1 LEFT JOIN messages m2
ON (m1.name = m2.name AND m1.id < m2.id)
WHERE m2.id IS NULL;
Regarding performance, one solution or the other can be better, depending on the nature of your data. So you should test both queries and use the one that is better at performance given your database.
For example, I have a copy of the StackOverflow August data dump. I'll use that for benchmarking. There are 1,114,357 rows in the Posts table. This is running on MySQL 5.0.75 on my Macbook Pro 2.40GHz.
I'll write a query to find the most recent post for a given user ID (mine).
First using the technique shown by #Eric with the GROUP BY in a subquery:
SELECT p1.postid
FROM Posts p1
INNER JOIN (SELECT pi.owneruserid, MAX(pi.postid) AS maxpostid
FROM Posts pi GROUP BY pi.owneruserid) p2
ON (p1.postid = p2.maxpostid)
WHERE p1.owneruserid = 20860;
1 row in set (1 min 17.89 sec)
Even the EXPLAIN analysis takes over 16 seconds:
+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 76756 | |
| 1 | PRIMARY | p1 | eq_ref | PRIMARY,PostId,OwnerUserId | PRIMARY | 8 | p2.maxpostid | 1 | Using where |
| 2 | DERIVED | pi | index | NULL | OwnerUserId | 8 | NULL | 1151268 | Using index |
+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+
3 rows in set (16.09 sec)
Now produce the same query result using my technique with LEFT JOIN:
SELECT p1.postid
FROM Posts p1 LEFT JOIN posts p2
ON (p1.owneruserid = p2.owneruserid AND p1.postid < p2.postid)
WHERE p2.postid IS NULL AND p1.owneruserid = 20860;
1 row in set (0.28 sec)
The EXPLAIN analysis shows that both tables are able to use their indexes:
+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
| 1 | SIMPLE | p1 | ref | OwnerUserId | OwnerUserId | 8 | const | 1384 | Using index |
| 1 | SIMPLE | p2 | ref | PRIMARY,PostId,OwnerUserId | OwnerUserId | 8 | const | 1384 | Using where; Using index; Not exists |
+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
2 rows in set (0.00 sec)
Here's the DDL for my Posts table:
CREATE TABLE `posts` (
`PostId` bigint(20) unsigned NOT NULL auto_increment,
`PostTypeId` bigint(20) unsigned NOT NULL,
`AcceptedAnswerId` bigint(20) unsigned default NULL,
`ParentId` bigint(20) unsigned default NULL,
`CreationDate` datetime NOT NULL,
`Score` int(11) NOT NULL default '0',
`ViewCount` int(11) NOT NULL default '0',
`Body` text NOT NULL,
`OwnerUserId` bigint(20) unsigned NOT NULL,
`OwnerDisplayName` varchar(40) default NULL,
`LastEditorUserId` bigint(20) unsigned default NULL,
`LastEditDate` datetime default NULL,
`LastActivityDate` datetime default NULL,
`Title` varchar(250) NOT NULL default '',
`Tags` varchar(150) NOT NULL default '',
`AnswerCount` int(11) NOT NULL default '0',
`CommentCount` int(11) NOT NULL default '0',
`FavoriteCount` int(11) NOT NULL default '0',
`ClosedDate` datetime default NULL,
PRIMARY KEY (`PostId`),
UNIQUE KEY `PostId` (`PostId`),
KEY `PostTypeId` (`PostTypeId`),
KEY `AcceptedAnswerId` (`AcceptedAnswerId`),
KEY `OwnerUserId` (`OwnerUserId`),
KEY `LastEditorUserId` (`LastEditorUserId`),
KEY `ParentId` (`ParentId`),
CONSTRAINT `posts_ibfk_1` FOREIGN KEY (`PostTypeId`) REFERENCES `posttypes` (`PostTypeId`)
) ENGINE=InnoDB;
Note to commenters: If you want another benchmark with a different version of MySQL, a different dataset, or different table design, feel free to do it yourself. I have shown the technique above. Stack Overflow is here to show you how to do software development work, not to do all the work for you.
UPD: 2017-03-31, the version 5.7.5 of MySQL made the ONLY_FULL_GROUP_BY switch enabled by default (hence, non-deterministic GROUP BY queries became disabled). Moreover, they updated the GROUP BY implementation and the solution might not work as expected anymore even with the disabled switch. One needs to check.
Bill Karwin's solution above works fine when item count within groups is rather small, but the performance of the query becomes bad when the groups are rather large, since the solution requires about n*n/2 + n/2 of only IS NULL comparisons.
I made my tests on a InnoDB table of 18684446 rows with 1182 groups. The table contains testresults for functional tests and has the (test_id, request_id) as the primary key. Thus, test_id is a group and I was searching for the last request_id for each test_id.
Bill's solution has already been running for several hours on my dell e4310 and I do not know when it is going to finish even though it operates on a coverage index (hence using index in EXPLAIN).
I have a couple of other solutions that are based on the same ideas:
if the underlying index is BTREE index (which is usually the case), the largest (group_id, item_value) pair is the last value within each group_id, that is the first for each group_id if we walk through the index in descending order;
if we read the values which are covered by an index, the values are read in the order of the index;
each index implicitly contains primary key columns appended to that (that is the primary key is in the coverage index). In solutions below I operate directly on the primary key, in you case, you will just need to add primary key columns in the result.
in many cases it is much cheaper to collect the required row ids in the required order in a subquery and join the result of the subquery on the id. Since for each row in the subquery result MySQL will need a single fetch based on primary key, the subquery will be put first in the join and the rows will be output in the order of the ids in the subquery (if we omit explicit ORDER BY for the join)
3 ways MySQL uses indexes is a great article to understand some details.
Solution 1
This one is incredibly fast, it takes about 0,8 secs on my 18M+ rows:
SELECT test_id, MAX(request_id) AS request_id
FROM testresults
GROUP BY test_id DESC;
If you want to change the order to ASC, put it in a subquery, return the ids only and use that as the subquery to join to the rest of the columns:
SELECT test_id, request_id
FROM (
SELECT test_id, MAX(request_id) AS request_id
FROM testresults
GROUP BY test_id DESC) as ids
ORDER BY test_id;
This one takes about 1,2 secs on my data.
Solution 2
Here is another solution that takes about 19 seconds for my table:
SELECT test_id, request_id
FROM testresults, (SELECT #group:=NULL) as init
WHERE IF(IFNULL(#group, -1)=#group:=test_id, 0, 1)
ORDER BY test_id DESC, request_id DESC
It returns tests in descending order as well. It is much slower since it does a full index scan but it is here to give you an idea how to output N max rows for each group.
The disadvantage of the query is that its result cannot be cached by the query cache.
Use your subquery to return the correct grouping, because you're halfway there.
Try this:
select
a.*
from
messages a
inner join
(select name, max(id) as maxid from messages group by name) as b on
a.id = b.maxid
If it's not id you want the max of:
select
a.*
from
messages a
inner join
(select name, max(other_col) as other_col
from messages group by name) as b on
a.name = b.name
and a.other_col = b.other_col
This way, you avoid correlated subqueries and/or ordering in your subqueries, which tend to be very slow/inefficient.
I arrived at a different solution, which is to get the IDs for the last post within each group, then select from the messages table using the result from the first query as the argument for a WHERE x IN construct:
SELECT id, name, other_columns
FROM messages
WHERE id IN (
SELECT MAX(id)
FROM messages
GROUP BY name
);
I don't know how this performs compared to some of the other solutions, but it worked spectacularly for my table with 3+ million rows. (4 second execution with 1200+ results)
This should work both on MySQL and SQL Server.
Solution by sub query fiddle Link
select * from messages where id in
(select max(id) from messages group by Name)
Solution By join condition fiddle link
select m1.* from messages m1
left outer join messages m2
on ( m1.id<m2.id and m1.name=m2.name )
where m2.id is null
Reason for this post is to give fiddle link only.
Same SQL is already provided in other answers.
An approach with considerable speed is as follows.
SELECT *
FROM messages a
WHERE Id = (SELECT MAX(Id) FROM messages WHERE a.Name = Name)
Result
Id Name Other_Columns
3 A A_data_3
5 B B_data_2
6 C C_data_1
We will look at how you can use MySQL at getting the last record in a Group By of records. For example if you have this result set of posts.
id
category_id
post_title
1
1
Title 1
2
1
Title 2
3
1
Title 3
4
2
Title 4
5
2
Title 5
6
3
Title 6
I want to be able to get the last post in each category which are Title 3, Title 5 and Title 6. To get the posts by the category you will use the MySQL Group By keyboard.
select * from posts group by category_id
But the results we get back from this query is.
id
category_id
post_title
1
1
Title 1
4
2
Title 4
6
3
Title 6
The group by will always return the first record in the group on the result set.
SELECT id, category_id, post_title
FROM posts
WHERE id IN (
SELECT MAX(id)
FROM posts
GROUP BY category_id );
This will return the posts with the highest IDs in each group.
id
category_id
post_title
3
1
Title 3
5
2
Title 5
6
3
Title 6
Reference Click Here
Here are two suggestions. First, if mysql supports ROW_NUMBER(), it's very simple:
WITH Ranked AS (
SELECT Id, Name, OtherColumns,
ROW_NUMBER() OVER (
PARTITION BY Name
ORDER BY Id DESC
) AS rk
FROM messages
)
SELECT Id, Name, OtherColumns
FROM messages
WHERE rk = 1;
I'm assuming by "last" you mean last in Id order. If not, change the ORDER BY clause of the ROW_NUMBER() window accordingly. If ROW_NUMBER() isn't available, this is another solution:
Second, if it doesn't, this is often a good way to proceed:
SELECT
Id, Name, OtherColumns
FROM messages
WHERE NOT EXISTS (
SELECT * FROM messages as M2
WHERE M2.Name = messages.Name
AND M2.Id > messages.Id
)
In other words, select messages where there is no later-Id message with the same Name.
Clearly there are lots of different ways of getting the same results, your question seems to be what is an efficient way of getting the last results in each group in MySQL. If you are working with huge amounts of data and assuming you are using InnoDB with even the latest versions of MySQL (such as 5.7.21 and 8.0.4-rc) then there might not be an efficient way of doing this.
We sometimes need to do this with tables with even more than 60 million rows.
For these examples I will use data with only about 1.5 million rows where the queries would need to find results for all groups in the data. In our actual cases we would often need to return back data from about 2,000 groups (which hypothetically would not require examining very much of the data).
I will use the following tables:
CREATE TABLE temperature(
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
groupID INT UNSIGNED NOT NULL,
recordedTimestamp TIMESTAMP NOT NULL,
recordedValue INT NOT NULL,
INDEX groupIndex(groupID, recordedTimestamp),
PRIMARY KEY (id)
);
CREATE TEMPORARY TABLE selected_group(id INT UNSIGNED NOT NULL, PRIMARY KEY(id));
The temperature table is populated with about 1.5 million random records, and with 100 different groups.
The selected_group is populated with those 100 groups (in our cases this would normally be less than 20% for all of the groups).
As this data is random it means that multiple rows can have the same recordedTimestamps. What we want is to get a list of all of the selected groups in order of groupID with the last recordedTimestamp for each group, and if the same group has more than one matching row like that then the last matching id of those rows.
If hypothetically MySQL had a last() function which returned values from the last row in a special ORDER BY clause then we could simply do:
SELECT
last(t1.id) AS id,
t1.groupID,
last(t1.recordedTimestamp) AS recordedTimestamp,
last(t1.recordedValue) AS recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.groupID = g.id
ORDER BY t1.recordedTimestamp, t1.id
GROUP BY t1.groupID;
which would only need to examine a few 100 rows in this case as it doesn't use any of the normal GROUP BY functions. This would execute in 0 seconds and hence be highly efficient.
Note that normally in MySQL we would see an ORDER BY clause following the GROUP BY clause however this ORDER BY clause is used to determine the ORDER for the last() function, if it was after the GROUP BY then it would be ordering the GROUPS. If no GROUP BY clause is present then the last values will be the same in all of the returned rows.
However MySQL does not have this so let's look at different ideas of what it does have and prove that none of these are efficient.
Example 1
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.id = (
SELECT t2.id
FROM temperature t2
WHERE t2.groupID = g.id
ORDER BY t2.recordedTimestamp DESC, t2.id DESC
LIMIT 1
);
This examined 3,009,254 rows and took ~0.859 seconds on 5.7.21 and slightly longer on 8.0.4-rc
Example 2
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM temperature t1
INNER JOIN (
SELECT max(t2.id) AS id
FROM temperature t2
INNER JOIN (
SELECT t3.groupID, max(t3.recordedTimestamp) AS recordedTimestamp
FROM selected_group g
INNER JOIN temperature t3 ON t3.groupID = g.id
GROUP BY t3.groupID
) t4 ON t4.groupID = t2.groupID AND t4.recordedTimestamp = t2.recordedTimestamp
GROUP BY t2.groupID
) t5 ON t5.id = t1.id;
This examined 1,505,331 rows and took ~1.25 seconds on 5.7.21 and slightly longer on 8.0.4-rc
Example 3
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM temperature t1
WHERE t1.id IN (
SELECT max(t2.id) AS id
FROM temperature t2
INNER JOIN (
SELECT t3.groupID, max(t3.recordedTimestamp) AS recordedTimestamp
FROM selected_group g
INNER JOIN temperature t3 ON t3.groupID = g.id
GROUP BY t3.groupID
) t4 ON t4.groupID = t2.groupID AND t4.recordedTimestamp = t2.recordedTimestamp
GROUP BY t2.groupID
)
ORDER BY t1.groupID;
This examined 3,009,685 rows and took ~1.95 seconds on 5.7.21 and slightly longer on 8.0.4-rc
Example 4
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.id = (
SELECT max(t2.id)
FROM temperature t2
WHERE t2.groupID = g.id AND t2.recordedTimestamp = (
SELECT max(t3.recordedTimestamp)
FROM temperature t3
WHERE t3.groupID = g.id
)
);
This examined 6,137,810 rows and took ~2.2 seconds on 5.7.21 and slightly longer on 8.0.4-rc
Example 5
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM (
SELECT
t2.id,
t2.groupID,
t2.recordedTimestamp,
t2.recordedValue,
row_number() OVER (
PARTITION BY t2.groupID ORDER BY t2.recordedTimestamp DESC, t2.id DESC
) AS rowNumber
FROM selected_group g
INNER JOIN temperature t2 ON t2.groupID = g.id
) t1 WHERE t1.rowNumber = 1;
This examined 6,017,808 rows and took ~4.2 seconds on 8.0.4-rc
Example 6
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM (
SELECT
last_value(t2.id) OVER w AS id,
t2.groupID,
last_value(t2.recordedTimestamp) OVER w AS recordedTimestamp,
last_value(t2.recordedValue) OVER w AS recordedValue
FROM selected_group g
INNER JOIN temperature t2 ON t2.groupID = g.id
WINDOW w AS (
PARTITION BY t2.groupID
ORDER BY t2.recordedTimestamp, t2.id
RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
)
) t1
GROUP BY t1.groupID;
This examined 6,017,908 rows and took ~17.5 seconds on 8.0.4-rc
Example 7
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.groupID = g.id
LEFT JOIN temperature t2
ON t2.groupID = g.id
AND (
t2.recordedTimestamp > t1.recordedTimestamp
OR (t2.recordedTimestamp = t1.recordedTimestamp AND t2.id > t1.id)
)
WHERE t2.id IS NULL
ORDER BY t1.groupID;
This one was taking forever so I had to kill it.
Here is another way to get the last related record using GROUP_CONCAT with order by and SUBSTRING_INDEX to pick one of the record from the list
SELECT
`Id`,
`Name`,
SUBSTRING_INDEX(
GROUP_CONCAT(
`Other_Columns`
ORDER BY `Id` DESC
SEPARATOR '||'
),
'||',
1
) Other_Columns
FROM
messages
GROUP BY `Name`
Above query will group the all the Other_Columns that are in same Name group and using ORDER BY id DESC will join all the Other_Columns in a specific group in descending order with the provided separator in my case i have used || ,using SUBSTRING_INDEX over this list will pick the first one
Fiddle Demo
Hi #Vijay Dev if your table messages contains Id which is auto increment primary key then to fetch the latest record basis on the primary key your query should read as below:
SELECT m1.* FROM messages m1 INNER JOIN (SELECT max(Id) as lastmsgId FROM messages GROUP BY Name) m2 ON m1.Id=m2.lastmsgId
I've not yet tested with large DB but I think this could be faster than joining tables:
SELECT *, Max(Id) FROM messages GROUP BY Name
SELECT
column1,
column2
FROM
table_name
WHERE id IN
(SELECT
MAX(id)
FROM
table_name
GROUP BY column1)
ORDER BY column1 ;
You can take view from here as well.
http://sqlfiddle.com/#!9/ef42b/9
FIRST SOLUTION
SELECT d1.ID,Name,City FROM Demo_User d1
INNER JOIN
(SELECT MAX(ID) AS ID FROM Demo_User GROUP By NAME) AS P ON (d1.ID=P.ID);
SECOND SOLUTION
SELECT * FROM (SELECT * FROM Demo_User ORDER BY ID DESC) AS T GROUP BY NAME ;
If you need the most recent or oldest record of a text column in a grouped query, and you would rather not use a subquery, you can do this...
Ex. You have a list of movies and need to get the count in the series and the latest movie
id
series
name
1
Star Wars
A New hope
2
Star Wars
The Empire Strikes Back
3
Star Wars
Return of The Jedi
SELECT COUNT(id), series, SUBSTRING(MAX(CONCAT(id, name)), LENGTH(id) + 1),
FROM Movies
GROUP BY series
This returns...
id
series
name
3
Star Wars
Return of The Jedi
MAX will return the row with the highest value, so by concatenating the id to the name, you now will get the newest record, then just strip off the id for your final result.
More efficient than using a subquery.
So for the given example:
SELECT MAX(Id), Name, SUBSTRING(MAX(CONCAT(Id, Other_Columns)), LENGTH(Id) + 1),
FROM messages
GROUP BY Name
Happy coding, and "May The Force Be With You" :)
Try this:
SELECT jos_categories.title AS name,
joined .catid,
joined .title,
joined .introtext
FROM jos_categories
INNER JOIN (SELECT *
FROM (SELECT `title`,
catid,
`created`,
introtext
FROM `jos_content`
WHERE `sectionid` = 6
ORDER BY `id` DESC) AS yes
GROUP BY `yes`.`catid` DESC
ORDER BY `yes`.`created` DESC) AS joined
ON( joined.catid = jos_categories.id )
Here is my solution:
SELECT
DISTINCT NAME,
MAX(MESSAGES) OVER(PARTITION BY NAME) MESSAGES
FROM MESSAGE;
SELECT * FROM table_name WHERE primary_key IN (SELECT MAX(primary_key) FROM table_name GROUP BY column_name )
**
Hi, this query might help :
**
SELECT
*
FROM
message
WHERE
`Id` IN (
SELECT
MAX(`Id`)
FROM
message
GROUP BY
`Name`
)
ORDER BY
`Id` DESC
i find best solution in https://dzone.com/articles/get-last-record-in-each-mysql-group
select * from `data` where `id` in (select max(`id`) from `data` group by `name_id`)
The below query will work fine as per your question.
SELECT M1.*
FROM MESSAGES M1,
(
SELECT SUBSTR(Others_data,1,2),MAX(Others_data) AS Max_Others_data
FROM MESSAGES
GROUP BY 1
) M2
WHERE M1.Others_data = M2.Max_Others_data
ORDER BY Others_data;
If you want the last row for each Name, then you can give a row number to each row group by the Name and order by Id in descending order.
QUERY
SELECT t1.Id,
t1.Name,
t1.Other_Columns
FROM
(
SELECT Id,
Name,
Other_Columns,
(
CASE Name WHEN #curA
THEN #curRow := #curRow + 1
ELSE #curRow := 1 AND #curA := Name END
) + 1 AS rn
FROM messages t,
(SELECT #curRow := 0, #curA := '') r
ORDER BY Name,Id DESC
)t1
WHERE t1.rn = 1
ORDER BY t1.Id;
SQL Fiddle
If performance is really your concern you can introduce a new column on the table called IsLastInGroup of type BIT.
Set it to true on the columns which are last and maintain it with every row insert/update/delete. Writes will be slower, but you'll benefit on reads. It depends on your use case and I recommend it only if you're read-focused.
So your query will look like:
SELECT * FROM Messages WHERE IsLastInGroup = 1
MariaDB 10.3 and newer using GROUP_CONCAT.
The idea is to use ORDER BY + LIMIT:
SELECT GROUP_CONCAT(id ORDER BY id DESC LIMIT 1) AS id,
name,
GROUP_CONCAT(Other_columns ORDER BY id DESC LIMIT 1) AS Other_columns
FROM t
GROUP BY name;
db<>fiddle demo
How about this:
SELECT DISTINCT ON (name) *
FROM messages
ORDER BY name, id DESC;
I had similar issue (on postgresql tough) and on a 1M records table. This solution takes 1.7s vs 44s produced by the one with LEFT JOIN.
In my case I had to filter the corrispondant of your name field against NULL values, resulting in even better performances by 0.2 secs
Yet another option without subqueries.
This solution uses MySQL LAST_VALUE window function, exploiting Window Function Frame available MySQL tool from .
SELECT DISTINCT
LAST_VALUE(Id)
OVER(PARTITION BY Name
ORDER BY Id
ROWS BETWEEN 0 PRECEDING
AND UNBOUNDED FOLLOWING),
Name,
LAST_VALUE(Other_Columns)
OVER(PARTITION BY Name
ORDER BY Id
ROWS BETWEEN 0 PRECEDING
AND UNBOUNDED FOLLOWING)
FROM
tab
Try it here.
Hope below Oracle query can help:
WITH Temp_table AS
(
Select id, name, othercolumns, ROW_NUMBER() over (PARTITION BY name ORDER BY ID
desc)as rank from messages
)
Select id, name,othercolumns from Temp_table where rank=1
Another approach :
Find the propertie with the max m2_price withing each program (n properties in 1 program) :
select * from properties p
join (
select max(m2_price) as max_price
from properties
group by program_id
) p2 on (p.program_id = p2.program_id)
having p.m2_price = max_price
What about:
select *, max(id) from messages group by name
I have tested it on sqlite and it returns all columns and max id value for all names.
As of MySQL 8.0.14, this can also be achieved using Lateral Derived Tables:
SELECT t.*
FROM messages t
JOIN LATERAL (
SELECT name, MAX(id) AS id
FROM messages t1
WHERE t.name = t1.name
GROUP BY name
) trn ON t.name = trn.name AND t.id = trn.id
db<>fiddle

Alternative of NOT EXISTS in MySQL as query is taking time to execute with NOT EXISTS

Dummy table:
id FileName DateLastSaved
1 Marium.doc 2015-01-01
2 Amna.doc 2016-01-01
3 Marium.doc 2016-01-01
I want the query to return such rows where FileName is unique in the whole table. Rows should be returned for particular date range.
Suppose date ranges are of 2016 only, so third row should not be returned as FileName is not unique.
The query that I have created is:
$presentquery="SELECT * FROM InitialLog i WHERE MDid='$MDid' AND
(DateLastSaved>='$firstdate' AND DateLastSaved<='$presentdate') AND NOT
EXISTS (SELECT id FROM InitialLog i2 WHERE i2.id<i.id AND i.FileName=i2.FileName )";
(Where $firstdate and $presentdate are 2 dates for date ranges)
The query is returning the accurate results but it's taking time to execute. Is there any other way that I can rewrite this query??
(I have table with many rows)
I put this query together and it returns the results very quickly.
Select *
FROM foo
Where (`datelastsaved` > '2015-12-31' && `datelastsaved` < '2017-01-01')
AND `filename` NOT IN (
Select `filename`
FROM foo
GROUP BY `filename`
HAVING COUNT(*) > 1);
The first part is your normal select statement with the where clauses to filter on the dates.
The second part is the NOT IN where the select statement finds all of the ones with duplicate filenames.
Select `filename` FROM foo GROUP BY `filename` HAVING COUNT(*) > 1)
You can get the same logic using a LEFT JOIN and looking for nulls, that is,
$presentquery = "SELECT DISTINCT i.* FROM InitialLog i
LEFT JOIN InitialLog i2 ON i2.id<i.id AND i.FileName=i2.FileName
WHERE i.MDid='$MDid'
AND i.DateLastSaved>='$firstdate'
AND i.DateLastSaved<='$presentdate'
AND i2.id IS NULL";
This way you are doing a single join rather than subquerying against each value in i.
It looks like you are trying to get the data associated with the first occurrence of each file name, this should work:
SELECT *
FROM InitialLog i
WHERE MDid='$MDid'
AND DateLastSaved>='$firstdate'
AND DateLastSaved<='$presentdate'
AND id IN (SELECT MIN(id) FROM InitialLog GROUP BY FileName)
;
Alternatively, you can do a JOIN with the same subquery instead:
SELECT i.*
FROM InitialLog AS i
INNER JOIN (SELECT MIN(id) AS id
FROM InitialLog
GROUP BY FileName
) AS firsts USING (id)
WHERE i.MDid='$MDid'
AND i.DateLastSaved>='$firstdate'
AND i.DateLastSaved<='$presentdate'
;

Joined two tables & get results

I'm having some trouble with joining two mysql tables & getting the result as I want.
I have two tables , users table & times table. Users table is having id & name . time table is having user_id & minutesSpent. I want to get the times for all the users with name & times column as the result set. But for some specific dates all user ids are not in the times table. So I need null value as for such users.
I have tried several queries, but every time I'm getting times only for user_ids available in the times table. Not getting other user_id's times as null :(
SELECT `users`.`name`,SUM(`times`.`minutesSpent`) AS `Total`
FROM `users`
LEFT OUTER JOIN `times` ON `users`.`id` = `times`.`user_id`
WHERE DATE(`date`) = '2015-06-03'
GROUP BY `users`.`name`
& I have tried this query as well
SELECT `users`.`name`,SUM(`times`.`minutesSpent`) AS `Total` ,CASE WHEN times.user_id IS NULL THEN 0 ELSE 1 END
FROM `users`
INNER JOIN `times` ON `users`.`id` = `times`.`user_id` AND `users`.`isTimeEnable` = 0
WHERE DATE(`date`) = '2015-06-03'
GROUP BY `users`.`name`
if any one help me with this, it would be great .
Thanks
Your WHERE clause is turning your left join into an inner join. Try this:
SELECT `users`.`name`,SUM(`times`.`minutesSpent`) AS `Total`
FROM `users`
LEFT OUTER JOIN `times` ON `users`.`id` = `times`.`user_id`
AND DATE(`date`) = '2015-06-03'
GROUP BY `users`.`name`
Incidentally, if less typing (well 20 characters) and better performance is your thing then consider the following:
SELECT u.name
, SUM(t.minutesSpent) Total
FROM users u
LEFT
JOIN times t
ON u.id = t.user_id
AND t.date BETWEEN '2015-06-03 00:00:00' AND '2015-06-03 23:59:59'
GROUP
BY u.name;

MySQL select distinct rows and the latest one based on timestamp [duplicate]

There is a table messages that contains data as shown below:
Id Name Other_Columns
-------------------------
1 A A_data_1
2 A A_data_2
3 A A_data_3
4 B B_data_1
5 B B_data_2
6 C C_data_1
If I run a query select * from messages group by name, I will get the result as:
1 A A_data_1
4 B B_data_1
6 C C_data_1
What query will return the following result?
3 A A_data_3
5 B B_data_2
6 C C_data_1
That is, the last record in each group should be returned.
At present, this is the query that I use:
SELECT
*
FROM (SELECT
*
FROM messages
ORDER BY id DESC) AS x
GROUP BY name
But this looks highly inefficient. Any other ways to achieve the same result?
MySQL 8.0 now supports windowing functions, like almost all popular SQL implementations. With this standard syntax, we can write greatest-n-per-group queries:
WITH ranked_messages AS (
SELECT m.*, ROW_NUMBER() OVER (PARTITION BY name ORDER BY id DESC) AS rn
FROM messages AS m
)
SELECT * FROM ranked_messages WHERE rn = 1;
This and other approaches to finding groupwise maximal rows are illustrated in the MySQL manual.
Below is the original answer I wrote for this question in 2009:
I write the solution this way:
SELECT m1.*
FROM messages m1 LEFT JOIN messages m2
ON (m1.name = m2.name AND m1.id < m2.id)
WHERE m2.id IS NULL;
Regarding performance, one solution or the other can be better, depending on the nature of your data. So you should test both queries and use the one that is better at performance given your database.
For example, I have a copy of the StackOverflow August data dump. I'll use that for benchmarking. There are 1,114,357 rows in the Posts table. This is running on MySQL 5.0.75 on my Macbook Pro 2.40GHz.
I'll write a query to find the most recent post for a given user ID (mine).
First using the technique shown by #Eric with the GROUP BY in a subquery:
SELECT p1.postid
FROM Posts p1
INNER JOIN (SELECT pi.owneruserid, MAX(pi.postid) AS maxpostid
FROM Posts pi GROUP BY pi.owneruserid) p2
ON (p1.postid = p2.maxpostid)
WHERE p1.owneruserid = 20860;
1 row in set (1 min 17.89 sec)
Even the EXPLAIN analysis takes over 16 seconds:
+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 76756 | |
| 1 | PRIMARY | p1 | eq_ref | PRIMARY,PostId,OwnerUserId | PRIMARY | 8 | p2.maxpostid | 1 | Using where |
| 2 | DERIVED | pi | index | NULL | OwnerUserId | 8 | NULL | 1151268 | Using index |
+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+
3 rows in set (16.09 sec)
Now produce the same query result using my technique with LEFT JOIN:
SELECT p1.postid
FROM Posts p1 LEFT JOIN posts p2
ON (p1.owneruserid = p2.owneruserid AND p1.postid < p2.postid)
WHERE p2.postid IS NULL AND p1.owneruserid = 20860;
1 row in set (0.28 sec)
The EXPLAIN analysis shows that both tables are able to use their indexes:
+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
| 1 | SIMPLE | p1 | ref | OwnerUserId | OwnerUserId | 8 | const | 1384 | Using index |
| 1 | SIMPLE | p2 | ref | PRIMARY,PostId,OwnerUserId | OwnerUserId | 8 | const | 1384 | Using where; Using index; Not exists |
+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
2 rows in set (0.00 sec)
Here's the DDL for my Posts table:
CREATE TABLE `posts` (
`PostId` bigint(20) unsigned NOT NULL auto_increment,
`PostTypeId` bigint(20) unsigned NOT NULL,
`AcceptedAnswerId` bigint(20) unsigned default NULL,
`ParentId` bigint(20) unsigned default NULL,
`CreationDate` datetime NOT NULL,
`Score` int(11) NOT NULL default '0',
`ViewCount` int(11) NOT NULL default '0',
`Body` text NOT NULL,
`OwnerUserId` bigint(20) unsigned NOT NULL,
`OwnerDisplayName` varchar(40) default NULL,
`LastEditorUserId` bigint(20) unsigned default NULL,
`LastEditDate` datetime default NULL,
`LastActivityDate` datetime default NULL,
`Title` varchar(250) NOT NULL default '',
`Tags` varchar(150) NOT NULL default '',
`AnswerCount` int(11) NOT NULL default '0',
`CommentCount` int(11) NOT NULL default '0',
`FavoriteCount` int(11) NOT NULL default '0',
`ClosedDate` datetime default NULL,
PRIMARY KEY (`PostId`),
UNIQUE KEY `PostId` (`PostId`),
KEY `PostTypeId` (`PostTypeId`),
KEY `AcceptedAnswerId` (`AcceptedAnswerId`),
KEY `OwnerUserId` (`OwnerUserId`),
KEY `LastEditorUserId` (`LastEditorUserId`),
KEY `ParentId` (`ParentId`),
CONSTRAINT `posts_ibfk_1` FOREIGN KEY (`PostTypeId`) REFERENCES `posttypes` (`PostTypeId`)
) ENGINE=InnoDB;
Note to commenters: If you want another benchmark with a different version of MySQL, a different dataset, or different table design, feel free to do it yourself. I have shown the technique above. Stack Overflow is here to show you how to do software development work, not to do all the work for you.
UPD: 2017-03-31, the version 5.7.5 of MySQL made the ONLY_FULL_GROUP_BY switch enabled by default (hence, non-deterministic GROUP BY queries became disabled). Moreover, they updated the GROUP BY implementation and the solution might not work as expected anymore even with the disabled switch. One needs to check.
Bill Karwin's solution above works fine when item count within groups is rather small, but the performance of the query becomes bad when the groups are rather large, since the solution requires about n*n/2 + n/2 of only IS NULL comparisons.
I made my tests on a InnoDB table of 18684446 rows with 1182 groups. The table contains testresults for functional tests and has the (test_id, request_id) as the primary key. Thus, test_id is a group and I was searching for the last request_id for each test_id.
Bill's solution has already been running for several hours on my dell e4310 and I do not know when it is going to finish even though it operates on a coverage index (hence using index in EXPLAIN).
I have a couple of other solutions that are based on the same ideas:
if the underlying index is BTREE index (which is usually the case), the largest (group_id, item_value) pair is the last value within each group_id, that is the first for each group_id if we walk through the index in descending order;
if we read the values which are covered by an index, the values are read in the order of the index;
each index implicitly contains primary key columns appended to that (that is the primary key is in the coverage index). In solutions below I operate directly on the primary key, in you case, you will just need to add primary key columns in the result.
in many cases it is much cheaper to collect the required row ids in the required order in a subquery and join the result of the subquery on the id. Since for each row in the subquery result MySQL will need a single fetch based on primary key, the subquery will be put first in the join and the rows will be output in the order of the ids in the subquery (if we omit explicit ORDER BY for the join)
3 ways MySQL uses indexes is a great article to understand some details.
Solution 1
This one is incredibly fast, it takes about 0,8 secs on my 18M+ rows:
SELECT test_id, MAX(request_id) AS request_id
FROM testresults
GROUP BY test_id DESC;
If you want to change the order to ASC, put it in a subquery, return the ids only and use that as the subquery to join to the rest of the columns:
SELECT test_id, request_id
FROM (
SELECT test_id, MAX(request_id) AS request_id
FROM testresults
GROUP BY test_id DESC) as ids
ORDER BY test_id;
This one takes about 1,2 secs on my data.
Solution 2
Here is another solution that takes about 19 seconds for my table:
SELECT test_id, request_id
FROM testresults, (SELECT #group:=NULL) as init
WHERE IF(IFNULL(#group, -1)=#group:=test_id, 0, 1)
ORDER BY test_id DESC, request_id DESC
It returns tests in descending order as well. It is much slower since it does a full index scan but it is here to give you an idea how to output N max rows for each group.
The disadvantage of the query is that its result cannot be cached by the query cache.
Use your subquery to return the correct grouping, because you're halfway there.
Try this:
select
a.*
from
messages a
inner join
(select name, max(id) as maxid from messages group by name) as b on
a.id = b.maxid
If it's not id you want the max of:
select
a.*
from
messages a
inner join
(select name, max(other_col) as other_col
from messages group by name) as b on
a.name = b.name
and a.other_col = b.other_col
This way, you avoid correlated subqueries and/or ordering in your subqueries, which tend to be very slow/inefficient.
I arrived at a different solution, which is to get the IDs for the last post within each group, then select from the messages table using the result from the first query as the argument for a WHERE x IN construct:
SELECT id, name, other_columns
FROM messages
WHERE id IN (
SELECT MAX(id)
FROM messages
GROUP BY name
);
I don't know how this performs compared to some of the other solutions, but it worked spectacularly for my table with 3+ million rows. (4 second execution with 1200+ results)
This should work both on MySQL and SQL Server.
Solution by sub query fiddle Link
select * from messages where id in
(select max(id) from messages group by Name)
Solution By join condition fiddle link
select m1.* from messages m1
left outer join messages m2
on ( m1.id<m2.id and m1.name=m2.name )
where m2.id is null
Reason for this post is to give fiddle link only.
Same SQL is already provided in other answers.
An approach with considerable speed is as follows.
SELECT *
FROM messages a
WHERE Id = (SELECT MAX(Id) FROM messages WHERE a.Name = Name)
Result
Id Name Other_Columns
3 A A_data_3
5 B B_data_2
6 C C_data_1
We will look at how you can use MySQL at getting the last record in a Group By of records. For example if you have this result set of posts.
id
category_id
post_title
1
1
Title 1
2
1
Title 2
3
1
Title 3
4
2
Title 4
5
2
Title 5
6
3
Title 6
I want to be able to get the last post in each category which are Title 3, Title 5 and Title 6. To get the posts by the category you will use the MySQL Group By keyboard.
select * from posts group by category_id
But the results we get back from this query is.
id
category_id
post_title
1
1
Title 1
4
2
Title 4
6
3
Title 6
The group by will always return the first record in the group on the result set.
SELECT id, category_id, post_title
FROM posts
WHERE id IN (
SELECT MAX(id)
FROM posts
GROUP BY category_id );
This will return the posts with the highest IDs in each group.
id
category_id
post_title
3
1
Title 3
5
2
Title 5
6
3
Title 6
Reference Click Here
Here are two suggestions. First, if mysql supports ROW_NUMBER(), it's very simple:
WITH Ranked AS (
SELECT Id, Name, OtherColumns,
ROW_NUMBER() OVER (
PARTITION BY Name
ORDER BY Id DESC
) AS rk
FROM messages
)
SELECT Id, Name, OtherColumns
FROM messages
WHERE rk = 1;
I'm assuming by "last" you mean last in Id order. If not, change the ORDER BY clause of the ROW_NUMBER() window accordingly. If ROW_NUMBER() isn't available, this is another solution:
Second, if it doesn't, this is often a good way to proceed:
SELECT
Id, Name, OtherColumns
FROM messages
WHERE NOT EXISTS (
SELECT * FROM messages as M2
WHERE M2.Name = messages.Name
AND M2.Id > messages.Id
)
In other words, select messages where there is no later-Id message with the same Name.
Clearly there are lots of different ways of getting the same results, your question seems to be what is an efficient way of getting the last results in each group in MySQL. If you are working with huge amounts of data and assuming you are using InnoDB with even the latest versions of MySQL (such as 5.7.21 and 8.0.4-rc) then there might not be an efficient way of doing this.
We sometimes need to do this with tables with even more than 60 million rows.
For these examples I will use data with only about 1.5 million rows where the queries would need to find results for all groups in the data. In our actual cases we would often need to return back data from about 2,000 groups (which hypothetically would not require examining very much of the data).
I will use the following tables:
CREATE TABLE temperature(
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
groupID INT UNSIGNED NOT NULL,
recordedTimestamp TIMESTAMP NOT NULL,
recordedValue INT NOT NULL,
INDEX groupIndex(groupID, recordedTimestamp),
PRIMARY KEY (id)
);
CREATE TEMPORARY TABLE selected_group(id INT UNSIGNED NOT NULL, PRIMARY KEY(id));
The temperature table is populated with about 1.5 million random records, and with 100 different groups.
The selected_group is populated with those 100 groups (in our cases this would normally be less than 20% for all of the groups).
As this data is random it means that multiple rows can have the same recordedTimestamps. What we want is to get a list of all of the selected groups in order of groupID with the last recordedTimestamp for each group, and if the same group has more than one matching row like that then the last matching id of those rows.
If hypothetically MySQL had a last() function which returned values from the last row in a special ORDER BY clause then we could simply do:
SELECT
last(t1.id) AS id,
t1.groupID,
last(t1.recordedTimestamp) AS recordedTimestamp,
last(t1.recordedValue) AS recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.groupID = g.id
ORDER BY t1.recordedTimestamp, t1.id
GROUP BY t1.groupID;
which would only need to examine a few 100 rows in this case as it doesn't use any of the normal GROUP BY functions. This would execute in 0 seconds and hence be highly efficient.
Note that normally in MySQL we would see an ORDER BY clause following the GROUP BY clause however this ORDER BY clause is used to determine the ORDER for the last() function, if it was after the GROUP BY then it would be ordering the GROUPS. If no GROUP BY clause is present then the last values will be the same in all of the returned rows.
However MySQL does not have this so let's look at different ideas of what it does have and prove that none of these are efficient.
Example 1
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.id = (
SELECT t2.id
FROM temperature t2
WHERE t2.groupID = g.id
ORDER BY t2.recordedTimestamp DESC, t2.id DESC
LIMIT 1
);
This examined 3,009,254 rows and took ~0.859 seconds on 5.7.21 and slightly longer on 8.0.4-rc
Example 2
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM temperature t1
INNER JOIN (
SELECT max(t2.id) AS id
FROM temperature t2
INNER JOIN (
SELECT t3.groupID, max(t3.recordedTimestamp) AS recordedTimestamp
FROM selected_group g
INNER JOIN temperature t3 ON t3.groupID = g.id
GROUP BY t3.groupID
) t4 ON t4.groupID = t2.groupID AND t4.recordedTimestamp = t2.recordedTimestamp
GROUP BY t2.groupID
) t5 ON t5.id = t1.id;
This examined 1,505,331 rows and took ~1.25 seconds on 5.7.21 and slightly longer on 8.0.4-rc
Example 3
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM temperature t1
WHERE t1.id IN (
SELECT max(t2.id) AS id
FROM temperature t2
INNER JOIN (
SELECT t3.groupID, max(t3.recordedTimestamp) AS recordedTimestamp
FROM selected_group g
INNER JOIN temperature t3 ON t3.groupID = g.id
GROUP BY t3.groupID
) t4 ON t4.groupID = t2.groupID AND t4.recordedTimestamp = t2.recordedTimestamp
GROUP BY t2.groupID
)
ORDER BY t1.groupID;
This examined 3,009,685 rows and took ~1.95 seconds on 5.7.21 and slightly longer on 8.0.4-rc
Example 4
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.id = (
SELECT max(t2.id)
FROM temperature t2
WHERE t2.groupID = g.id AND t2.recordedTimestamp = (
SELECT max(t3.recordedTimestamp)
FROM temperature t3
WHERE t3.groupID = g.id
)
);
This examined 6,137,810 rows and took ~2.2 seconds on 5.7.21 and slightly longer on 8.0.4-rc
Example 5
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM (
SELECT
t2.id,
t2.groupID,
t2.recordedTimestamp,
t2.recordedValue,
row_number() OVER (
PARTITION BY t2.groupID ORDER BY t2.recordedTimestamp DESC, t2.id DESC
) AS rowNumber
FROM selected_group g
INNER JOIN temperature t2 ON t2.groupID = g.id
) t1 WHERE t1.rowNumber = 1;
This examined 6,017,808 rows and took ~4.2 seconds on 8.0.4-rc
Example 6
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM (
SELECT
last_value(t2.id) OVER w AS id,
t2.groupID,
last_value(t2.recordedTimestamp) OVER w AS recordedTimestamp,
last_value(t2.recordedValue) OVER w AS recordedValue
FROM selected_group g
INNER JOIN temperature t2 ON t2.groupID = g.id
WINDOW w AS (
PARTITION BY t2.groupID
ORDER BY t2.recordedTimestamp, t2.id
RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
)
) t1
GROUP BY t1.groupID;
This examined 6,017,908 rows and took ~17.5 seconds on 8.0.4-rc
Example 7
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.groupID = g.id
LEFT JOIN temperature t2
ON t2.groupID = g.id
AND (
t2.recordedTimestamp > t1.recordedTimestamp
OR (t2.recordedTimestamp = t1.recordedTimestamp AND t2.id > t1.id)
)
WHERE t2.id IS NULL
ORDER BY t1.groupID;
This one was taking forever so I had to kill it.
Here is another way to get the last related record using GROUP_CONCAT with order by and SUBSTRING_INDEX to pick one of the record from the list
SELECT
`Id`,
`Name`,
SUBSTRING_INDEX(
GROUP_CONCAT(
`Other_Columns`
ORDER BY `Id` DESC
SEPARATOR '||'
),
'||',
1
) Other_Columns
FROM
messages
GROUP BY `Name`
Above query will group the all the Other_Columns that are in same Name group and using ORDER BY id DESC will join all the Other_Columns in a specific group in descending order with the provided separator in my case i have used || ,using SUBSTRING_INDEX over this list will pick the first one
Fiddle Demo
Hi #Vijay Dev if your table messages contains Id which is auto increment primary key then to fetch the latest record basis on the primary key your query should read as below:
SELECT m1.* FROM messages m1 INNER JOIN (SELECT max(Id) as lastmsgId FROM messages GROUP BY Name) m2 ON m1.Id=m2.lastmsgId
I've not yet tested with large DB but I think this could be faster than joining tables:
SELECT *, Max(Id) FROM messages GROUP BY Name
SELECT
column1,
column2
FROM
table_name
WHERE id IN
(SELECT
MAX(id)
FROM
table_name
GROUP BY column1)
ORDER BY column1 ;
You can take view from here as well.
http://sqlfiddle.com/#!9/ef42b/9
FIRST SOLUTION
SELECT d1.ID,Name,City FROM Demo_User d1
INNER JOIN
(SELECT MAX(ID) AS ID FROM Demo_User GROUP By NAME) AS P ON (d1.ID=P.ID);
SECOND SOLUTION
SELECT * FROM (SELECT * FROM Demo_User ORDER BY ID DESC) AS T GROUP BY NAME ;
If you need the most recent or oldest record of a text column in a grouped query, and you would rather not use a subquery, you can do this...
Ex. You have a list of movies and need to get the count in the series and the latest movie
id
series
name
1
Star Wars
A New hope
2
Star Wars
The Empire Strikes Back
3
Star Wars
Return of The Jedi
SELECT COUNT(id), series, SUBSTRING(MAX(CONCAT(id, name)), LENGTH(id) + 1),
FROM Movies
GROUP BY series
This returns...
id
series
name
3
Star Wars
Return of The Jedi
MAX will return the row with the highest value, so by concatenating the id to the name, you now will get the newest record, then just strip off the id for your final result.
More efficient than using a subquery.
So for the given example:
SELECT MAX(Id), Name, SUBSTRING(MAX(CONCAT(Id, Other_Columns)), LENGTH(Id) + 1),
FROM messages
GROUP BY Name
Happy coding, and "May The Force Be With You" :)
Try this:
SELECT jos_categories.title AS name,
joined .catid,
joined .title,
joined .introtext
FROM jos_categories
INNER JOIN (SELECT *
FROM (SELECT `title`,
catid,
`created`,
introtext
FROM `jos_content`
WHERE `sectionid` = 6
ORDER BY `id` DESC) AS yes
GROUP BY `yes`.`catid` DESC
ORDER BY `yes`.`created` DESC) AS joined
ON( joined.catid = jos_categories.id )
Here is my solution:
SELECT
DISTINCT NAME,
MAX(MESSAGES) OVER(PARTITION BY NAME) MESSAGES
FROM MESSAGE;
SELECT * FROM table_name WHERE primary_key IN (SELECT MAX(primary_key) FROM table_name GROUP BY column_name )
**
Hi, this query might help :
**
SELECT
*
FROM
message
WHERE
`Id` IN (
SELECT
MAX(`Id`)
FROM
message
GROUP BY
`Name`
)
ORDER BY
`Id` DESC
i find best solution in https://dzone.com/articles/get-last-record-in-each-mysql-group
select * from `data` where `id` in (select max(`id`) from `data` group by `name_id`)
The below query will work fine as per your question.
SELECT M1.*
FROM MESSAGES M1,
(
SELECT SUBSTR(Others_data,1,2),MAX(Others_data) AS Max_Others_data
FROM MESSAGES
GROUP BY 1
) M2
WHERE M1.Others_data = M2.Max_Others_data
ORDER BY Others_data;
If you want the last row for each Name, then you can give a row number to each row group by the Name and order by Id in descending order.
QUERY
SELECT t1.Id,
t1.Name,
t1.Other_Columns
FROM
(
SELECT Id,
Name,
Other_Columns,
(
CASE Name WHEN #curA
THEN #curRow := #curRow + 1
ELSE #curRow := 1 AND #curA := Name END
) + 1 AS rn
FROM messages t,
(SELECT #curRow := 0, #curA := '') r
ORDER BY Name,Id DESC
)t1
WHERE t1.rn = 1
ORDER BY t1.Id;
SQL Fiddle
If performance is really your concern you can introduce a new column on the table called IsLastInGroup of type BIT.
Set it to true on the columns which are last and maintain it with every row insert/update/delete. Writes will be slower, but you'll benefit on reads. It depends on your use case and I recommend it only if you're read-focused.
So your query will look like:
SELECT * FROM Messages WHERE IsLastInGroup = 1
MariaDB 10.3 and newer using GROUP_CONCAT.
The idea is to use ORDER BY + LIMIT:
SELECT GROUP_CONCAT(id ORDER BY id DESC LIMIT 1) AS id,
name,
GROUP_CONCAT(Other_columns ORDER BY id DESC LIMIT 1) AS Other_columns
FROM t
GROUP BY name;
db<>fiddle demo
How about this:
SELECT DISTINCT ON (name) *
FROM messages
ORDER BY name, id DESC;
I had similar issue (on postgresql tough) and on a 1M records table. This solution takes 1.7s vs 44s produced by the one with LEFT JOIN.
In my case I had to filter the corrispondant of your name field against NULL values, resulting in even better performances by 0.2 secs
Yet another option without subqueries.
This solution uses MySQL LAST_VALUE window function, exploiting Window Function Frame available MySQL tool from .
SELECT DISTINCT
LAST_VALUE(Id)
OVER(PARTITION BY Name
ORDER BY Id
ROWS BETWEEN 0 PRECEDING
AND UNBOUNDED FOLLOWING),
Name,
LAST_VALUE(Other_Columns)
OVER(PARTITION BY Name
ORDER BY Id
ROWS BETWEEN 0 PRECEDING
AND UNBOUNDED FOLLOWING)
FROM
tab
Try it here.
Hope below Oracle query can help:
WITH Temp_table AS
(
Select id, name, othercolumns, ROW_NUMBER() over (PARTITION BY name ORDER BY ID
desc)as rank from messages
)
Select id, name,othercolumns from Temp_table where rank=1
Another approach :
Find the propertie with the max m2_price withing each program (n properties in 1 program) :
select * from properties p
join (
select max(m2_price) as max_price
from properties
group by program_id
) p2 on (p.program_id = p2.program_id)
having p.m2_price = max_price
What about:
select *, max(id) from messages group by name
I have tested it on sqlite and it returns all columns and max id value for all names.
As of MySQL 8.0.14, this can also be achieved using Lateral Derived Tables:
SELECT t.*
FROM messages t
JOIN LATERAL (
SELECT name, MAX(id) AS id
FROM messages t1
WHERE t.name = t1.name
GROUP BY name
) trn ON t.name = trn.name AND t.id = trn.id
db<>fiddle

Complex MySQL Group By

I have a table that contains results from football matches between two teams. Firstly I want to have a drop down menu of the teams. This should be simply something like SELECT * FROM table GROUP BY Team_A
But a team could be missed if they only ever appear in the Team_B column. Is it possible to group columns Team_A and Team_B so that I could output all unique teams across both columns?
If you want all the names, then use union. This also removes duplicates:
select TeamA from table union
select TeamB from table
This will give you the right data, but . . .
select team_a as team_name
from test
union
select team_b
from test
order by team_name;
You should probably have foreign key references to a table of teams. If you had that, with the obligatory unique constraint on the team's name, you'd just need to
select team_name
from teams
order by team_name;
Use a UNION like this:
SELECT Team_A AS TeamName FROM table
UNION ALL
SELECT Team_B AS TeamName FROM table
to get the distinct listing of teams. But I have to say, you really need to restructure your database. You should have a table like this:
CREATE TABLE Teams (
ID INT PRIMARY KEY IDENTITY(1, 1),
Name VARCHAR(50) NOT NULL UNIQUE
)
and then a table like this:
CREATE TABLE Scores (
ID INT PRIMARY KEY IDENTITY(1, 1),
HomeTeamId INT NOT NULL,
AwayTeamId INT NOT NULL,
HomeTeamScore INT NOT NULL,
AwayTeamScore INT NOT NULL
)
and that would allow not only a more straight forward selection of teams:
SELECT Name FROM Teams
but it would also build some real database integrity because you could build foreign keys and you wouldn't be using simple string values to indicate key values.
Don't you just mean:
group by Team_A, Team_B
?
This will group by the combination of Team_A and Team_B
edit: To end up with TWO columns, each having a unique list of teams that already existed in that column (exactly as described by OP):
SELECT a.*, b.* FROM
(SELECT Team_A, #ida := ifnull(#ida,0) + 1 AS ida FROM table GROUP BY Team_A) AS a
LEFT JOIN
(SELECT Team_B, #idb := ifnull(#idb,0) + 1 AS idb FROM table GROUP BY Team_B) AS b
ON a.ida=b.idb
and isnull(#ida := null) and isnull(#idb := null) -- reset #ida and #idb
UNION ALL -- turn the left and right join into full outer join
SELECT c.*, d.* FROM
(SELECT Team_A, #idc := ifnull(#idc,0) + 1 AS idc FROM table GROUP BY Team_A) AS c
RIGHT JOIN
(SELECT Team_B, #idd := ifnull(#idd,0) + 1 AS idd FROM table GROUP BY Team_B) AS d
ON c.idc=d.idd
and isnull(#idc := null) and isnull(#idd := null) -- reset #idc and #idd
WHERE c.idc is null -- prevent doubles in the full outer join

Categories