Sort records based on string - mysql

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).

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

Get max ID for every Type and every Date from a lookup table

I want to keep the highest report id (Report_ID) for every type (Types) for every single date (Date)
Note: The data column has multiple dates, only 01.01.2021 is shown below.
Question: t1 is the lookup table that I need to use and my challenge is that it does not contain a date column for reference.
select t2.*
from t2
where t1.Report_ID = (select max(t1.Report_ID)
from t1
where t2.Date = ??? and t2.Types = ???
);
t1
Report_ID
Name
Value
1
Name 1
Value 1
2
Name 2
Value 2
3
Name 3
Value 3
t2
Date
Types
Report_ID
Name
01.01.2020
Type 1
1
Name 1
01.01.2020
Type 1
2
Name 2
01.01.2020
Type 3
3
Name 3
view
Date
Types
Name
Value
Report_ID
01.01.2020
Type 1
Name 2
Value 2
2
01.01.2020
Type 3
Name 3
Value 3
3
With this query:
SELECT Date, Types, MAX(Report_ID) Report_ID
FROM t2
GROUP BY Date, Types
you get the max Report_ID for each Date and Types
Join it to t1:
SELECT t2.Date, t2.Types, t1.Name, t1.Value, t1.Report_ID
FROM t1
INNER JOIN (
SELECT Date, Types, MAX(Report_ID) Report_ID
FROM t2
GROUP BY Date, Types
) t2 ON t2.Report_ID = t1.Report_ID
See the demo.
Results:
Date
Types
Name
Value
Report_ID
2020-01-01
Type 1
Name 2
Value 2
2
2020-01-01
Type 3
Name 3
Value 3
3
Using ROW_NUMBER():
WITH cte AS (
SELECT t2.*, t1.Value,
ROW_NUMBER() OVER(PARTITION BY `Date`, Types ORDER BY Report_ID DESC) AS rn
FROM t2
JOIN t1 ON t1.Report_ID = t2.Report_ID
)
SELECT * FROM cte WHERE rn = 1;
db<>fiddle demo
You can use NOT EXISTS as follows:
select t2.*
from t2
--join t1 on t1.Report_ID = t2.Report_ID -- use it if you want data from t1 in SELECT
where not exists
(select 1 from t2 t22
where t22.date = t2.date and t22.type = t2.type
and t22.Report_ID > t2.Report_ID)
This answers the original version of the question.
I want to keep the highest report id (Report_ID) for every type (Types) for every single date (Date)
The reference table is not needed for this. Your logic should do what you want with t2 in the subquery:
select t2.*
from t2
where t2.Report_ID = (select max(tt2.Report_ID)
from t2 tt2
where tt2.Date = t2.date and tt2.Type = t2.Type
);
You can easily achieve that through row_number() and CTE. First we need to join t1 and t2 to get the value column from t1. We used row_number() to put a sequence number in every row starting from highest Report_ID to lowest for a particular type in a given date.
Then we only consider the rows with lowest sequence number which represents highest report_id for any particular type of a given da.
With cte as
(
select t2.date,t2.types,t2.report_id,t2.name ,t1.value ,row_number () over (partition by date,types order by t2.report_id desc) RowNumber
from t2 inner join t1 on t2.report_id=t1.report_id
)
select date_format(date,"%Y.%m.%d") date,types,name,value,report_id from cte where RowNumber=1
Output:

Select all orders except the max order for each distinct customer

Sorry for the poor formatting but as part of a larger problem, I have created a query that produces this table:
id id2
4 7
4 6
1 3
1 2
1 1
How would I extract the rows that don't have the highest id2 for each id1.
What I want:
id id2
4 6
1 2
1 1
I can only seem to figure out how to get rid of the max id2 overall but not for each distinct id1. Any help on actually differentiating the max id2 for each id1 would be appreciated.
You can try below way -
select a.id, a.id2
from tablename a
where a.id2 <> (select max(a1.id2) from tablename a1 where a.id=a1.id)
If you are using MySQL 8+, then RANK() provides one option:
WITH cte AS (
SELECT id, id2, RANK() OVER (PARTITION BY id ORDER BY id2 DESC) rnk
FROM yourTable
)
SELECT id, id2
FROM cte
WHERE rnk > 1
ORDER BY id DESC, id2 DESC;
Demo
instead of a correlated subquery in the where, you can LEFT JOIN and apply not in...
select id, id2
from yourTable YT
LEFT JOIN
( select id, max( id2 ) highestID2
from YourTable
group by id ) TopPerID
on YT.ID = TopPerID.ID
AND YT.ID2 != TopPerID.highestID2
where TopPerID.id IS NULL
Since you can have id values with only one id2 value, you need to check for that situation as well, which you can do by comparing the MAX(id2) value with the MIN(id2) value in a JOIN:
SELECT t1.*
FROM Table1 t1
JOIN (SELECT id, MAX(id2) AS max_id2, MIN(id2) AS min_id2
FROM Table1
GROUP BY id) t2 ON t2.id = t1.id
AND (t1.id2 < t2.max_id2 OR t2.min_id2 = t2.max_id2)
If we add a row 2, 5 to your sample data this correctly gives the result as
id id2
4 6
1 2
1 1
2 5
Demo on SQLFiddle

Remove duplicate based on 2 rows value

So I have a database with more than 2000 line and I wanted to delete the duplicate value based on 2 rows.For Example :
no server name
1 serv1 a
2 serv1 b
3 serv1 b
4 serv1 b
5 serv2 a
6 serv2 b
7 serv2 c
8 serv2 c
So basically I wanted to remove the duplicate IF two of the rows have a duplicateBUT I dont want to remove them if just one of the row has duplicate.
Expected Output:
no server name
1 serv1 a
2 serv1 b
3 serv2 a
4 serv2 b
5 serv2 c
Any answer would be appreciated.
There is many ways to do what you want.
If you 're looking for just SELECTing the data without duplicates then you could use:
DISTINCT
SELECT DISTINCT Server,
Name
FROM YourTableName
GROUP BY
SELECT Server,
Name
FROM YourTableName
GROUP BY Server, Name
Window function (ROW_NUMBER) in a subquery
SELECT Server,
Name
FROM
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY Server, Name ORDER BY Server) RN
FROM YourTableName
) TT
WHERE TT.RN = 1
You delete the duplicates as
DELETE T1
FROM
(
SELECT ROW_NUMBER() OVER(PARTITION BY Server, Name ORDER BY Server) RN
FROM T --YourTableName
) T1 JOIN
(
SELECT ROW_NUMBER() OVER(PARTITION BY Server, Name ORDER BY Server) RN
FROM T --YourTableName
) T2 ON T1.RN = T2.RN
WHERE T1.RN > 1;
Use select distinct:
select distinct server, name
from t;
If you have a lot of duplicates, the simplest way is probably to recreate the table:
create table temp_t as
select min(no) as no, server, name
from t
group by server, name;
truncate table t;
insert into t (no, server, name)
select no, server, name
from temp_t;
If you don't have many rows, then delete is fine:
delete t
from t join
(select server, name, min(no) as min_no
from t
group by server, name
) tt
on t.server = tt.server and t.name = tt.name
where t.no > tt.min_no;
You can use the following query to remove duplicates
DELETE t1 FROM tablename t1 INNER JOIN tablename t2
WHERE t1.id < t2.id AND t1.server = t2.server AND t1.name = t2.name;

Condensing MySQL ordered group values

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.