Select smallest date after group by - mysql

Let's start off by saying that I have almost completed my MySQL query, but I just need a final hint towards the answer.
I used the following MySQL query (for reference):
SELECT * FROM
(
SELECT ll.id AS id, ll.globalId AS globalId, ll.date AS date, ll.serverId AS serverId, ll.gamemodeId AS gamemodeId, ll.mapId AS mapId, origin,
pjl.id AS pjlid, pjl.globalid AS pjlglobalId, pjl.date AS pjldate, MIN(pjl.date) AS mindate, pjl.serverId AS pjlserverId, pjl.playerId AS pjlplayerId
FROM (
(
SELECT id, globalId, date, serverId, playerId, 'playerjoins' AS origin
FROM playerjoins pj
WHERE playerId =976
)
UNION ALL
(
SELECT id, globalId, date, serverId, playerId, 'playerleaves' AS origin
FROM playerleaves pl
WHERE playerId =976
)
ORDER BY date DESC
)pjl
JOIN levelsloaded ll ON pjl.date >= ll.date
GROUP BY ll.id, origin
ORDER BY date DESC) above
This give me the resultset (part of it), that can be found on the following SQL Fiddle: http://sqlfiddle.com/#!2/514b6/1/0
What I want is the following:
You now see that there are duplicate id's in the resultset, take for the example result with id = 133.
I want to see the first action that happened after the date in that record (id = 113).
The date of that record is November, 27 2013 00:00:17+0000.
Now there are two possible actions that happened directly after that date:
1) origin = 'playerjoins' on mindate = November, 28 2013 00:00:18+0000.
2) origin = 'playerleaves' on mindate = November, 28 2013 00:00:19+0000.
Since playerjoins is the one that has first happened, I want that in my final resultset.
So, I hope it is clear with my example: I want to have, for every 2 rows with the same id, the row that has the lowest mindate. I need to be able to see the whole row, so only knowing the lowest mindate per 2 rows does not suffice. I need to know the origin aswell.
EDIT: The answer might be found here, https://stackoverflow.com/a/7745635/2057294 , still investigating it.

The correct query is:
SELECT *
FROM levelsloaded ll
INNER JOIN
(SELECT id, MIN(mindate) AS finalmindate
FROM levelsloaded
GROUP BY id
) ill
ON ll.id = ill.id AND ll.mindate = ill.finalmindate
ORDER BY date DESC
This does exactly what I described, a more detailed answer can be found in: https://stackoverflow.com/a/7745635/2057294.

Related

Mysql query for getting today's data in a sub-query #mysql

I have to get today's data in MySQL query.
SELECT
SUM(score) AS tscore,
(SELECT
players.id_company
FROM
players
WHERE
id = 377) AS id_company,
(SELECT
SUM(players_score.score),
DATE_FORMAT(players_score.reg_dt, '%Y-%m-%d')
FROM
players_score
WHERE
id_player = 377
AND DATE(reg_dt) = CURDATE())
FROM
players_score
WHERE
id_player = 377
In this query, it shows the error message that we can select one column which is understandable as I have used (col1, col2(for date)) as col. but I need a solution to get this done. thanks
If you need today's data only, You should use the condition at the last only -
SELECT
SUM(score) AS tscore,
(SELECT
players.id_company
FROM
players
WHERE
id = PS.id_player) AS id_company, -- I have changed the 377 to players_score to make the query more dynamic.
SUM(players_score.score),
DATE_FORMAT(players_score.reg_dt, '%Y-%m-%d')
FROM
players_score PS
WHERE
id_player = 377
AND DATE(reg_dt) = CURDATE()
I don't know your schema or data, so there could very well be more efficient ways of gathering this information, like the date seems like it should be set as the current date if that's what you're pulling, instead of referencing the table rows for the date, but the following should work for you in regards to pulling the sums from different tables in the same query.
The problem with your query was that there are multiple things being asked and tallied in a single query, which won't work.
SELECT
(select SUM(score) AS tscore FROM players_score WHERE id_player = 377 AND DATE(reg_dt) = CURDATE()),
(SELECT players.id_company FROM players WHERE id = 377) AS id_company,
(SELECT SUM(players_score.score WHERE id_player = 377 AND DATE(reg_dt) = CURDATE()),
(select DATE_FORMAT(players_score.reg_dt, '%Y-%m-%d') FROM players_score WHERE id_player = 377 AND DATE(reg_dt) = CURDATE())

Grouping rows via two different columns in MYSQL

I just want to ask if grouping rows with the same value but came from different columns is possible.
I have a scenario that we should sum up the total minutes if the records are found "continuous" transactions by checking if the STARTDATETIME column matches the previous data of ENDDATETIME column if they are the same. See image link below for reference.
Thanks guys.
I modified Gordon Linoff's solution ( see my comment under the question):
SELECT
c.employee_id
,MIN(c.start_date) AS start_date
,MAX(c.end_date) AS end_date
,COUNT(*) AS numcontracts,
TIMESTAMPDIFF(minute,MIN(c.start_date),MAX(c.end_date)) AS timediff
FROM
(
SELECT
c0.*
,(#rn := #rn + COALESCE(startflag, 0)) AS cumestarts
FROM
(SELECT c1.*,
(NOT EXISTS (SELECT 1
FROM contracts c2
WHERE c1.employee_id = c2.employee_id AND
c1.start_date = c2.end_date
)
) AS startflag
FROM contracts c1
ORDER BY employee_id, start_date
) c0 CROSS JOIN (SELECT #rn := 0) params
) c
GROUP BY c.employee_id, c.cumestarts
http://rextester.com/VOGMU19779
timediff contains the minutes passed in the combined interval.

mysql index guidance needed - group by sub query super slow

Quick overview, I have worked out a mysql query but need to optimize the performance.
My original post was here but its gone cold and im getting desperate to elaborate on some of the suggestions which I tried to implement. So its not a dupe post but it is related.
Here is the query that takes 45 seconds plus, the group by on the second sub query really slows things down.
SELECT * FROM
(
SELECT DISTINCT email,
title,
first_name,
last_name,
'chauntry' AS source,
post_code AS postcode
FROM chauntry
WHERE mailing_indicator = 1
) AS x
JOIN
(
SELECT email,
Avg(amount_paid) AS avg_paid,
Count(*) AS no_times_booked,
Count(DISTINCT( Date_format(added, '%M %Y') )) AS unique_months
FROM chauntry
WHERE added >= Now() - INTERVAL 1 year
GROUP BY email
) AS y
ON x.email = y.email
Based on the index suggestions from here I looked around for a few examples of indexing and came up with the below
ALTER TABLE `chauntry`
ADD INDEX(`mailing_indicator`, `email`);
ALTER TABLE `chauntry`
ADD INDEX covering_index (`added`, `email`, `amount_paid`);
This makes no difference to the query time and im not sure if what im doing is even close as up until now I have had no need to use indexing.
suggestions welcome on how to index my table correctly or how to modify the query.
Out of curiousity, does this query do what you want?
SELECT email, title, first_name, last_name, 'chauntry' AS source,
post_code AS postcode,
Avg(amount_paid) AS avg_paid,
Count(*) AS no_times_booked,
Count(DISTINCT( Date_format(added, '%M %Y') )) AS unique_months
FROM chauntry
WHERE added >= Now() - INTERVAL 1 year
GROUP BY email, title, first_name, last_name, post_code
HAVING SUM(mailing_indicator = 1) > 0;
It would seem to follow the same logic as your query, except that the mailing indicator would need to have been set in the past year.
Why use JOIN on subselects to same table?
I would try this:
SELECT email,
title,
first_name,
last_name,
'chauntry' AS source,
post_code AS postcode
Avg(amount_paid) AS avg_paid,
Count(*) AS no_times_booked,
Count(DISTINCT( Date_format(added, '%M %Y') )) AS unique_months
FROM chauntry
WHERE
mailing_indicator = 1 and
added >= Now() - INTERVAL 1 year
GROUP BY email
Also I don't think you need any index with query like this, maybe on added and email, but you already added them.
Minor play.
The average of the amount_paid is the biggest problem. If you are prepared to put up with the possibility of an inaccuracy for this figure then you could maybe average the distinct values of the amount_paid field. This WILL give the wrong value under certain circumstances (ie, if you had 100 bookings, 99 at $1 and 1 at $100 the average would be given as $50.50 rather than $1.99), but if the amount paid is never repeated then this may be acceptable.
Otherwise you can probably use a join of the table against itself. To get the no_times_booked you can count the DISTINCT unique identifiers of the table (I have assumed id here).
SELECT c1.email,
c1.title,
c1.first_name,
c1.last_name,
'chauntry' AS source,
c1.post_code AS postcode
Avg(DISTINCT c2.amount_paid) AS avg_paid,
Count(DISTINCT c2.id) AS no_times_booked,
Count(DISTINCT( Date_format(c2.added, '%M %Y') )) AS unique_months
FROM chauntry c1
INNER JOIN chauntry c2
ON c1.email = c2.email
WHERE c1.mailing_indicator = 1
AND c2.added >= Now() - INTERVAL 1 year
GROUP BY c1.email,
c1.title,
c1.first_name,
c1.last_name,
source,
c1.post_code

sql calculate change and percent by year

I have an data set that simulates the rate of return for a trading account. There is an entry for each day showing the balance and the open equity. I want to calculate the yearly, or quarterly, or monthly change and percent gain or loss. I have this working for daily data, but for some reason I can't seem to get it to work for yearly data.
The code for daily data follows:
SELECT b.`Date`, b.Open_Equity, delta,
concat(round(delta_p*100,4),'%') as delta_p
FROM (SELECT *,
(Open_Equity - #pequity) as delta,
(Open_Equity - #pequity)/#pequity as delta_p,
(#pequity:= Open_Equity)
FROM tim_account_history p
CROSS JOIN
(SELECT #pequity:= NULL
FROM tim_account_history
ORDER by `Date` LIMIT 1) as a
ORDER BY `Date`) as b
ORDER by `Date` ASC
Grouping by YEAR(Date) doesn't seem to make the desired difference. I have tried everything I can think of, but it still seems to return daily rate of change even if you group by month or year, etc. I think I'm not using windowing correctly, but I can't seem to figure it out. If anyone knows of a good book about this sort of query I'd appreciate that also.
Thanks.sqlfiddle example
Using what Lolo contributed, I have added some code so the data comes from the last day of the year, instead of the first. I also just need the Open_Equity, not the sum.
I'm still not certain I understand why this works, but it does give me what I was looking for. Using another select statement as a from seems to be the key here; I don't think I would have come up with this without Lolo's help. Thank you.
SELECT b.`yyyy`, b.Open_Equity,
concat('$',round(delta, 2)) as delta,
concat(round(delta_p*100,4),'%') as delta_p
FROM (SELECT *,
(Open_Equity - #pequity) as delta,
(Open_Equity - #pequity)/#pequity as delta_p,
(#pequity:= Open_Equity)
FROM (SELECT (EXTRACT(YEAR FROM `Date`)) as `yyyy`,
(SUBSTRING_INDEX(GROUP_CONCAT(CAST(`Open_Equity` AS CHAR) ORDER BY `Date` DESC), ',', 1 )) AS `Open_Equity`
FROM tim_account_history GROUP BY `yyyy` ORDER BY `yyyy` DESC) p
CROSS JOIN
(SELECT #pequity:= NULL) as a
ORDER BY `yyyy` ) as b
ORDER by `yyyy` ASC
Try this:
SELECT b.`Date`, b.Open_Equity, delta,
concat(round(delta_p*100,4),'%') as delta_p
FROM (SELECT *,
(Open_Equity - #pequity) as delta,
(Open_Equity - #pequity)/#pequity as delta_p,
(#pequity:= Open_Equity)
FROM (SELECT YEAR(`Date`) `Date`, SUM(Open_Equity) Open_Equity FROM tim_account_history GROUP BY YEAR(`Date`)) p
CROSS JOIN
(SELECT #pequity:= NULL) as a
ORDER BY `Date` ) as b
ORDER by `Date` ASC

MySql Query for generating report

I have one table which is having three fields:
Id, Creation Time, Fuel Level
Every two minutes we are getting data and inserting to database.For generating a fuel credit/debit statement i want to get starting(Stating of the day) and ending(End of the Day) Fuel Level.Can anyone help to form a query to generate this report?
Search parameters will be date range.
Id=10;creation time =2019-02-15 16:32:59;Fuel Level =20
I created one sample schema here
http://sqlfiddle.com/#!2/76dd5
First, for a query that provides the change in fuel for each vehicle for each day, you can use the following SQL:
SELECT trip_range.dt
, trip_range.vehicle_id
, st.fuel_content as start_fuel_content
, en.fuel_content as end_fuel_content
, en.fuel_content - st.fuel_content as fuel_change
FROM (
SELECT tp.vehicle_id, DATE(tp.creation_time) dt
, MIN(tp.creation_time) start_time
, MAX(tp.creation_time) end_time
FROM trip_parameters tp
GROUP BY tp.vehicle_id, DATE(tp.creation_time)
) as trip_range
JOIN trip_parameters st
ON st.vehicle_id = trip_range.vehicle_id
AND st.creation_time = trip_range.start_time
JOIN trip_parameters en
ON en.vehicle_id = trip_range.vehicle_id
AND en.creation_time = trip_range.end_time
WHERE trip_range.dt BETWEEN '2012-11-08' AND '2012-11-09'
If you want the Cumulative change in fuel across all vehicles for each day in the range, the following SQL should work:
SELECT dt, SUM(fuel_change) as fuel_change
FROM (
SELECT trip_range.dt
, en.fuel_content - st.fuel_content as fuel_change
FROM (
SELECT tp.vehicle_id, DATE(tp.creation_time) dt
, MIN(tp.creation_time) start_time
, MAX(tp.creation_time) end_time
FROM trip_parameters tp
GROUP BY tp.vehicle_id, DATE(tp.creation_time)
) as trip_range
JOIN trip_parameters st
ON st.vehicle_id = trip_range.vehicle_id
AND st.creation_time = trip_range.start_time
JOIN trip_parameters en
ON en.vehicle_id = trip_range.vehicle_id
AND en.creation_time = trip_range.end_time
WHERE trip_range.dt BETWEEN '2012-11-08' AND '2012-11-09'
) change_by_vehicle
GROUP BY 1
Hope this helps!
john...
Try below:
SELECT Id, CreationTime, FuelLevel
FROM MYTABLE
WHERE DATE(CreationTime) = CURDATE();
This gets the date part of CreationTime and compares against current date, thus returns all records created today.
here's a query that returns the output as:
DATE FUEL_CONTENT_AT_START_OF_DAY FUEL_CONTENT_AT_END_OF_DAY
Nov, 08 2012 4.6 3.6
Nov, 09 2012 11.6 5.6
Query
SELECT
DATE( startofday.creation_time) AS date,
startofday.fuel_content AS fuel_content_at_start_of_day,
(SELECT fuel_content FROM trip_parameters WHERE trip_paramid = max(endofday.trip_paramid) ) AS fuel_content_at_end_of_day
FROM
trip_parameters startofday
INNER JOIN trip_parameters endofday ON ( DATE( endofday.creation_time ) = DATE( startofday.creation_time) )
WHERE
DATE( startofday.creation_time) BETWEEN '2012-11-08' AND '2012-11-09'
GROUP BY
startofday.TRIP_PARAMID
HAVING
min( endofday.trip_paramid ) = startofday.trip_paramid
I have update the same on SQLFiddle, http://sqlfiddle.com/#!2/76dd5/67
The query works as
Get a row
Get all corresponding rows on the same day
Get the minimum and maximum ID on the same day
If the current row Id equals the minimum Id from the JOIN display it, else ignore
Use the maximum id in a sub-query to get the end of day fuel_content
Hope this helps