mysql advanced order by query - mysql

I have a table like this in MySQL:
+-----+------+
| id | type |
+-----+------+
| 149 | 8 |
| 150 | 7 |
| 151 | 8 |
| 152 | 7 |
| 153 | 5 |
| 154 | 6 |
| 155 | 3 |
| 156 | 2 |
| 157 | 4 |
| 158 | 2 |
| 159 | 1 |
| 160 | 0 |
+-----+------+
I would like to sort this table and receive results like this:
+-----+------+
| id | type |
+-----+------+
| 151 | 8 |
| 152 | 7 |
| 154 | 6 |
| 153 | 5 |
| 157 | 4 |
| 155 | 3 |
| 158 | 2 |
| 159 | 1 |
| 160 | 0 |
| 149 | 8 |
| 150 | 7 |
| 156 | 2 |
+-----+------+
As further explanation, I want to sort type column like continues count down like this : 8,7,6,5,4,3,2,1,0,8,7,6,5,4,3,2,1,0,8,7,...
Is it possible to sort table like that? or achieve that result by procedures or something else?

SELECT id, TYPE FROM (
SELECT id, TYPE,
IF(#myvar = 0 OR #myvar = TYPE, #counter := #counter + 1, #counter := 1) sequence,
#myvar := TYPE FROM mytable
JOIN (SELECT #myvar := 0, #counter := 0 ) a
ORDER BY TYPE DESC, id) b
ORDER BY sequence, TYPE DESC, id
This query will work for any level.
Put an outer query if necessary to fetch only relevant fields.
Checkout this SqlFiddle

This will work only if frequency of type is two.
Write following code in procedure:
drop temporary table if exists tmp;
create temporary table tmp
select min(id) as min_id ,max(id) as max_id from table_name group by type;
select * from table_name where id in( select max_id from tmp) order by type
union all
select * from table_name where id in(select min_id from tmp) order by type;

Try this query
select id,type from(
SELECT a.id, a.Type, count(*) as row_number FROM test a
JOIN test b ON a.type = b.type AND a.id >= b.id
GROUP BY a.Type, a.id) a
order by row_number

Related

How to select other columns of a table when grouping? [duplicate]

This question already has answers here:
SQL select only rows with max value on a column [duplicate]
(27 answers)
Closed 1 year ago.
Please assume this table:
// mytable
+--------+-------------+---------+
| num | business_id | user_id |
+--------+-------------+---------+
| 3 | 503 | 12 |
| 7 | 33 | 12 |
| 1 | 771 | 13 |
| 2 | 86 | 13 |
| 1 | 772 | 13 |
| 4 | 652 | 14 |
| 4 | 567 | 14 |
+--------+-------------+---------+
I need to group it based on user_id, So, here is my query:
select max(num), user_id from mytable
group by user_id
Here is the result:
// res
+--------+---------+
| num | user_id |
+--------+---------+
| 7 | 12 |
| 2 | 13 |
| 4 | 14 |
+--------+---------+
Now I need to also get the business_id of those rows. Here is the expected result:
// mytable
+--------+-------------+---------+
| num | business_id | user_id |
+--------+-------------+---------+
| 7 | 33 | 12 |
| 2 | 86 | 13 |
| 4 | 567 | 14 | -- This is selected randomly, because of the equality of values
+--------+-------------+---------+
Any idea how can I do that?
You don't group. You filter. One method uses window functions such as row_number():
select t.*
from (select t.*,
row_number() over (partition by user_id order by num desc) as seqnum
from mytable t
) t
where seqnum = 1;
Another method which can have slightly better performance with an index on (user_id, num) is a correlated subquery:
select t.*
from mytable t
where t.num = (select max(t2.num)
from mytable t2
where t2.user_id = t.user_id
);
You should think "group by" when you want to summarize rows. You should think "where" when you want to choose rows with particular characteristics.

sort data by specific order sequence (mysql)

So, let say I have this data
id | value | group
1 | 100 | A
2 | 120 | A
3 | 150 | B
4 | 170 | B
I want to sort it so it become like this
id | value | group
1 | 100 | A
3 | 150 | B
2 | 120 | A
4 | 170 | B
there will be more group than that, so if I the data ordered the group like (A,C,B,D,B,C,A), it will become (A,B,C,D,A,B,C)
You can add a counter column to the table, which will be used to sort the table:
select t.id, t.value, t.`group`
from (
select t.id, t.value, t.`group`,
(select count(*) from tablename
where `group` = t.`group` and id < t.id) counter
from tablename t
) t
order by t.counter, t.`group`
See the demo.
Results:
| id | value | group |
| --- | ----- | ----- |
| 1 | 100 | A |
| 3 | 150 | B |
| 2 | 120 | A |
| 4 | 170 | B |
You can approach this as
SELECT *
FROM `tablename`
ORDER BY
row_number() OVER (PARTITION BY `group` ORDER BY `group`), `group`

Select all queries with reference id in a chain

I have this table which I would like to store a chain of records.
CREATE TABLE table_name (
id INT,
unique_id varchar,
reference_id varchar,
);
I want to implement SQL query for MariDB which prints all records by id with all records with reference_id. Something like this:
| id | unique_id | reference_id | | |
|----|-----------|--------------|---|---|
| 43 | 55544 | | | |
| 45 | 45454 | 43 | | |
| 66 | 55655 | 45 | | |
| 78 | 88877 | 66 | | |
| 99 | 454 | 33 | | |
I would like when I select record 66 to get all up and down transactions because each other are using id which points to them. How I can implement this using Recursive CTE? Is there a better way?
Expected result for record with unique_id 66:
| id | unique_id | reference_id | | |
|----|-----------|--------------|---|---|
| 43 | 55544 | | | |
| 45 | 45454 | 43 | | |
| 66 | 55655 | 45 | | |
| 78 | 88877 | 66 | | |
I tried this but above rows are not printed.
select #ref:=id as id, unique_id, reference_id
from mytable
join (select #ref:=id from mytable WHERE reference_id=#ref or id = 66)tmp
where reference_id=#ref
Demo on DB Fiddle
Can you give me hand to find a solution?
EDIT: Attempt with CTE:
with recursive cte as (
select t.*
from mytable
where t.id = 66
union all
select t.*
from cte join
mytable t
on cte.id = t.reference_id
)
select *
from cte;
I get error Unknown table 't'
I'm not familiar with recursive CTE. You can try the below query.
select t.id, t.unique_id, #uid := t.reference_id reference_id
from (select * from mytable order by id desc) t
join (select #uid := 66) tmp
where t.id = #uid or reference_id=66

select groups of adjacent rows randomly

I have a huge table, I want to select groups of rows randomly.
The classic random query (SELECT * FROM table ORDER BY RAND() LIMIT 1000;
) selects not adjacent rows, but I want to select random groupS of n rows (in my picture n = 3rows).
The following picture is just example, the rows are random with every execution.
Not perfect - but maybe adequate for your purposes...
SELECT * FROM my_table;
+-----+
| id |
+-----+
| 1 |
| 2 |
| 3 |
...
| 188 |
| 189 |
| 190 |
| 191 |
...
| 253 |
| 254 |
| 255 |
| 256 |
+-----+
SELECT DISTINCT a.* FROM my_table a JOIN (SELECT * FROM my_table ORDER BY RAND() LIMIT 10) b ON b.id BETWEEN a.id AND a.id+2 ORDER BY id;
+-----+
| id |
+-----+
| 1 |
| 31 |
| 32 |
| 33 |
| 108 |
| 109 |
| 110 |
| 144 |
| 145 |
| 146 |
| 166 |
| 167 |
| 168 |
| 199 |
| 200 |
| 201 |
| 202 |
| 203 |
| 204 |
| 225 |
| 226 |
| 227 |
| 232 |
| 233 |
| 234 |
| 246 |
| 247 |
| 248 |
+-----+
28 rows in set (0.00 sec)
Assuming langids are contiguous you can select one group with SELECT ... WHERE id>3*r and id<=3*(r+1) where r is a random integer from 1 to MAX(id)/3. Multiplying r by 3 ensures no groups will overlap.
You could create a temporary table or subquery by SELECT DISTINCT CAST(langid/3 AS INT), order it randomly, and select the first N of them, then join against this table.
Consider this
SELECT id, name, #rank:=#rank+1 AS rank, CAST(rank/3 AS INT) AS groupid FROM
(SELECT id, name FROM Objects) z, (SELECT #rank:=0) zz;
This result set will give new contiguous IDs to the rows in the Objects table, so we don't have to assume anything about their actual primary keys. groupid indexes the groups.
From this set you can select any number of groupids randomly, and then for each chosen groupid you can find the original primary key.

MySQL SUM with same ID

Sorry for the real simple question, I just learn PHP & MySQL, I already googling it for more than a week but I didn't found any answer.
I create a simple finance script and the table is like below :
table_a
aid | value
1 | 100
2 | 50
3 | 150
table_b
bid | aid | value
1 | 1 | 10
2 | 1 | 15
3 | 2 | 5
4 | 2 | 10
5 | 3 | 25
6 | 3 | 40
I want the result like this
No | ID | Total | Balance
1 | 1 | 10 | 90
2 | 1 | 25 | 75
3 | 2 | 5 | 45
4 | 2 | 15 | 35
5 | 3 | 25 | 125
6 | 3 | 65 | 85
Can anybody help me with my problem?
Thanks
Try this running total: http://www.sqlfiddle.com/#!2/ce765/1
select
bid as no, value,
#rt := if(aid = #last_id, #rt + value, value) as total,
#last_id := aid
from table_b b, (select #rt := 0 as x, #last_id := null) as vars
order by b.bid, b.aid;
Output:
| NO | VALUE | TOTAL | #LAST_ID := AID |
|----|-------|-------|-----------------|
| 1 | 10 | 10 | 1 |
| 2 | 15 | 25 | 1 |
| 3 | 5 | 5 | 2 |
| 4 | 10 | 15 | 2 |
| 5 | 25 | 25 | 3 |
| 6 | 40 | 65 | 3 |
Then join to table A, final query:
select x.no, x.aid, x.value, x.total, a.value - x.total as balance
from
(
select
bid as no, aid, value,
#rt := if(aid = #last_id, #rt + value, value) as total,
#last_id := aid
from table_b b, (select #rt := 0 as x, #last_id := null) as vars
order by b.bid, b.aid
) as x
join table_a a using(aid)
Output:
| NO | AID | VALUE | TOTAL | BALANCE |
|----|-----|-------|-------|---------|
| 1 | 1 | 10 | 10 | 90 |
| 2 | 1 | 15 | 25 | 75 |
| 3 | 2 | 5 | 5 | 45 |
| 4 | 2 | 10 | 15 | 35 |
| 5 | 3 | 25 | 25 | 125 |
| 6 | 3 | 40 | 65 | 85 |
Live test: http://www.sqlfiddle.com/#!2/ce765/1
UPDATE
Not dependent on column bid sorting, running total on grouping will not be impacted: http://www.sqlfiddle.com/#!2/6a1e6/3
select x.no, x.aid, x.value, x.total, a.value - x.total as balance
from
(
select
#rn := #rn + 1 as no, aid, value,
#rt := if(aid = #last_id, #rt + value, value) as total,
#last_id := aid
from table_b b, (select #rt := 0 as x, #last_id := null, #rn := 0) as vars
order by b.aid, b.bid
) as x
join table_a a using(aid)
Output:
| NO | AID | VALUE | TOTAL | BALANCE |
|----|-----|-------|-------|---------|
| 1 | 1 | 10 | 10 | 90 |
| 2 | 1 | 15 | 25 | 75 |
| 3 | 1 | 7 | 32 | 68 |
| 4 | 2 | 5 | 5 | 45 |
| 5 | 2 | 10 | 15 | 35 |
| 6 | 3 | 25 | 25 | 125 |
| 7 | 3 | 40 | 65 | 85 |
Live test: http://www.sqlfiddle.com/#!2/6a1e6/3
SELECT
tb.bid as No,
ta.aid as ID,
tb.value as Total,
ta.value-tb.total as Balance
FROM
table_a AS ta
INNER JOIN (
SELECT
tbx.aid AS aid,
tbx.bid AS bid,
tbx.value AS value,
SUM(tby.value) AS total
FROM
table_b AS tbx
INNER JOIN table_b AS tby ON tby.aid=tbx.aid AND tby.bid<=tbx.bid
GROUP BY tbx.bid
ORDER BY tbx.bid
) AS tb ON tb.aid=ta.aid
ORDER BY tb.bid
As #Quassnoi pointed out, this is not very efficient with MySQL. I tried to use a freak join instead of a subquery, as the inner query might be of use in its own right.
Edit
Took some interest in this and found the join version to be twice as fast as the subquery version by #Quassnoi ... anybody having an idea why this would be?
Edit
Answer to the second question (in comment below):
SELECT
table_a.aid AS aid,
SUM(table_b.value) AS Total,
table_a.value-SUM(table_b.value) AS Balance
FROM
table_a
INNER JOIN table_b ON table_a.aid=table_b.aid
GROUP BY table_a.aid
You are looking for analytics functions. Unfortunately, MySQL lacks them.
You can implement it in a less efficient way:
SELECT bid, aid, total, value - total
FROM (
SELECT b.bid, b.aid, COALESCE(a.value, 0) AS value,
(
SELECT SUM(value)
FROM b bp
WHERE bp.aid = b.aid
AND bp.bid <= b.bid
) AS total
FROM b
LEFT JOIN
a
ON a.aid = b.aid
) q