GROUP CONCAT with ORDER in MEMSQL - mysql

Here is a toy example:
CREATE TABLE TEST
(
ID INT,
AGG NVARCHAR(20),
GRP NVARCHAR(20)
);
INSERT INTO TEST VALUES
(1, 'AB', 'X'), (2, 'BC', 'X'), (3, 'AC', 'X'),
(4, 'EF', 'Y'), (5, 'FG', 'Y'), (6, 'DC', 'Y'),
(7, 'JI', 'Z'), (8, 'IJ', 'Z'), (9, 'JK', 'Z');
Now, I would like to do this (this is a valid code in MySQL, but not in MEMSQL):
SELECT
COUNT(*),
SUM(ID),
GROUP_CONCAT(AGG ORDER BY AGG),
GRP
FROM TEST
GROUP BY GRP
So that the output looks like this (Required Output):
3 6 AB,AC,BC X
3 15 DC,EF,FG Y
3 24 IJ,JI,JK Z
Note that the values in the third column are sorted for each row. My output looks like this (Current Wrong Output):
3 6 BC,AB,AC X
3 15 DC,EF,FG Y
3 24 IJ,JI,JK Z
Compare each row in the third column, the lists are sorted.
However, since the above query is not valid in MEMSQL, I have to remove the ORDER BY AGG part in GROUP_CONCAT which causes the third column to not be sorted.
As per the documentation of GROUP_CONCAT, the expression can also be a function, however, there is no built in function to sort. I have tried many combinations of SELECT ... ORDER BY statements in GROUP_CONCAT without success. Is this impossible to do, or am I missing something?

I think this works for my case.
SELECT
COUNT(*),
SUM(T.ID),
GROUP_CONCAT(T.AGG),
T.GRP
FROM (
SELECT
*,
RANK() OVER(PARTITION BY GRP ORDER BY AGG) AS R
FROM TEST
) T
GROUP BY T.GRP
ORDER BY T.R
It is rather convoluted, so I hope someone can suggest an improvement.

Try this:
SELECT
COUNT(*),
SUM(ID),
GROUP_CONCAT(AGG),
GRP
FROM TEST
GROUP BY GRP
ORDER BY GROUP_CONCAT(AGG)

Related

Getting the most common value in a table

I'm struggling to work out how to get the most commonly occurring value from a table in MySQL.
Example:
CREATE TABLE words(`letter1` char(1), `letter2` char(1));
INSERT INTO words(`letter1`, `letter2`)VALUES
('A', 'A'), ('B', 'A'), ('C', 'A'), ('D', 'A'), ('D', 'B'),
('B', 'B'), ('D', 'B'), ('A', 'C'), ('B', 'D'), ('D', 'A');
So for letter1 I want to pick out the value 'D' and for letter2 I want to pick out 'A'.
For a tie I'm not too bothered which of the tied values it picks.
Thanks for any help, It looks like it ought to be easy but I can't figure it out. For one letter it would be easy but for multiple I don't know how to.
SELECT ( SELECT letter1
FROM table
GROUP BY 1
ORDER BY COUNT(*) DESC LIMIT 1 ) letter1,
( SELECT letter2
FROM table
GROUP BY 1
ORDER BY COUNT(*) DESC LIMIT 1 ) letter2;
If two or more letters have the same and maximal amount of occurences then one indefinite letter of these letters (but in most cases - the least lexicographically) will be returned.
Will it work for you?
Query for letter1
SELECT letter1 FROM words GROUP BY letter1 ORDER BY COUNT(1) DESC LIMIT 1;
Query for letter2
SELECT letter2 FROM words GROUP BY letter2 ORDER BY COUNT(1) DESC LIMIT 1;
For calculations based on individual columns, Akina already provided an answer. Just in case you'd like to find the mostly used pair , you can try this if you don't care which pair to pick in case of a tie:
select count(*) ct,letter1,letter2 from words group by letter1,letter2 order by ct desc limit 1;

How to get the latest value?

I am trying to write a query and I want to get only the max(date), and I have to group by the rest of info such as job_ID, invoice, total_paid, and payment_method.
I can't group by the payment method because logically its not correct and also because the payment methods are different... and, I cannot use listagg here.
Any idea how can I accomplish my goal?
I almost reached the end but the payment method cause some errors in the code...
Thank you in advance :)
You can get the row with the max date for any id with a qualify row_number() over():
with data as (
select $1 id, $2 date, $3 value
from values (1, 1, 'a')
, (1, 2, 'b')
, (1, 3, 'c')
, (2, 1, 'e')
, (2, 2, 'f')
, (2, 3, 'g')
)
select *
from data
qualify row_number() over(partition by id order by date desc) = 1
;
If this is not what you want — you will need to improve the question by detailing sample input data and desired results.

Using rollup with a view gives me an empty field?

I've been smashing my head against this for a few days, so I decided to create a sample data set to prove to myself it wasn't our monolithic schema that's breaking things. But nope, it still happens.
CREATE TABLE test_data (
`id` int(11) NOT NULL AUTO_INCREMENT,
`label` varchar(10) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE VIEW test_view AS
SELECT id, label
FROM test_data;
INSERT INTO test_data VALUES
(1, 'red'),
(2, 'red'),
(3, 'red'),
(4, 'red'),
(5, 'green'),
(6, 'green'),
(7, 'green'),
(8, 'yellow'),
(9, 'yellow'),
(10, 'yellow'),
(11, 'blue'),
(12, 'blue'),
(13, 'blue'),
(14, 'blue'),
(15, 'blue'),
(16, 'blue');
https://www.db-fiddle.com/f/vMwvXDtReEwfAtGjYw4xEt/0
So we have a table with some data, and then we select it using group by with rollup:
SELECT label, COUNT(id) as cnt FROM test_data GROUP BY label WITH ROLLUP
Then there's another query wrapped around this (which in the real world does other things, in this example is just pulling data from the select).
SELECT COALESCE(label, 'TOTAL') as wrap_label, label, cnt FROM
(
SELECT label, COUNT(id) as cnt FROM test_data GROUP BY label WITH ROLLUP
) as d;
When I pull directly from a table, it works perfectly, we get the final line with TOTAL as the wrap_label. However if there's a view (which is essentially a SELECT * from test_data) involved it stops pulling out the wrap_label:
SELECT COALESCE(label, 'TOTAL') as wrap_label, label, cnt FROM
(
SELECT label, COUNT(id) as cnt FROM test_view GROUP BY label WITH ROLLUP
) as d;
If you run the fiddle above you'll see that Query #2 has TOTAL, Query #4 has a blank identifier. weirdly the subqueries (#1 from the table, #3 from the view) both seem to return the exact same data!
Is this expected behaviour? I'm guessing it's something to do with execution order and the select or the group by from the view is happening AFTER the subquery is trying to pull?
Obviously this is easily fixed by ditching the subselect and just doing
SELECT COALESCE(label, 'TOTAL') as label, COUNT(id) as cnt FROM test_view GROUP BY label WITH ROLLUP;
but this is the real world and I can't do that I'm afraid, the outer query does more and is in many places in our massive codebase.
I'd appreciate any insight anyone might have.
Cheers,
Si.

Get n oldest rows, but no more than x that have the same value in a column

I have a simple table
CREATE TABLE `example` (
`id` int(12) NOT NULL,
`food` varchar(250) NOT NULL
);
With the following data
INSERT INTO `example` (`id`, `food`) VALUES
(1, 'apple'),
(2, 'apple'),
(3, 'apple'),
(4, 'apple'),
(5, 'apple'),
(6, 'apple'),
(7, 'apple'),
(8, 'banana'),
(9, 'banana'),
(10, 'potato'),
(11, 'potato'),
(12, 'potato'),
(13, 'banana'),
(14, 'banana'),
(15, 'banana');
I want to get the oldest 10 rows
SELECT *
FROM example
ORDER BY id ASC
LIMIT 10
But I don't want to get more than 5 rows where food has the same value.
My current query receives 7 apple (more than I want), 2 banana, and 1 potato. In the data provided, I'd want to receive 5 apple, 2 banana, and 3 potato.
How can I accomplish this?
Update:
SQL Group BY, Top N Items for each Group is not a duplicate because it involves a different database. In particular, GROUP BY works different in sql-server than it does in MySQL
You can add a count (in reverse) for each food . . . using variables or a correlated subquery. This will use the latter:
select t.*
from (select t.*,
(select count(*) from example t2 where t2.food = t.food and t2.id >= t.id) as seqnum
from example t
) t
where seqnum <= 5
order by id desc
limit 10;
I didn't create the table and test this, but it should give you what you want. Just a different approach than the one above.
Select *
From (Select ID, Food
, Count(Food) Over(Partition By Food Order by ID) as Appearances
From Your_Table) as a
Where a.Appearances <= 5
Order By ID Asc
You can obviously put the limit if you want.

get rank of specific users in mysql

I have a table of all users and I have another array which has subset of that table.
Ex:table contains 1,2,3,4,5,6,7,8,9,10
my users: 2,4,6,8,10:
Ranking of my users among themselves w.r.t points and has nothing to do with 1,3,5,7,9
I currently have this table:
create table uservotes(id int, name varchar(50), vote int);
INSERT INTO uservotes VALUES
(1, 'A', 34),
(2, 'B', 80),
(3, 'bA', 30),
(4, 'C', 8),
(5, 'D', 4),
(6, 'E', 14),
(7, 'F', 304),
(8, 'AA', 42),
(9, 'Ab', 6),
(10, 'Aa', 10);
How do I get ranking among 2,4,6,8,10
Answer I am looking for:
id rank votes name
2 1 80 B
4 5 8 C
6 3 14 E
8 2 42 AA
10 4 10 Aa
I really appreciate any help.Thanks in Advance.
I suspect that this is what you want:
select uv.*, (#rank := #rank + 1) as rank
from uservotes uv cross join
(select #rank := 0) const
where uv.id in (2, 4, 6, 8, 10)
order by votes desc;
This is the standard way of calculating a rank efficiently in MySQL, along with a where clause to choose your ids.
EDIT:
Before starting, in most databases you would simply use row_number() or dense_rank() for this purpose. MySQL does not support this ANSI standard functionality.
The key is the variable #rank. The subquery const initializes this to 0. Then the query run. The where clause gets only the rows you are interested in. The order by then puts them in order by votes, with the biggest votes first. Finally, the #rank := #rank + 1 as rank both updates the #rank variable and assigns it to a column in the output.
You need to loop a sql question with an dynamic variable
something like
select * from `uservotes` where id = $someones_id
$someones_id could be an array for an example