I've data like below:
ID Task Time
1 X started T1
2 X ended T2
3 X started T3 [wrong entry in data]
4 X started T4
5 X ended T5
6 Y started T6 [wrong entry in data]
7 Y started T7
8 Y ended T8
And, I need to get the data from above in started/ended fashion, but in case of wrong entry I need to pickup the latest one [as T4>T3 and T7>T6].
How can I write SQL on above dataset to get below result ?
ID Task Time
1 X started T1
2 X ended T2
4 X started T4
5 X ended T5
7 Y started T7
8 Y ended T8
You may use the difference of two row_number()s to define unique groups for the consecutive duplicate values of Task column, then get the max of Time and ID columns, try the following:
select max(ID), Task, max(Time) Time
from
(
select *,
row_number() over (order by Time) -
row_number() over (partition by Task order by Time) grp
from table_name
) T
group by Task, grp
order by max(Time)
See a demo.
For MySQL 5.7 try the following:
set #gr=0;
set #ts=null;
select max(ID), Task, max(Time)
Time
from
(
select *,
if(#ts<>Task, #gr:=#gr+1,
#gr) grp,
#ts:=Task
from table_name
order by Time
) T
group by Task, grp
order by max(Time)
Demo.
use a subquery in the where to match the ID, the subquery returns the ID of the row that matches the task (in the primary query), if there is more than one row with the same task value, it will return the row with the greatest id value.
SELECT * FROM `mytable` p1
where id = (select id from `mytable` p2
where p2.task= p1.task
order by id DESC limit 1);
Related
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).
I have this column: name and price. I don't really know how or why in mysql database there are few line that are double record exactly from the previous line.
how to select all records but show only one of the records if the record is double with a line in front or behind it?
For example I have this records:
id
name
price
1
book
5
2
lamp
7
3
lamp
7
4
book
5
5
book
5
the result I want is:
id
name
price
1
book
5
2
lamp
7
4
book
5
If you want to exclude rows that match the previous name, there are several ways like the following.
Case 1:
If you use MySQL8, you can use the LAG function.
SELECT t1.id,t1.name,t1.price FROM (
SELECT t2.id,t2.name,t2.price,
LAG(t2.name) OVER(ORDER BY t2.id) prev
FROM mytable t2
) t1
WHERE t1.prev IS NULL OR t1.name<>t1.prev
ORDER BY 1
Case 2:
If the ids are continuous without any steps, you will get the expected result by comparing name and the previous id by JOIN.
SELECT t1.id,t1.name,t1.price FROM mytable t1
LEFT JOIN mytable t2
ON t1.name=t2.name AND
t1.id=t2.id-1
WHERE t1.id=1 OR t2.id IS NOT NULL
ORDER BY 1
Case 3:
If the ids are not continuous, there is a way to get the maximum id that does not exceed the other id.
SELECT t1.id,t1.name,t1.price FROM mytable t1
LEFT JOIN mytable t2
ON t1.name=t2.name AND
t1.id=(SELECT MAX(t3.id) FROM mytable t3 WHERE t3.id<t2.id)
WHERE t1.id=1 OR t2.id IS NOT NULL
ORDER BY 1
DB Fiddle
Select distinct is not an option here as id column is always unique. I guess this will work for you:
select min(id), name, price from table_name group by name, price
I am facing a problem with MySQL query which is a variant of "Id for row with max value". I am either getting error or incorrect result for all my trials.
Here is the table structure
Row_id
Group_id
Grp_col1
Grp_col2
Field_for_aggregate_func
Another_field_for_row
For all rows with a particular group_id, I want to group by fields Grp_col1, Grp_col2 then get max value of Field_for_aggregate_func and then corresponding value of Another_field_for_row.
Query I have tried is like below
SELECT c.*
FROM mytable as c left outer join mytable as c1
on (
c.group_id=c1.group_id and
c.Grp_col1 = c1.Grp_col1 and
c.Grp_col2 = c1.Grp_col2 and
c.Field_for_aggregate_func > c1.Field_for_aggregate_func
)
where c.group_id=2
Among alternative solutions for this problem I want a high performance solution as this will be used for large set of data.
EDIT: Here is the sample set of row and expected answer
Group_ID Grp_col1 Grp_col2 Field_for_aggregate_func Another_field_for_row
2 -- N 12/31/2015 35
2 -- N 1/31/2016 15 select 15 from group for max value 1/31/2016
2 -- Y 12/31/2015 5
2 -- Y 1/1/2016 15
2 -- Y 1/2/2016 25
2 -- Y 1/3/2016 30 select 30 from group for max value 1/3/2016
You can use a sub-query to find the maximums, then join that with the original table, along the lines of:
select m1.group_id, m1.grp_col1, m1.grp_col2, m1.another_field_for_row, max_value
from mytable m1, (
select group_id, grp_col1, grp_col2, max(field_for_aggregate_func) as max_value
from mytable
group by group_id, grp_col1, grp_col2) as m2
where m1.group_id=m2.group_id
and m1.grp_col1=m2.grp_col1
and m1.grp_col2=m2.grp_col2
and m1.field_for_aggregate_func=m2.max_value;
Watch out for when there is more than one max_value for the given grouping. You'll get multiple rows for that grouping. Fiddle here.
Try this.
See Fiddle demo here
http://sqlfiddle.com/#!9/9a3c26/8
Select t1.* from table1 t1 inner join
(
Select a.group_id,a.grp_col2,
A.Field_for_aggregate_func,
count(*) as rnum from table1 a
Inner join table1 b
On a.group_id=b.group_id
And a.grp_col2=b.grp_col2
And a.Field_for_aggregate_func
<=b.Field_for_aggregate_func
Group by a.group_id,
a.grp_col2,
a.Field_for_aggregate_func) t2
On t1.group_id=t2.group_id
And t1.grp_col2=t2.grp_col2
And t1.Field_for_aggregate_func
=t2.Field_for_aggregate_func
And t2.rnum=1
Here first I am assigning a rownumber in descending order based on date. The selecting all the records for that date.
I'm trying to figure out best way to take required rows from database.
Database table:
id user cat time
1 5 1 123
2 5 1 150
3 5 2 160
4 5 3 100
I want to take DISTINCT cat ... WHERE user=5 with MAX time value. How should I do that in efficient way?
You will want to use an aggregate function with a GROUP BY:
select user, cat, max(time) as Time
from yourtable
group by user, cat
See SQL Fiddle with Demo
If you want to include the id column, then you can use a subquery:
select t1.id,
t1.user,
t1.cat,
t1.time
from yourtable t1
inner join
(
select max(time) Time, user, cat
from yourtable
group by user, cat
) t2
on t1.time = t2.time
and t1.user = t2.user
and t1.cat = t2.cat
See SQL Fiddle with Demo. I used a subquery to be sure that the id value that is returned with each max(time) row is the correct id.
I have a MySQL table from which I want to select:
1) Either "most occurring" value, if there is any prevailing
2) Or "average" value, if there is no most occurring value.
Example table 1:
value
1
2
3
4
All values are occurred equally, therefore I want to take AVG(`value`)
Example table 2:
value
1
2
2
3
Value 2 prevails, therefore I want to select the value 2.
What mysql query would do this?
Starting from Gordon's answer I tested and corrected the SQL query in SQL Fiddle:
SELECT IF(t4.numcnts = 1, t1.avgvalue, t2.topvalue) AS result
FROM (select avg(value) as avgvalue from test) t1
CROSS JOIN (select value as topvalue from test group by value order by count(*) desc limit 1) t2
CROSS JOIN join (select count(distinct cnt) as numcnts from
(select count(*) as cnt from test group by value) t3) t4
Here is the Fiddle with the two test tables (switch out test2 for test to see the result when a particular value prevails): http://sqlfiddle.com/#!2/76914/3
My changes were to use an IF instead of a CASEstatement in the SELECTclause and to add the necessary table aliases for the subselects.
The following approach calculates both values and then chooses between them:
select (case when numcnts = 1 then avgvalue else topvalue end)
from (select avg(value) as avgvalue from t) cross join
(select value as topvalue from t group by value order by count(*) desc limit 1) cross join
(select count(distinct cnt) as numcnts from (select count(*) as cnt from t group by value))
Note: if you have ties for the top, but other values as well, then an arbitrary value is returned. You don't specify what to do in this case.
Also, the SQL is untested, so it might have syntax errors.