MySQL Using ORDER BY On Grouped (GROUP BY) Results - mysql

I am clearly misundersatanding something here with MySQL's GROUP BY as it is changing the order of my results.
Using this example SQL data:
CREATE TABLE IF NOT EXISTS `example_table` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`GROUP_NAME` text NOT NULL,
`ORDER_COLUMN` int(11) NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;
INSERT INTO `example_table` (`ID`, `GROUP_NAME`, `ORDER_COLUMN`) VALUES
(NULL, '271007K240.003:10', 70),
(NULL, '271007K240.003:10', 90),
(NULL, '271007K240.003:10', 100),
(NULL, '271007K240.003:10', 50),
(NULL, '271007K240.003:10', 80),
(NULL, '271007K240.003:10', 60);
Now I've created this data as an example so there would be lots of different values in the GROUP_NAME column., but for clairty I've just included what demonstrates the issue.
Running this very simple query, returns the highest ORDER_COLUMN (ID: 3) at the top as expected:
SELECT
*
FROM
`example_table`
ORDER BY
ORDER_COLUMN
DESC
However I've actually wanting to group by the column I've named here GROUP_NAME, sowith this in mind I was anting to do asomething like so:
SELECT
*
FROM
`example_table`
GROUP BY
GROUP_NAME
ORDER BY
ORDER_COLUMN
DESC
Now doing this just simply returns the first row, and ignores the ORDER BY.
I then thought to achieve what I need, I would need to use a sub-query with the data pre-sorted and then the group by can just group the sub-queries data-set like so:
SELECT
*
FROM
(
SELECT
*
FROM
`example_table`
ORDER BY
ORDER_COLUMN
DESC
) AS TEMP_TABLE
GROUP BY GROUP_NAME
Unfortunately though, this still returns just the first row of the table. What am I doing wrong here?

With this query:
select group_name, max(order_column) order_column
from example_table
group by group_name
you can get the max value of order_column for each id.
Then join it to the table:
select t.*
from example_table t inner join (
select group_name, max(order_column) order_column
from example_table
group by group_name
) g on g.group_name = t.group_name and g.order_column = t.order_column
See the demo.
Results:
| ID | GROUP_NAME | ORDER_COLUMN |
| --- | ----------------- | ------------ |
| 3 | 271007K240.003:10 | 100 |

Not completely sure what you want to accomplish, but did you try:
ORDER BY
GROUP_NAME,
ORDER_COLUMN
If that doesn't do the trick, how about adding more data with different group names and an example of what you expect the output to look like

Related

AVG with LIMIT and GROUP BY

I'm looking to make a SQL query, but I can't do it... and I can't find an example like mine.
I have a simple table People with 3 columns, 7 records :
I'd like to get for each team, the average points of 2 bests people.
My Query:
SELECT team
, (SELECT AVG(point)
FROM People t2
WHERE t1.team = t2.team
ORDER
BY point DESC
LIMIT 2) as avg
FROM People t1
GROUP
BY team
Current result: (average on all people of each team)
Apparently, it's not possible to use a limit into subquery. "ORDER BY point DESC LIMIT 2" is ignored.
Result expected:
I want the average points of 2 bests people (with highest points) for each team, not the average points of all people of each team.
How can I do that? If anyone has any idea..
I'm on MySQL Database
Link of Fiddle : http://sqlfiddle.com/#!9/8c80ef/1
Thanks !
You can try this.
try to make a order number by a subquery, which order by point desc.
then only get top 2 row by each team, if you want to get other top number just modify the number in where clause.
CREATE TABLE `People` (
`id` int(11) NOT NULL,
`name` varchar(20) NOT NULL,
`team` varchar(20) NOT NULL,
`point` int(4) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `People` (`id`, `name`, `team`, `point`) VALUES
(1, 'Luc', 'Jupiter', 10),
(2, 'Marie', 'Saturn', 0),
(3, 'Hubert', 'Saturn', 0),
(4, 'Albert', 'Jupiter', 50),
(5, 'Lucy', 'Jupiter', 50),
(6, 'William', 'Saturn', 20),
(7, 'Zeus', 'Saturn', 40);
ALTER TABLE `People`
ADD PRIMARY KEY (`id`);
ALTER TABLE `People`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=8;
Query 1:
SELECT team,avg(point) totle
FROM People t1
where (
select count(*)
from People t2
where t2.id >= t1.id and t1.team = t2.team
order by t2.point desc
) <=2 ## if you want to get other `top` number just modify this number
group by team
Results:
| team | totle |
|---------|-------|
| Jupiter | 50 |
| Saturn | 30 |
This is a pain in MySQL. If you want the two highest point values, you can do:
SELECT p.team, AVG(p2.point)
FROM people p
WHERE p.point >= (SELECT DISTINCT p2.point
FROM people p2
WHERE p2.team = p.team
ORDER BY p2.point DESC
LIMIT 1, 1 -- get the second one
);
Ties make this tricky, and your question isn't clear on what to do about them.

Why MySQL GROUP_CONCAT returns NULL when there are some non-NULL values

Here is some sample code:
CREATE TABLE test (
first_name VARCHAR(255),
last_name VARCHAR(255)
);
INSERT INTO test (first_name) VALUES ('first_1');
INSERT INTO test (last_name) VALUES ('last_1');
SELECT GROUP_CONCAT(first_name, last_name) FROM test;
SELECT GROUP_CONCAT(first_name), GROUP_CONCAT(last_name) FROM test;
The first select returns null, while the MySQL documentation states that it returns null if there are no non-NULL values.
Here is a demo.
This result is expected. Consider the first query:
SELECT GROUP_CONCAT(first_name, last_name) FROM test;
Because each record has a NULL for either the first or last name, you are concatenating a NULL value and then aggregating on that NULL, also yielding NULL. To understand this behavior better, run the query SELECT CONCAT('Hello', NULL) FROM dual and observe that the output is NULL.
However, in the second query:
SELECT GROUP_CONCAT(first_name), GROUP_CONCAT(last_name) FROM test;
You are group concatenating on the individual columns. In this case, the NULL values will be ignored, and you are left with the non NULL first and last name individual values.
Diagrammatically, we can draw the following:
first_name | last_name | CONCAT (first_name, last_name)
first_1 | NULL | NULL
NULL | last_1 | NULL
--------------------------------
first_1 | last_1 | NULL <-- GROUP_CONCAT of columns
You can see that GROUP_CONCAT across records behaves like the other aggregate functions, ignoring NULL values. But across columns, GROUP_CONCAT will do a concatenation first, resulting in NULL if even one value be NULL.
use having
SELECT
`a`.`id`, `a`.`name`, `b`.`id` AS `b_id`, `b`.`name` AS `b_name`, GROUP_CONCAT( `c`.`l_id` ) AS `c_ls`
FROM
`a`
INNER JOIN `b` ON `a`.`b_id` = `b`.`id`
LEFT OUTER JOIN `c` ON `a`.`id` = `c`.`a_id`
having `a`.`id` IS NOT NULL
GROUP BY `a`.`id`
ORDER BY `a`.`created` DESC

MySQL Select duplicates with LEAST condition

I'm trying to find duplicates and select the result with the least value combination in a table.
Until now I'm only able to select the result that has the lowest value on a column using MIN(). I thought it would be easy to just replace MIN with LEAST and change the columns.
Here's a layout:
CREATE TABLE `index`.`products` ( `id` INT NOT NULL AUTO_INCREMENT , `name` VARCHAR(10) NOT NULL , `price` INT NOT NULL , `availability` INT NOT NULL , PRIMARY KEY (`id`)) ENGINE = InnoDB;
INSERT INTO `products` (`id`, `name`, `price`, `availability`) VALUES
(NULL, 'teste', '10', '1'),
(NULL, 'teste', '5', '2'),
(NULL, 'teste', '3', '3');
The simplified layout
id - name - price - availabilty
1 - test - 10 - 1
2 - test - 5 - 2
3 - test - 3 - 3
using the following query:
select name, MIN(price) from products group by name having count(*) > 1
gets me the lowest price. I'm trying to get the lowest price and lowest availabilty.
select name, LEAST(price, availability) from products group by name having count(*) > 1
This doesn't work.
Clarification: I want to select the row with the lowest price and lowest availabity. In this case it should be the first one I guess.
I should clarifity that 1=available, 2=not available and 3=coming soon
The statement to select lowest price for the best availability is:
set sql_mode=only_full_group_by;
SELECT
name, MIN(price), availability
FROM
products
JOIN
(
SELECT
name, MIN(availability) availability
FROM
products
GROUP BY name
) as x
USING (name , availability)
GROUP BY name , availability;

get the id of the row with the least value, group by an other column

I ran into a problem trying to pull one action per user with the least priority, the priority is based on other columns content and is an integer,
This is the initial query :
SELECT
CASE
...
END AS dummy_priority,
id,
user_id
FROM
actions
Result :
id user_id priority
1 2345 1
2 2345 3
3 2999 5
4 2999 2
5 3000 10
Desired result :
id user_id priority
1 2345 1
4 2999 2
5 3000 10
Following what i want i tried
SELECT x.id, x.user_id, MIN(x.priority)
FROM (
SELECT
CASE
...
END AS priority,
id,
user_id
FROM
actions
) x
GROUP BY x.user_id
Which didn't work
Error Code: 1055. Expression #1 of SELECT list is not in GROUP BY
clause and contains nonaggregated column 'x.id' which is not
functionally dependent on columns in GROUP BY clause;
Most examples of this I found were extracting just the user_id and priority and then doing an inner join with both of them to get the row, but I can't do that since (priority, user_id) isn't unique
A simple verifiable example would be
CREATE TABLE `actions` (
`id` int(11) NOT NULL,
`user_id` int(11) DEFAULT NULL,
`priority` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO `actions` (`id`, `user_id`, `priority`) VALUES
(1, 2345, 1),
(2, 2345, 3),
(3, 2999, 5),
(4, 2999, 2),
(5, 3000, 10);
how to extract the desired result (please hold in mind that this table is a subquery)?
The proper way to do this would involve a subquery of some sort . . . and that would require repeating the case definition.
Here is another method, using the substring_index()/group_concat() trick:
SELECT SUBSTRING_INDEX(GROUP_CONCAT(x.id ORDER BY x.priority), ',', 1) as id,
x.user_id, MIN(x.priority)
FROM (SELECT (CASE ...
END) AS priority,
id, user_id
FROM actions a
) x
GROUP BY x.user_id;
And that proper way in full...
SELECT x...
, CASE...x... priority
FROM my_table x
JOIN
( SELECT user_id
, MIN(CASE...) priority
FROM my_table
GROUP
BY user_id
) y
ON y.user_id = x.user_id
AND y.priority = CASE...x...;
This should work ...
SELECT id , user_id, priority FROM actions act
INNER JOIN
(SELECT
user_id, MIN(priority) AS priority
FROM
actions
GROUP BY user_id) pri
ON act.user_id = pri.user_id AND act.priority = pri.prority

Alternative to Intersect in MySQL

I need to implement the following query in MySQL.
(select * from emovis_reporting where (id=3 and cut_name= '全プロセス' and cut_name='恐慌') )
intersect
( select * from emovis_reporting where (id=3) and ( cut_name='全プロセス' or cut_name='恐慌') )
I know that intersect is not in MySQL. So I need another way.
Please guide me.
Microsoft SQL Server's INTERSECT "returns any distinct values that are returned by both the query on the left and right sides of the INTERSECT operand" This is different from a standard INNER JOIN or WHERE EXISTS query.
SQL Server
CREATE TABLE table_a (
id INT PRIMARY KEY,
value VARCHAR(255)
);
CREATE TABLE table_b (
id INT PRIMARY KEY,
value VARCHAR(255)
);
INSERT INTO table_a VALUES (1, 'A'), (2, 'B'), (3, 'B');
INSERT INTO table_b VALUES (1, 'B');
SELECT value FROM table_a
INTERSECT
SELECT value FROM table_b
value
-----
B
(1 rows affected)
MySQL
CREATE TABLE `table_a` (
`id` INT NOT NULL AUTO_INCREMENT,
`value` varchar(255),
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE `table_b` LIKE `table_a`;
INSERT INTO table_a VALUES (1, 'A'), (2, 'B'), (3, 'B');
INSERT INTO table_b VALUES (1, 'B');
SELECT value FROM table_a
INNER JOIN table_b
USING (value);
+-------+
| value |
+-------+
| B |
| B |
+-------+
2 rows in set (0.00 sec)
SELECT value FROM table_a
WHERE (value) IN
(SELECT value FROM table_b);
+-------+
| value |
+-------+
| B |
| B |
+-------+
With this particular question, the id column is involved, so duplicate values will not be returned, but for the sake of completeness, here's a MySQL alternative using INNER JOIN and DISTINCT:
SELECT DISTINCT value FROM table_a
INNER JOIN table_b
USING (value);
+-------+
| value |
+-------+
| B |
+-------+
And another example using WHERE ... IN and DISTINCT:
SELECT DISTINCT value FROM table_a
WHERE (value) IN
(SELECT value FROM table_b);
+-------+
| value |
+-------+
| B |
+-------+
There is a more effective way of generating an intersect, by using UNION ALL and GROUP BY. Performances are twice better according to my tests on large datasets.
Example:
SELECT t1.value from (
(SELECT DISTINCT value FROM table_a)
UNION ALL
(SELECT DISTINCT value FROM table_b)
) AS t1 GROUP BY value HAVING count(*) >= 2;
It is more effective, because with the INNER JOIN solution, MySQL will look up for the results of the first query, then for each row, look up for the result in the second query. With the UNION ALL-GROUP BY solution, it will query results of the first query, results of the second query, then group the results all together at once.
Your query would always return an empty recordset since cut_name= '全プロセス' and cut_name='恐慌' will never evaluate to true.
In general, INTERSECT in MySQL should be emulated like this:
SELECT *
FROM mytable m
WHERE EXISTS
(
SELECT NULL
FROM othertable o
WHERE (o.col1 = m.col1 OR (m.col1 IS NULL AND o.col1 IS NULL))
AND (o.col2 = m.col2 OR (m.col2 IS NULL AND o.col2 IS NULL))
AND (o.col3 = m.col3 OR (m.col3 IS NULL AND o.col3 IS NULL))
)
If both your tables have columns marked as NOT NULL, you can omit the IS NULL parts and rewrite the query with a slightly more efficient IN:
SELECT *
FROM mytable m
WHERE (col1, col2, col3) IN
(
SELECT col1, col2, col3
FROM othertable o
)
I just checked it in MySQL 5.7 and am really surprised how no one offered a simple answer: NATURAL JOIN
When the tables or (select outcome) have IDENTICAL columns, you can use NATURAL JOIN as a way to find intersection:
For example:
table1:
id, name, jobid
'1', 'John', '1'
'2', 'Jack', '3'
'3', 'Adam', '2'
'4', 'Bill', '6'
table2:
id, name, jobid
'1', 'John', '1'
'2', 'Jack', '3'
'3', 'Adam', '2'
'4', 'Bill', '5'
'5', 'Max', '6'
And here is the query:
SELECT * FROM table1 NATURAL JOIN table2;
Query Result:
id, name, jobid
'1', 'John', '1'
'2', 'Jack', '3'
'3', 'Adam', '2'
For completeness here is another method for emulating INTERSECT. Note that the IN (SELECT ...) form suggested in other answers is generally more efficient.
Generally for a table called mytable with a primary key called id:
SELECT id
FROM mytable AS a
INNER JOIN mytable AS b ON a.id = b.id
WHERE
(a.col1 = "someval")
AND
(b.col1 = "someotherval")
(Note that if you use SELECT * with this query you will get twice as many columns as are defined in mytable, this is because INNER JOIN generates a Cartesian product)
The INNER JOIN here generates every permutation of row-pairs from your table. That means every combination of rows is generated, in every possible order. The WHERE clause then filters the a side of the pair, then the b side. The result is that only rows which satisfy both conditions are returned, just like intersection two queries would do.
Starting from MySQL 8.0.31 the INTERSECT is natively supported.
INTERSECT Clause:
SELECT ...
INTERSECT [ALL | DISTINCT] SELECT ...
[INTERSECT [ALL | DISTINCT] SELECT ...]
INTERSECT limits the result from multiple SELECT statements to those rows which are common to all.
Sample:
SELECT 1 AS col
INTERSECT
SELECT 1 AS col;
-- output
1
Break your problem in 2 statements: firstly, you want to select all if
(id=3 and cut_name= '全プロセス' and cut_name='恐慌')
is true . Secondly, you want to select all if
(id=3) and ( cut_name='全プロセス' or cut_name='恐慌')
is true. So, we will join both by OR because we want to select all if anyone of them is true.
select * from emovis_reporting
where (id=3 and cut_name= '全プロセス' and cut_name='恐慌') OR
( (id=3) and ( cut_name='全プロセス' or cut_name='恐慌') )
AFAIR, MySQL implements INTERSECT through INNER JOIN.
SELECT
campo1,
campo2,
campo3,
campo4
FROM tabela1
WHERE CONCAT(campo1,campo2,campo3,IF(campo4 IS NULL,'',campo4))
NOT IN
(SELECT CONCAT(campo1,campo2,campo3,IF(campo4 IS NULL,'',campo4))
FROM tabela2);