MySQL GROUP BY and HAVING - mysql

I'm grouping my results based on a column X and I want to return the rows that has highest Column Y's value in the group.
SELECT *
FROM mytable
GROUP BY col1
HAVING col2 >= (SELECT MAX(col2)
FROM mytable AS mytable2
WHERE mytable2.col1 = mytable.col1 GROUP BY mytable2.col1)
I want to optimize the query above. Is it doable without sub-queries?
I found the solution and it's simpler than you think:
SELECT * FROM (SELECT * FROM mytable ORDER BY col2 DESC) temp GROUP BY col1
Runs in 5 milliseconds on 20,000 rows.

Using a derived table/inline view for a JOIN:
SELECT x.*
FROM mytable x
JOIN (SELECT t.col1,
MAX(t.col2) AS max_col2
FROM MYTABLE t
GROUP BY t.col1) y ON y.col1 = x.col1
AND y.max_col2 >= x.col2
Be aware that this will duplicate x records if there's more than one related y record. To remove duplicates, use DISTINCT:
SELECT DISTINCT x.*
FROM mytable x
JOIN (SELECT t.col1,
MAX(t.col2) AS max_col2
FROM MYTABLE t
GROUP BY t.col1) y ON y.col1 = x.col1
AND y.max_col2 >= x.col2
The following is untested, but will not return duplicates (assuming valid):
SELECT x.*
FROM mytable x
WHERE EXISTS (SELECT NULL
FROM MYTABLE y
WHERE y.col1 = x.col1
GROUP BY y.col1
HAVING MAX(y.col2) >= x.col2)

Your Col2 never be > then MAX(col2) so i suggest to use col2 = MAX(col2)
so HERE is the QUERY
SELECT * FROM mytable GROUP BY col1 HAVING col2 = MAX( col2 )

Related

Select rows with non matching column

I am trying to retrieve rows with same Volume value or with only 1 Volume, but could not come up with a SQL logic.
Data:
ID
Volume
A
100
A
100
B
101
B
102
B
103
B
104
C
400
Required Output:
ID
Volume
A
100
A
100
C
400
This one is achievable using a subquery.
select * from test where col1 in (
select t.col1
from(
select col1, col2,
dense_rank() over (partition by col1 order by col2) as dr
from test) t
group by t.col1
having sum(case when t.dr = 1 then 0 else t.dr end) = 0)
Try this dbfiddle.
This can be done on a more easy way:
select t1.id,
t1.volume
from tbl t1
inner join (select id
from tbl
group by id
having count(distinct volume) = 1
) as t2 on t1.id=t2.id;
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=92bc234e631a1106b0e322bc4954d696
having count(distinct volume) = 1 will return only the id that have the same volume , including the id with just one volume.
I'd naturally be inclined towards Ergest Basha's pattern.
It can also be expressed using NOT EXISTS()
SELECT
t.*
FROM
tbl AS t
WHERE
NOT EXISTS (
SELECT *
FROM tbl
WHERE id = t.id
AND volume <> t.volume
)
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=243c42008f527391514d1ad124730587

select data from previous row as current row data

I have the following select in my mysql database:
select t.col1, t.THE_DATE, (select ?? as PREVIOUS_DATE)
from DUMMY_TABLE t
order by date
What I am trying to achieve is have the 'PREVIOUS_DATE' contain the value of the previous row's 'THE_DATE' column if there is one.
So if DUMMY_TABLE has the data :
col1 THE_DATE
x 10-01-2010
x 10-01-2012
x 10-01-2009
my select should return
col1 THE_DATE PREVIOUS_DATE
x 10-01-2009
x 10-01-2010 10-01-2009
x 10-01-2012 10-01-2010
You need order by clause in subquery with limit clause :
select t.col1, t.the_date,
( select t1.the_date
from dummy_table t1
where t1.col = t.col and
t1.the_date < t.the_date
order by t1.the_date desc
limit 1
) as PREVIOUS_DATE
from dummy_table t
order by the_date;

DB2 Show the latest row record from users, using the date and time

I have 100 records from 3 users. I want to show the most recent record from each user. I have the following query:
SELECT *
FROM Mytable
WHERE Dateabc = CURRENT DATE
AND timeabc =
(
SELECT MAX(timeabc)
FROM Mytable
)
It returns the most recent record for everyone, and I need it to return most recent record from every user.
Should the solution support both DB2 and mysql?
SELECT * FROM Mytable as x
WHERE Dateabc = CURRENT_DATE
AND timeabc = (SELECT MAX( timeabc ) FROM Mytable as y where x.user = y.user)
If it's only DB2 more efficient solutions exists:
SELECT * from (
SELECT x.*, row_number() over (partition by user order by timeabc desc) as rn
FROM Mytable as x
)
WHERE rn = 1
I assume somewhere in your table you have a userID...
select userID, max(timeabc) from mytable group by userID
SELECT *
FROM Mytable as a
WHERE Dateabc = CURRENT_DATE
AND timeabc =
(
SELECT MAX( timeabc )
FROM Mytable as b
WHERE a.uId = b.uId
)

How to show MIN value with whole table

I have table like:
id col1 col2
1 a 55
2 b 77
In result i want to see:
id col1 col2 MIN(col2)
1 a 55 55
2 b 77
Something like that, or in other case, how i can get one minimum value with whole table.
You can use a CROSS JOIN with a subquery which will select the min(col2) value for the entire table:
select t1.id,
t1.col1,
t1.col2,
t2.minCol2
from yourtable t1
cross join
(
select min(col2) minCol2
from yourtable
) t2
See SQL Fiddle with Demo.
If you want to expand this to only show the min(col2) value on the first row, then you could use user-defined variables:
select id,
col1,
col2,
case when rn = 1 then mincol2 else '' end mincol2
from
(
select t1.id,
t1.col1,
t1.col2,
t2.minCol2,
#row:=case when #prev:=t1.id then #row else 0 end +1 rn,
#prev:=t1.id
from yourtable t1
cross join
(
select min(col2) minCol2
from yourtable
) t2
cross join (select #row:=0, #prev:=null) r
order by t1.id
) d
order by id
See SQL Fiddle with Demo
If you had more than one column that you want to compare, then you could unpivot the data using a UNION ALL query and then select the min value for the result:
select t1.id,
t1.col1,
t1.col2,
t2.MinCol
from yourtable t1
cross join
(
select min(col) MinCol
from
(
select col2 col
from yourtable
union all
select col3
from yourtable
) src
) t2
See SQL Fiddle with Demo
You can't. The number of columns is fixed, so you can get the minimum value on all the rows as described by #bluefeet.
You could get it on a smaller number of rows (typically 1) by using the logic:
(case when t2.minCol2 = t1.col2 then t2.minCol2 end)
But this would put NULLs on the other rows.

calculate frequency using sql

I have a table in MySQL:
Col1 | Col2
a A
a B
c C
a B
i want to create a table like this:
col1 | col2 | freq
a A 0.33
a B 0.67
col1 is a specified item in Col1. col2 is distinct item that has occured with the specified item(i.e. a). freq column is the frequency of appearence of item in col2.
Can someone give me a hint of how to create such a query? Thanks a lot.
try this:
Select A.Col1, A.Col2, A.Count1 * 1.0 / B.Count2 As Freq
From (
Select Col1, Col2, Count(*) As Count1
From YourTableName
Group By Col1, Col2
) As A
Inner Join (
Select Col1, Count(*) As Count2
From YourTableName
Group By Col1
) As B
On A.Col1 = B.Col1
You can also use this which is coded in SQL server
DECLARE #Count INT;
SELECT #Count = COUNT(1) FROM YourTableName WHERE Col1 = 'a'
SELECT Col1, Col2, CAST(COUNT(1)* 1.00 /#Count AS DECIMAL(4,2) ) AS Frequency
FROM YourTableName
WHERE Col1 = 'a'
GROUP BY Col1, Col2
this way you have a better performance
With window functions
SELECT Col1, Col2, Count1*1.0 / Count2 AS freq
FROM (
SELECT
Col1,
Col2,
COUNT() OVER(PARTITION BY Col1, Col2) AS Count1,
COUNT() OVER(PARTITION BY Col1) AS Count2
FROM YourTableName
)
GROUP BY Col1, Col2