Condensing MySQL ordered group values - mysql

This question is an extension of mysql compress order_by values.
My table has groups of ordered numbers, with undesired gaps. How can I renumber each of these groups, while keeping the original order?
Group Order Desired Order
A 1 1
A 3 2
A 6 3
A 7 4
B 2 1
B 3 2
B 8 3
C 1 1
C 7 2
C 8 3

You can also get the desired order by using a correlated sub query without using and variables
select t1.*,
(
select count(*)
from demo t2
where t1.`group` = t2.`group`
and t1.`order` > t2.`order`
) + 1 desiredorder
from demo t1
DEMO
Or to update same table with your desired order you can use below query
update demo a
join (select t1.*,(
select count(*)
from demo t2
where t1.`group` = t2.`group`
and t1.`order` > t2.`order`
) + 1 desiredorder
from demo t1
) b on a.`group` = b.`group`
and a.`order` = b.`order`
set a.`order` = b.desiredorder
DEMO
Note make sure to add an index on group and order column for better performance.

You can create a variable to store the previous value of group and another one for the desiredorder. Essentially this is a mimic of a Window Function ROW_NUMBER which MySql doesn't support.
I renamed the columns as group=col1, order=col2
select col1,
col2,
neworder
from (
select t.col1,
t.col2,
case when #prev=t.col1 then #id:=#id+1 else #id:=1 end as neworder,
#prev:=t.col1
from (select #prev:=null, #id:=0) a,
toorder t
order by t.col1
) a
See it working here: http://sqlfiddle.com/#!9/9d8528/2
The external query is only needed if you want to pull out only those three columns.

Related

SQL add same contents

How can I delete all columns that are duplicate and don't have the biggest "amount". I have the following table:
ID TIME AMOUNT
-----------------------------------
1 x 5
2 y 1
2 y 3
3 z 1
3 z 2
3 z 3
But I want it to be like this, so that only the column which has the biggest number "survives":
ID TIME AMOUNT
------------------------------------
1 x 5
2 y 3
3 z 3
How can I do this?
You can get the max amount per id and time and then get the rows matching:
select t.Id, t.Time, t.amount
from myTable t
inner join
(select Id, time, max(amount) as amt
from myTable
group by Id, Time) tmp on t.id = tmp.id and
t.time = tmp.time and
t.amount = tmp.amt
DbFiddle demo
EDIT: You may want to add DISTINCT depending on your needs.
One other approach using a CTE
with del as (
select *,
First_Value(amount) over(partition by id order by amount desc) maxamount
from t
)
delete from t
using t join del on t.id = del.id and t.amount < maxamount;
WITH cte AS
(
SELECT
ID,
ROW_NUMBER() OVER (PARTITION BY TIME ORDER BY AMOUNT DESC) AS ROWNUM
FROM
MyTable
)
DELETE MyTable
FROM MyTable
JOIN cte USING (ID)
WHERE ROWNUM > 1;
WITH syntax requires MySQL 8.0.
I think some of the answers here are overly complicated.
delete t
from yourtable t
join yourtable t2 on t.id = t2.id
and t.time = t2.time
and t2.amount > t.amount

Sort records based on string

Please consider the table below
Id F1 F2
---------------------------
1 Nima a
2 Eli a
3 Arian a
4 Ava b
5 Arsha b
6 Rozhan c
7 Zhina c
I want to display records by sorting COLUMN F2 to display one record from each string category (a,b,c) in order
Id F1 F2
---------------------------
1 Nima a
5 Arsha b
6 Rozhan c
2 Eli a
4 Ava b
7 Zhina c
3 Arian a
NOTE: a,b,c could be anything... it should take one record from one entry and then 2nd from 2nd entry.
I have used join, or group by records but no success.
MySQL version 5.7 – Syed Saqlain
SELECT id, f1, f2
FROM ( SELECT t1.id, t1.f1, t1.f2, COUNT(*) cnt
FROM test t1
JOIN test t2 ON t1.f2 = t2.f2 AND t1.id >= t2.id
GROUP BY t1.id, t1.f1, t1.f2 ) t3
ORDER BY cnt, f2;
https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=8138bd9ab5be36ba534a258d20b2e555
ROW_NUMBER() alternative for lower version of MYSQL. This query will work for version 5.5, 5.6 & 5.7.
-- MySQL (v5.7)
SELECT t.id, t.f1, t.f2
FROM (SELECT #row_no:=CASE WHEN #db_names=d.f2 THEN #row_no+1 ELSE 1 END AS row_number
, #db_names:= d.f2
, d.f2
, d.f1
, d.id
FROM test d,
(SELECT #row_no := 0,#db_names:='') x
ORDER BY d.f2) t
ORDER BY t.row_number, t.f2
Please check from url https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=02dbb0086a6dd7c926d55a690bffbd06
You can use window functions in the order by:
select t.*
from t
order by row_number() over (partition by f2 order by id),
f2;
The row_number() function (as used above) assigns a sequential number starting with 1 to each value of f2.
In older versions of MySQL, you can use a correlated subquery instead:
order by (select count(*) from t t2 where t2.f2 = t.f2 and t2.id <= t.id),
f2;
For performance, you want an index on (f2, id).

Mysql query configuration

I have this query and runs perfectly, i just want to short the results to help me for quick reading :
SELECT numbers
FROM vista
WHERE id IN (
SELECT b.id + 3 FROM(
SELECT t1.id, t1.numbers t1val, t2.numbers t2val
FROM vista t1
JOIN vista t2 ON t1.id = t2.id-1
JOIN vista t3 ON t1.id = t3.id-2
WHERE t1.id = (SELECT MAX(id) - 2 FROM vista)
) a
JOIN (
SELECT t1.id, t1.numbers t1val, t2.numbers t2val, t3.numbers t3val
FROM vista t1
JOIN vista t2 ON t1.id = t2.id-1
JOIN vista t3 ON t1.id = t3.id-2
WHERE t1.id < (SELECT MAX(id) - 2 FROM vista)
) b
ON a.t1val = b.t1val
AND a.t2val = b.t2val
AND a.t3val = b.t3val
AND a.id <> b.id
)
ORDER BY id;
And the results it is like :
3
5
2
7
5
5
7
3
How can i get results counted in group and percentage :
Like :
5 = 3 (37.50%)
7 = 2 (25.00%)
3 = 2 (25.00%)
2 = 1 (12.50%)
Well if your query works than you should do something like this to achieve result you want:
Solution one - this will give you numbers, countend numbers in group and percentage in seperate column...
SELECT numbers, COUNT(numbers), (COUNT(numbers) / (SELECT COUNT(numbers)
FROM vista
/*your WHERE clause*/)) * 100 AS percent
FROM vista
-- your WHERE Statement
GROUP BY numbers
-- your ORDER BY
Here is a sql fiddle to see how that's look like http://sqlfiddle.com/#!9/08b4e/3
Solution two - this will give you numbers in one column and counted numbers in group and percentage in other:
SELECT numbers, CONCAT(CountNum, ' (', percent, '%)') AS counted
FROM (SELECT numbers, COUNT(numbers) AS CountNum, (COUNT(numbers) / (SELECT COUNT(numbers)
FROM vista
WHERE ID BETWEEN 1 AND 8
/*your WHERE clause*/)) * 100 AS percent
FROM vista
WHERE ID BETWEEN 1 AND 8
-- your WHERE clause this is just for a testing purpose
GROUP BY numbers
ORDER BY ID) AS srcTable
-- your ORDER BY clause this is just for a testing purpose
Here is Fiddle for that http://sqlfiddle.com/#!9/51275/1
Of course you need to insert your WHERE clause instead of mine i just put this one to test it. GL i hope i help a little.

Select Top 1 row for distinct id sqlite

I got following table content :
> id text time
> 1 Hi 2
> 2 Hello 1
> 1 Mr. SP 3
> 3 KK 1
> 2 TT 2
> 1 Sample 1
> 1 App 4
and i need to select distinct values for id with text, hence output should be:
id text time
1 App 4
2 TT 2
3 KK 1
Can anyone help me out with this query.
I'm using Sqlite
Thanks
select b.*
from
(
select id, max(time) as time
from yourtable
group by id) a inner join yourtable b on a.time = b.time and a.id = b.id
working demo: http://sqlfiddle.com/#!5/fbb87/5
You seem to want the value with the maximum time. The following is often an efficient way to get this:
select t.*
from table t
where not exists (select 1 from table t2 where t2.id = t.id and t2.time > t.time);
What this is saying is: "Get me all rows from the table where the id does not have a row with a larger id". With an index on table(id, time), this is probably the most efficient way to express this query.
select * from (
select id,[text],
ROW_NUMBER() OVER (PARTITION BY id ORDER BY [time] desc) as rn
from [table]) as cte
where rn = 1

Mysql query random order (pause and continue) question

Okay i am trying to create a mysql query that does this:
show 3 random records from table then after the 3th record show TEXT
and then show the same 3 items but other field (equaling to the items ofcourse) from same table.
eg table info:
--ids | titles------
10 | one
20 | two
30 | three
and the query results from the given example:
30 10 20 TEXT three one two
if anyone understand what i am asking,post your suggestion/asnwer
thanks for your time all :)
Just for kicks..
select t1.id, t2.id, t3.id, 'TEXT', t1.title, t2.title, t3.title
FROM
(
select #r := #r + 1 rownum, id
from (select #r:=0) initvar, (
select id
from tbl
order by rand()
limit 3
) X
) Y
join tbl t1 on Y.rownum=1 and t1.id = Y.id
join tbl t2 on Y.rownum=2 and t2.id = Y.id
join tbl t3 on Y.rownum=3 and t3.id = Y.id
You should really just do the query below, and do whatever display processing using the 3 rows returned, in whatever programming environment you use (Java/PHP/.Net etc).
select id, title
from tbl
order by rand()
limit 3
EDIT
To get the data in 7 different rows, you can use the below. I stress again that this is front-end display code. I will not use such SQL code in a production system.
select display
from
(
select sorter, rownum,
case when sorter=3 then title else id end display
from
(
select #r := #r + 1 rownum, id, title
from (select #r:=0) initvar,
(
select id, title
from tbl
order by rand()
limit 3
) X
) Y, (select 1 sorter union all select 3) dup
union all
select 2, 0, 'TEXT'
) Z
order by sorter, rownum
Example Output
7
2
1
TEXT
test 7 << title for id=7
test 2
test 1