MySQL 5.7 group by latest record - mysql

Very simple select, but I'm confused:
create table users (id int, type int);
insert into users values(1, 100), (2, 101), (3, 100);
mysql> select * from users;
+------+------+
| id | type |
+------+------+
| 1 | 100 |
| 2 | 101 |
| 3 | 100 |
+------+------+
I want to get the result:
+------+------+
| id | type |
+------+------+
| 3 | 100 |
| 2 | 101 |
+------+------+
My query is:
MySQL version 5.7 returns:
mysql> select * from (select * from users order by id desc) as t group by type;
+------+------+
| id | type |
+------+------+
| 1 | 100 |
| 2 | 101 |
+------+------+
At MySQL 5.5 it works as expected. sql_mode is NULL
Thanks for the replies. Extended table to have more clear results:
create table users (id int, type int, level int);
insert into users values(1, 100, 1000), (2, 101, 1001), (3, 100, 1002);
mysql> select max(id), type, level from users group by type;
+---------+------+-------+
| max(id) | type | level |
+---------+------+-------+
| 3 | 100 | 1000 | <- expected 1002
| 2 | 101 | 1001 |
+---------+------+-------+
This works:
mysql> SELECT t1.* FROM users t1 INNER JOIN (SELECT type, MAX(id) AS max_id FROM users GROUP BY type) t2 ON t1.type = t2.type AND t1.id = t2.max_id ORDER BY id DESC;
+------+------+-------+
| id | type | level |
+------+------+-------+
| 3 | 100 | 1002 |
| 2 | 101 | 1001 |
+------+------+-------+

You should use an aggregation function and group by
select max(id), type
from users
group by type;
order by id desc
the result in mysql5.5 not based on group by is casual .. (this beahvior is deprecated in sql and not allowed in default sql_mode in 5.7)

For completeness I can offer the following query which would work if you wanted to return entire records with an arbitrary number of columns.
SELECT t1.*
FROM users t1
INNER JOIN
(
SELECT type, MAX(id) AS max_id
FROM users
GROUP BY type
) t2
ON t1.type = t2.type AND
t1.id = t2.max_id

GROUP BY without aggregation is supported in very few SQL dialects (perhaps even only MySQL); the fact that it worked as you expected in 5.5 was (for the most part) just convenient, not guaranteed by MySQL. Officially, it grabs an effectively random value (encountered from the rows of the group) from the non-grouped, non-aggregated fields.

Related

How do I get the first n records, per foreign key with MySQL?

I have the following table:
+----+-----------+------+
| id | table2_id | type |
+----+-----------+------+
| 1 | 100 | A |
| 2 | 100 | B |
| 3 | 100 | C |
| 4 | 100 | A |
| 5 | 250 | A |
+----+-----------+------+
I need a select statement that would get all the records before the first occurrence of type C, per table2_id.
So I want records 1, 2, and 5
I'd do this in code with a loop, but I need to do it in MySQL specifically.
If you are running MySQL 8.0, you can do this with window functions:
select *
from (
select t.*,
min(case when type = 'C' then id end) over(partition by table2_id) min_id
from mytable t
) t
where min_id is null or id < min_id
In all versions, you could use not exists:
select t.*
from mytable t
where not exists (
select 1
from mytable t1
where t1.table2_id = t.table2_id and t1.id <= t.id and t1.type = 'C'
)

SQL get value based on corresponding min/max(value) from column in another related table

I have the following two tables related by the ID column as the Primary Key. My Goal is to query the values from the "Name" column in Table 1 which correspond to the User_id with the Max and Min "Score" Column Values from Table 2.
Table 1:
| ID | Name |
|----|------|
| 1 | Foo |
| 2 | Bar |
| 3 | Zoo |
| 4 | Bar |
| 5 | Foo |
| 6 | Zar |
Table 2:
| ID | Score |
|----|-------|
| 1 | 98 |
| 2 | 67 |
| 3 | 86 |
| 4 | 59 |
| 5 | 75 |
| 6 | 73 |
The final output should give me something like this:
| Name | Score |
|------|-------|
| Foo | 98 |
| Bar | 59 |
You can try the below -
select name, score
from table1 t1 join table2 t2 on t1.id=t2.id
where
score=(select max(score) from t2)
or
score=(select min(score) from t2)
(
SELECT name, score
FROM table1 NATURAL JOIN table2
ORDER BY 2 ASC LIMIT 1
)
UNION ALL
(
SELECT name, score
FROM table1 NATURAL JOIN table2
ORDER BY 2 DESC LIMIT 1
)
If you are running MySQL 8.0, you can use window functions:
select t1.name, t2.score
from table1 t1
inner join (
select t2.*,
rank() over(order by score) rn_asc,
rank() over(order by score desc) rn_desc
from table2 t2
) t2 on t2.id = t1.id
where 1 in (rn_asc, rn_desc)
The idea is to rank records of table2 by increasing and decreasing score, and use that information for filtering. Note that this allows top and bottom ties.

Group by priority

I have this
+---------+--------+-------+
| article | name |status |
+---------+--------+-------+
| 0001 | A | enable|
| 0002 | A | temp |
| 0003 | B | enable|
| 0004 | C | enable|
+---------+--------+-------+
I want to select all from this table 'product' but I want to group by name and if there is a status temp I want to ignore the enable status and display only the product with the temp status
This result after query will be :
+---------+--------+-------+
| article | name |status |
+---------+--------+-------+
| 0002 | A | temp |
| 0003 | B | enable|
| 0004 | C | enable|
+---------+--------+-------+
Could you help me to build this query ?
If find at least one temp in group, show it. Else show enable
select article, name, if(sum(status='temp'), 'temp', 'enable')
from thetable
group by name
To get article corresponding to temp status, use such query
select * from table1
where status = 'temp'
union
select * from table1
where name not in (select distinct name from table1 where status = 'temp' )
Try this, hope help for you;)
SQL Fiddle
MySQL 5.6 Schema:
CREATE TABLE table1
(`article` int, `name` varchar(1), `status` varchar(6))
;
INSERT INTO table1
(`article`, `name`, `status`)
VALUES
(0001, 'A', 'enable'),
(0002, 'A', 'temp'),
(0003, 'B', 'enable'),
(0004, 'C', 'enable')
;
Query 1:
select t1.*
from table1 t1
inner join (
select count(distinct status) cnt, name, group_concat(status) as names from table1 group by name
) t2 on t1.name = t2.name
and (t2.cnt = 1 or (find_in_set('temp', names) > 0 and t1.status = 'temp'))
group by t1.name, t1.status
Results:
| article | name | status |
|---------|------|--------|
| 2 | A | temp |
| 3 | B | enable |
| 4 | C | enable |

MySQL Select COUNT(*) and Row with Max Date

I am trying to select the COUNT(*) of deals grouped by seller along with their most recent product and the date that it was added. However, for some reason it seems to keep ordering the deals by creation date, ascending even when I try subqueries to prevent that. Here is an example table:
------------------------------------------------
| ID | Provider | URL | CreateDate |
------------------------------------------------
| 1 | Prov1 | http://ex.com/1 | 2015-05-10 |
| 2 | Prov1 | http://ex.com/2 | 2015-06-10 |
| 3 | Prov1 | http://ex.com/3 | 2015-07-10 |
| 4 | Prov2 | http://ex.com/4 | 2015-05-10 |
| 5 | Prov2 | http://ex.com/5 | 2015-06-10 |
------------------------------------------------
I am looking to return the following:
-----------------------------------------------------------
| ID | COUNT(*) | Provider | URL | CreateDate |
-----------------------------------------------------------
| 3 | 3 | Prov1 | http://ex.com/3 | 2015-07-10 |
| 5 | 2 | Prov2 | http://ex.com/5 | 2015-06-10 |
-----------------------------------------------------------
My current query is:
SELECT ID,COUNT(*),Provider,CreateDate,URL
FROM (SELECT * FROM products ORDER BY CreateDate DESC)
GROUP BY Provider;
But this doesn't seem to work. Does anyone have a suggestion?
EDIT
Thank you all for the great answers. What is extremely strange to me is that while they seem to work in a SQL fiddle, they fail on my database server. For example using the following on the server provides the following:
mysql> INSERT INTO products
-> (`ID`, `Provider`, `URL`, `CreateDate`)
-> VALUES
-> (1, 'Prov1', 'http://ex.com/1','2015-05-10'),
-> (2, 'Prov1', 'http://ex.com/2','2015-06-10'),
-> (3, 'Prov1', 'http://ex.com/3','2015-07-10'),
-> (4, 'Prov2', 'http://ex.com/4','2015-05-10'),
-> (5, 'Prov2', 'http://ex.com/5','2015-06-10')
-> ;
Query OK, 5 rows affected (0.06 sec)
Records: 5 Duplicates: 0 Warnings: 0
mysql> SELECT ID,COUNT(*),Provider,CreateDate,URL
-> FROM (SELECT * FROM products ORDER BY CreateDate DESC) t1
-> GROUP BY Provider;
+------+----------+----------+---------------------+-----------------+
| ID | COUNT(*) | Provider | CreateDate | URL |
+------+----------+----------+---------------------+-----------------+
| 1 | 3 | Prov1 | 2015-05-10 00:00:00 | http://ex.com/1 |
| 4 | 2 | Prov2 | 2015-05-10 00:00:00 | http://ex.com/4 |
+------+----------+----------+---------------------+-----------------+
While running the same thing on SQL fiddle works as expected. Further the comment regarding the JOIN should work, but I am having the same issue with it returning unexpected results. My version of MySQL is 5.5.37-MariaDB-34.0. Any ideas on this?
SELECT p1.*, COUNT(*)
FROM products p1
JOIN
(
select provider, max(CreateDate) as m_date
from products
group by provider
) p2 on p1.provider = p2.provider and p1.CreateDate = p2.m_date
You need specify the field name where you want mysql return count content:
select count(*) as nro from table;
in your case:
SELECT ID,COUNT(*) as nro,Provider,CreateDate,URL
FROM (SELECT * FROM products ORDER BY CreateDate DESC)
GROUP BY Provider;
but for all solution you need create an alias for this part of query "SELECT * FROM products ORDER BY CreateDate DESC"
CREATE VIEW p AS (SELECT * FROM products ORDER BY CreateDate DESC);
and the second query:
SELECT ID,COUNT(*) as nro,Provider,CreateDate,URL
FROM (p)
GROUP BY Provider;
must be work.
I did it in SQL Server but the query in mysql works.
Create TABLE #Products(
Id int,
Provider varchar(50),
URL Varchar(300),
Createdate varchar(50)
)
INSERT INTO #Products
SELECT 1,'Prov1','http://ex.com/1 ','2015-05-10' UNION ALL
SELECT 2,'Prov1','http://ex.com/2 ','2015-06-10' UNION ALL
SELECT 3,'Prov1','http://ex.com/3 ','2015-07-10' UNION ALL
SELECT 4,'Prov2','http://ex.com/4 ','2015-05-10' UNION ALL
SELECT 5,'Prov2','http://ex.com/5 ','2015-06-10'
SELECT p1.id, p2.Provider,p2.m_date,p1.url, COUNT(*)
FROM #products p1
JOIN
(
select provider, max(CreateDate) as m_date
from #products
group by provider
) p2 on p1.provider = p2.provider and p1.CreateDate = p2.m_date
GROUP BY p1.id, p2.Provider,p2.m_date,p1.url

MySQL select from specific ID until match condition

Given this table:
+----+-----------+--------+
| id | condition | values |
+----+-----------+--------+
| 1 | a | 1 |
+----+-----------+--------+
| 2 | a | 2 |
+----+-----------+--------+
| 3 | a | 3 |
+----+-----------+--------+
| 4 | a | 4 |
+----+-----------+--------+
| 5 | b | 5 |
+----+-----------+--------+
| 6 | b | 6 |
+----+-----------+--------+
How can I get a new table that begins on id=3 (including) and goes until condition = b (excluding):
+----+-----------+--------+
| id | condition | values |
+----+-----------+--------+
| 3 | a | 3 |
+----+-----------+--------+
| 4 | a | 4 |
+----+-----------+--------+
added fiddle: http://sqlfiddle.com/#!2/9882f7
Basically I want a table between a matching first condition (over a specific column - id) and a second one (over a different column - condition)
You need to stop thinking of SQL data as having any order. Think of SQL data in sets; you have to search by values, not by positions.
SELECT t1.*
FROM t AS t1
JOIN (
SELECT MIN(id) AS id FROM t
WHERE id >= 3 AND `condition` = 'b'
) AS t2
WHERE t1.id >= 3 AND t1.id < t2.id
ORDER BY t1.id
Something like this:
select t.*
from table t
where id >= 3 and id < (select min(t2.id) from table t2 where t2.condition = 'b');
EDIT:
This query works fine on the SQL Fiddle:
select t.*
from t
where id >= 3 and id < (select min(t2.id) from t t2 where t2.condition = 'b');
If I understand what you are asking for, I believe this will work for you:
SELECT id, condition, values
FROM tableName
WHERE id > 2
AND condition != b
ORDER BY id
I hope that works for you.