Is it possible to perform a group_concat in mysql and have some sort of group limit? thus allowing the group to be split over a number of result rows?
e.g.
if I have a table called num like so:
val
---
1
2
3
4
5
...
and I use the statement select group_concat(val) from num
I get the result
1,2,3,4,5,...
What I want to do is set a group limit of 2 and get the result
1,2
3,4
5,...
Now the use case I have is hundreds of thousands of values that I want to select as neat groups of about 500.
Is it possible?
You can use a user variable to create a row number, then group by dividing the row numbers by the group size.
SELECT GROUP_CONCAT(val) AS vals
FROM (SELECT val, #rownum := #rownum + 1 AS rownum
FROM (SELECT val FROM nums ORDER BY val) AS vals
CROSS JOIN (SELECT #rownum := -1) AS vars) AS temp
GROUP BY FLOOR(rownum/500)
Related
I have really different problem about database query. There is a little bit different scenarios:
I have a table created with 3 columns. They have ID, ItemId, TypeId columns. I need a count query, it should count ItemId and TypeId together but except duplicate columns. For example;
Id ItemId TypeId
-- ------ ------
1 1 1 -> count +1
2 1 1 -> ignore
3 1 2 -> count -1
4 1 2 -> ignore
5 1 1 -> count +1
result count = 1
In the end, if distinct row repeated, count ignore that row. But TypeId data changed for one specific Item it should increase or decrease count. TypeId equals to 1 count +=1, equals to 2 count -=1.
In MySQL, you would seemingly use count(distinct):
select count(distinct itemId, typeId)
from t;
However, you really have a gaps-and-islands problem. You are looking at the ordering to see where things change.
If I trust that the id has no gaps, you can do:
select count(*)
from t left join
t tprev
on t.id = tprev.id + 1
where not ((t.itemId, t.typeid) <=> (tprev.itemId, t.prev.id))
Try the following query. This employs User-defined session variables. It will work in all the cases (including gaps in Id):
SELECT
SUM(dt.factor) AS total_count
FROM
( SELECT
#factor := IF(#item = ItemId AND
#type = TypeId,
0,
IF(TypeID = 2, -1, 1)
) AS factor,
#item := ItemId,
#type := TypeId
FROM your_table
CROSS JOIN (SELECT #item := 0,
#type := 0,
#factor := 0) AS user_init_vars
ORDER BY Id
) AS dt
DB Fiddle DEMO
I think there is no question like this.
I need to group rows by n records and get some values of this group.
I think is better to explain with a graphic example:
Is possible to do a query like this? if not my solution will be make an script to create another table with this but I donĀ“t like duplicate data at all.
Thanks!!!
set #counter=-1;
select xgroup,max(x) as mx, max(y) as my, avg(value3) as v3,
from
(
select (#counter := #counter +1) as counter,
#counter div 5 as xgroup,
currency, datetime, value1, value2,
case mod(#counter,5) when 0 then value1 else 00 end as x,
case mod(#counter,5) when 4 then value2 else 00 end as y,
mod(#counter,5) as xxx
FROM findata
) name1
group by xgroup;
#jms has the right approach, but you have to be very careful when using variables:
You should not assign a variable in one expression and then reference it in another in the same select.
To work in the most recent versions of MySQL, I would suggest ordering the data in a subquery.
In addition, there are some other values that you need:
select min(col1), min(col2),
max(case when mod(rn, 5) = 0 then col3 end),
max(col4), min(col5),
max(case when mod(rn, 5) or rn = #rn then col6 end),
max(case when mod(rn, 5) or rn = #rn then col7 end)
from (select (#rn := #rn + 1) as rn, t.*
from (select t.*
from t
order by col1, col2
) t cross join
(select #rn := -1) params
) t
group by (#rn div 5);
Note the logic is a bit arcane for the last values -- this is to take into account the final group that might not have exactly 5 rows.
You need a column that looks like(assuming you want to group every 5 rows)
dummy_table
1
1
1
1
1
2
2
2
2
2
...
You can do this by using generate_series() if you are using postgre sql by using
select t1 from (select generate_series(1,x)) t1, (select generate_series(1,5)) t2;
where you can replace x by (total rows/5) i.e. for 100 rows, x = 20. If you are using any other SQL platform, you can just work on creating this dummy table accordingly.
Once you get this dummy_table, join it with your table on row_number of your table with t1 column of dummy_table(not row_number of dummy_table). Syntax for accessing row number should be straightforward.
After the join, group by this t1 column and do the required aggregation. To do this in a single query, you can do the above in an inner query and do aggregation outside it. Hope this makes sense.
Ok, thanks you all guys for your answers, thanks to it I found the simple solution.
I simply add an autoincrement column, and then I can group results by integer division by 5.
And with this query:
SELECT id,
symbol,
datetime,
open,
MAX(high),
MIN(low),
SUBSTRING_INDEX( GROUP_CONCAT(CAST(close AS CHAR) ORDER BY datetime DESC), ',', 1 ) AS close
FROM `table`
GROUP BY (id-1) DIV 5
And the resulting is:
Thanks!
A solution is to introduce some field for grouping rows for aggregative operations.
It can be reached by introducing a user-variable and assigning values that will allow to group rows as required. For example, it can be a row counter divided by grouping chuck size and rounded to nearest upper ceil number:
SET #counter=0;
SELECT CEIL((#counter:=#counter+1)/5) AS chunk, MAX(high), MIN(low) FROM `table` GROUP BY chunk;
I have a table that contains 100000 rows and in that there is a column say drugname whose type is varchar.
The values of the column may start with 0 to 9 and
A-Z or a-z.
I need a query that returns 25000 rows. And out of that 25000 rows it should contain all the alphabets and numbers.
Say for example,
alphabet A contains 500 rows
alphabet B contains 500 rows
number 1 contains 400 rows
number 2 contains 300 rows etc...
that includes all the numbers and alphabets and overall it should have 25000 rows.
First select minimal set of ids for all the groups (by the first char)
select min(id) as substrId
from the_table
group by substr(value,0,1)
Then use the subselect to return the selected ids first (to represent all the groups) and then some random ids
select t1.*
from the_table t1
left join (select min(id) as substrId
from the_table
group by substr(value,0,1)) sub on t1.id=sub.substrId
order by ifnull(sub.substrId, 0) desc
limit 25000
Thus all the distinct group members are included in the final result set.
Enumerate the rows based on the first character. Then use the enumeration to get the rows:
select d.*
from (select d.*,
(#rn := if(#l = left(d.drugname, 1), #rn + 1,
if(#l := left(d.drugname, 1), 1, 1)
)
) as seqnum
from drugs d cross join
(select #rn := 0, #l := '') params
order by d.drugname
) d
order by seqnum
limit 25000;
Note: This returns the rows with the smallest drug names for each letter. If you want random ones, then use this order by:
order by left(d.drugname, 1), rand()
I have a query like this:
(select #number:=3)
union
(select #number:=2)
union
(select #number:=1)
order by #number ASC
With results:
3
2
1
But I would like the results in ascending order, like this:
1
2
3
How can I achieve the results in ascending order with a query like this?
You can wrap the UNION in a subquery, try this:
SELECT *
FROM(
SELECT #number := 3 AS number
UNION
SELECT #number := 2 AS number
UNION
SELECT #number := 1 AS number) tmp
ORDER BY number;
Here is an SQL Fiddle example.
An edit, to explain what is happening:
In your example, MySQL is treating each group as its own query (which is how you'd expect a union to work) so it is as if you had three different queries, and only the third one is being ordered.
So, by putting the unioned queries together, you have one result set, and that entire result set is what is being ordered.
This is your query:
(select #number:=3)
union
(select #number:=2)
union
(select #number:=1)
order by #number ASC
Your order by has a constant. It is order by "1" -- #number is a variable, not a column name. Hence, no ordering. What you want is to specify the number as a column name:
select 3 as number
union all
select 2
union all
select 1
order by number;
You should also use union all instead of union, unless you want the additional overhead of removing duplicates.
I have a table in MySQL populated as follows. Now I need to select the row number of a record in its sorted order. For example, the row number of words starting with 'c' should be 4.
Words
=====
coffee
banana
apple
cherry
blackberry
I tried the following query, but I get wrong results. Here dict is the table name and words is the column name.
SELECT #rownum:=#rownum + 1 id FROM (SELECT * FROM dict ORDER BY words) d,(SELECT #rownum:=0) r WHERE d.words LIKE CONCAT('c','%')
For the above query, I am getting the row numbers for the outer query. But I want the row numbers of the internal query. I do not know how to get that.
Any help is appreciated. Thanks.
Try this perhaps:
SET #rownum = 0;
SELECT id
FROM (SELECT *, #rownum:=#rownum + 1 AS id FROM dict ORDER BY words) d
WHERE d.words LIKE CONCAT('c','%')
As single query, try this:
SELECT id
FROM (SELECT *, #rownum:=#rownum + 1 AS id FROM dict, (SELECT #rownum:=0) r ORDER BY words) d
WHERE d.words LIKE CONCAT('c','%')