Sub select of row that has max column - mysql

I'm trying to do something that sounds really simple but I have been going round in circles a little with it..
I have a stored procedure that currently works as required missing only one bit of functionality, to return a name for a corrosponding max calculation...
So I return
average calculation &
max calculation but want to return 'the name from another column' for the max value.
Here is an example of my SP, apologies that it may not seem very natural as I have had to rename and omit non relevant bits so may seem a little contrived::
SELECT
IFNULL(ROUND(AVG(TABLE1.TotalCapacityPercentageUsage / TABLE1.TotalSnapshotsForTimeSegment), 2), 0.0) AS TotalAvgCapacityPercentageUsage,
IFNULL(ROUND(MAX(TABLE1.MaxCapacityPercentageUsage), 2), 0.0) AS TotalMaxCapacityPercentageUsage,
-- TODO return the QueuesTmp.QueueName for max calculation (This could be more than one row, so I was going to use something like the following:
-- (SELECT GROUP_CONCAT(QueuesTmp.QueueName SEPARATOR ' ') to ensure only one field is returned..
FROM TABLE1
INNER JOIN QueuesTmp ON QueuesTmp.QueueID = TABLE1.QueueID
RIGHT JOIN TimesTmp ON TABLE1.TimeSegment = TimesTmp.QuarterHour AND
TABLE1.Date = DATE(TimesTmp.StartOfRangeUTC)
GROUP BY TimesTmp.QuarterHour;
I started by doing a Sub select but it seems I would then have to repeat all of the Joins, WHERE and Group By (Seems this is not even possible because that's what having is for)..
Can anybody guide me in the right direction as to how this can be achieved?
Thanks in advance.
WORKING SOLUTION
GROUP_CONCAT(DISTINCT QueuesTmp.QueueName ORDER BY MYCOLUMN DESC
SEPARATOR ':') AS MaxColumnQueueName,

I'm not sure that I'm on the right way. You need the QueueName of that row with the max - calculation. So use the group_concat with an ORDER BY of this calculation and get with SUBSTRING_INDEX the first element of this list.
substring_index(
GROUP_CONCAT(DISTINCT QueuesTmp.QueueName ORDER BY `maxCalculation` DESC) SEPARATOR ':',
':',
1
)
Additional question.
Sorry unfortunately the max comment space has reached. Here a query.
I used your example - query for sub and select the queueId as comma-separated list and the max(maxColumn) as additional.
After that I join to queue-table again with queueId and maxColumn. I can't guarantee if that works.
SELECT
sub.TotalAvgCapacityPercentageUsage,
sub.TotalMaxCapacityPercentageUsage,
GROUP_CONCAT(DISTINCT QueuesTmp.QueueName ORDER BY MYCOLUMN DESC SEPARATOR ':') AS MaxColumnQueueName
FROM(
SELECT
TimesTmp.QuarterHour,
IFNULL(
ROUND(
AVG(
TABLE1.TotalCapacityPercentageUsage /
TABLE1.TotalSnapshotsForTimeSegment
),
2
),
0.0
) AS TotalAvgCapacityPercentageUsage,
IFNULL(
ROUND(
MAX(TABLE1.MaxCapacityPercentageUsage),
2
),
0.0
) AS TotalMaxCapacityPercentageUsage,
max(QueuesTmp.maxColumn) AS maxColumn,
group_concat(DISTINCT QueueID) AS QueueID
FROM TABLE1
INNER JOIN QueuesTmp
ON QueuesTmp.QueueID = TABLE1.QueueID
RIGHT JOIN TimesTmp
ON TABLE1.TimeSegment = TimesTmp.QuarterHour
AND TABLE1.Date = DATE(TimesTmp.StartOfRangeUTC)
GROUP BY TimesTmp.QuarterHour
) AS sub
LEFT JOIN QueuesTmp
ON QueuesTmp.QueueID IN(sub.QueueID)
AND QueuesTmp.maxColumn = sub.maxColumn

Related

Mysql LEFT to match first 3 chars

Im trying to get all matching records from the invoice_id field where the first 3 characters are RBK, case sensitivity not important. I've tried to use the LEFT function in the bottom 2 ways but its not working. Any ideas on how to achieve this?
SELECT *, IF( LEFT( invoice_id, 3) = 'RBK') FROM `invoices` ORDER BY id ASC
SELECT *, IF( LEFT( invoice_id, 3) = 'RBK', 3, 0) FROM `invoices` ORDER BY id ASC
an if inside the select is not to filter results,if you want to filter result use where clause.
SELECT * FROM `invoices` WHERE LEFT(invoice_id, 3) = "RBK" ORDER BY id ASC

MySQL GROUP BY each comma separated value

Before anyone comments, I did not design this database with comma separated values :)
I have spent time trying to find the answer but all I could find was GROUP_CONCAT() which seemed to do the opposite of what I wanted.
I would like to GROUP BY each of the values within the comma separated value field.
SELECT round(avg(DATEDIFF( dateClosed , dateAded ) * 1.0), 2) AS avg, department
FROM tickets GROUP BY assignedto
the assignedto field is the comma separated value field
row1 54,69,555
row2 54,75,555
row3 75,555
DESIRED OUTPUT: an average rounded figure for each value in assignedto field grouped.
EDIT - TRYING TO TAKE THIS TO THE NEXT LEVEL:
I want to include the ticket answer table to get the first response for that ticket, use its datetime field to work out the average response time for each user.
SELECT a.id as theuser, round(avg(DATEDIFF( ta.dateAded , t.dateAded ) * 1.0), 2) as avg
FROM tickets t join
mdl_user a
on find_in_set(a.id, t.assignedto) > 0
INNER JOIN (SELECT MIN(ta.dateAded) as started FROM ticketanswer GROUP BY ta.ticketId) ta ON t.id = ta.ticketId
GROUP BY a.id ORDER BY avg ASC
Yuck. You can do this, assuming you know the maximum number of assignments. Here is an approach:
select substring_index(substring_index(assignedto, ',', n.n), ',', -1) as assignedto,
round(avg(DATEDIFF( dateClosed , dateAded ) * 1.0), 2) as avg
from tickets t join
(select 1 as n union all select 2 union all select 3)
on length(assignedto) - length(replace(assignedto, ',', '')) < n.n
group by substring_index(substring_index(assignedto, ',', n.n), ',', -1);
Or, an easier way if you have a list of assigned values, say in an AssignedTo table:
select a.assignedto, round(avg(DATEDIFF( dateClosed , dateAded ) * 1.0), 2) as avg
from tickets t join
assignedto a
on find_in_set(a.assignedto, t.assignedto) > 0
group by a.assignedto;
I'm sorry you have to deal with this malformed database structure.

Non correlated values on group by statement

Help me with next SQL:
SELECT
date_format(from_unixtime(`ticket_logs`.`created`),'%Y-%m-%d') AS `datac`,
`ticket_logs`.`ticket_id` AS `ticket_id`,
ticket_logs.value_old,
ticket_logs.value_new,
max(`ticket_logs`.`action`) AS `ultima_act`
FROM
`ticket_logs`
WHERE
(
(`ticket_logs`.`action` = 6)
OR (`ticket_logs`.`action` = 16)
)
GROUP BY
date_format(
from_unixtime(`ticket_logs`.`created`),
'%Y-%m-%d'
),
`ticket_logs`.`ticket_id`
ORDER BY
`datac` DESC,
`ticket_logs`.`ticket_id` DESC
The problem is that "value_old" and "value_new", always take the first value per date and not the value corresponding with the max value of "action"
I don't see how this is a problem. That is how SQL works -- the order by takes place after the group by. In addition, MySQL is just confusing you, because you are using an extension to group by that you don't fully understand -- having extra columns in the select that are not in the group by. (See this.)
Fortunately, MySQL supports a hack to get what you want, without writing a much more complicated SQL statement. The expressions you want are:
substring_index(group_concat(ticket_logs.value_old order by `ticket_logs`.`action` desc), ',', 1)
substring_index(group_concat(ticket_logs.value_new order by `ticket_logs`.`action` desc), ',', 1)
Found another approach. I used the "created" column to obtain a max and joined:
SELECT
date_format(
from_unixtime(`ticket_logs`.`created`),
'%Y-%m-%d'
) AS `datax`,
ticket_logs.ticket_id,
ticket_logs.action,
ticket_logs.value_old,
ticket_logs.value_new
FROM
ticket_logs
INNER JOIN (
SELECT
date_format(
from_unixtime(`ticket_logs`.`created`),
'%Y-%m-%d'
) AS `datac`,
max(ticket_logs.created) AS maxts,
ticket_id
FROM
ticket_logs
WHERE
ticket_logs.action = 6
OR ticket_logs.action = 16
GROUP BY
date_format(
from_unixtime(`ticket_logs`.`created`),
'%Y-%m-%d'
),
ticket_id
) maxtbl ON ticket_logs.ticket_id = maxtbl.ticket_id
AND ticket_logs.created = maxtbl.maxts
ORDER BY
datax DESC,
ticket_id DESC

SQL query that finds a negative change between two rows with the same name field

I have a single table with rows like this: (Date, Score, Name)
The Date field has two possible dates, and it's possible that a Name value will appear under only one date (if that name was recently added or removed).
I'm looking to get a table with rows like this: (Delta, Name), where delta is the score change for each name between the earlier and later dates. In addition, only a negative change interests me, so if Delta>=0, it shouldn't appear in the output table at all.
My main challenge for me is calculating the Delta field.
As stated in the title, it should be an SQL query.
Thanks in advance for any help!
I assumed that each name can have it's own start/end dates. It can be simplified significantly if there are only two possible dates for the entire table.
I tried this out in SQL Fiddle here
SELECT (score_end - score_start) delta, name_start
FROM
( SELECT date date_start, score score_start, name name_start
FROM t t
WHERE NOT EXISTS
( SELECT 1
FROM t x
WHERE x.date < t.date
AND x.name = t.name
)
) AS start_date_t
JOIN
( SELECT date date_end, score score_end, name name_end
FROM t t
WHERE NOT EXISTS
( SELECT 1
FROM t x
WHERE x.date > t.date
AND x.name = t.name
)
) end_date_t ON start_date_t.name_start = end_date_t.name_end
WHERE score_end-score_start < 0
lets say you have a table with date_value, sum_value
Then it should be something like that:
select t.date_value,sum_value,
sum_value - COALESCE((
select top 1 sum_value
from tmp_num
where date_value > t.date_value
order by date_value
),0) as sum_change
from tmp_num as t
order by t.date_value
The following uses a "trick" in MySQL that I don't really like using, because it turns the score into a string and then back into a number. But, it is an easy way to get what you want:
select t.name, (lastscore - firstscore) as diff
from (select t.name,
substring_index(group_concat(score order by date asc), ',', 1) as firstscore,
substring_index(group_concat(score order by date desc), ',', 1) as lastscore
from table t
group by t.name
) t
where lastscore - firstscore < 0;
If MySQL supported window functions, such tricks wouldn't be necessary.

MySQL get subquery value

I'm trying to calculate the distance of my centroid point, that's calculated through the total number of tags, and sum of the instant time that tags appear. So that's the concept of (tc_sum/cnt).
However the SELECT on the subquery, doesn't allow me to get the centroid point, because the "centr" is not calculated yet, and so i can't get the "distance".
Any help?
SELECT cnt, tc_sum, ROUND(tc_sum/cnt) as centr, distance
FROM (
SELECT SUM(timecode) as tc_sum, count(timecode) as cnt, ABS( centr - '".$timecode."' ) AS distance
FROM dados d
WHERE tag = 'donald'
AND filename = 'donald.mp4'
AND group_id = '1'
) d
SELECT
SUM(timecode) as tc_sum,
SUM(timecode) as cnt,
ABS( SUM(timecode) / SUM(timecode) - '".$timecode."' ) AS distance,
ROUND(SUM(timecode) / SUM(timecode)) AS centr
FROM dados d
WHERE tag = 'donald'
AND filename = 'donald.mp4'
AND group_id = '1'
A query works on row after row and you can't refer to aliases this way. You have to "recalculate" them again. "Recalculate" is not the right word, since the result isn't really calculated multiple times. The optimizer will take care of it being only calculated once. But an alias is only known after the query ran. I'm afraid my english sucks too much to explain it in a good way :)
Try
SELECT cnt, tc_sum, ROUND(tc_sum/cnt) as centr, distance
FROM (
SELECT SUM(timecode) as tc_sum, count(timecode) as cnt, ABS( ROUND(tc_sum/cnt)- '".$timecode."' ) AS distance
FROM dados d
WHERE tag = 'donald'
AND filename = 'donald.mp4'
AND group_id = '1'
) d