Display SUM and LAST_VALUE groups by Year - mysql

Last forum I made question how to display values following max date, but I want to sum some values group by max date unfortunately a single value didn't want follow max date.
Here was my table:
And my query:
SELECT
SUM(pembelian) AS Buying,
SUM(penjualan) AS Selling,
SUM(penjualan)-SUM(pembelian) AS Benefit,
saldo,
MAX(tgl_lap)
FROM laporan GROUP BY DATE_FORMAT(tgl_lap,'%Y')
The results:
As we saw it, the results does work for some values but a single value (saldo) didn't following max date, guys can see the row of 2020 should be '23581800' and 2021 should be '35639800' according table.
So what I have missed?

I mean next query can solve the problem:
SELECT Buying, Selling, Benefit, saldo, last_tgl_lap
FROM laporan
JOIN (
SELECT
SUM(pembelian) AS Buying,
SUM(penjualan) AS Selling,
SUM(penjualan)-SUM(pembelian) AS Benefit,
MAX(tgl_lap) last_tgl_lap
FROM laporan
GROUP BY YEAR(tgl_lap)
) aggregated on aggregated.last_tgl_lap = tgl_lap;
Look here the example SQLize.online
If your MySQL version is 8.0 or greater you can use window function like:
SELECT
Pembelian AS Buying,
Penjualan AS Selling,
Penjualan - Pembelian AS Benefit,
Saldo,
LastDate
FROM (
SELECT
SUM(pembelian) OVER (PARTITION BY YEAR(tgl_lap) ORDER BY tgl_lap ASC) AS Pembelian,
SUM(penjualan) OVER (PARTITION BY YEAR(tgl_lap) ORDER BY tgl_lap ASC) AS Penjualan,
LAST_VALUE(saldo) OVER (PARTITION BY YEAR(tgl_lap) ORDER BY tgl_lap ASC) AS Saldo,
LAST_VALUE(tgl_lap) OVER (PARTITION BY YEAR(tgl_lap) ORDER BY tgl_lap ASC) AS LastDate,
ROW_NUMBER() OVER (PARTITION BY YEAR(tgl_lap) ORDER BY tgl_lap DESC) AS row_num
FROM laporan
) tbl
WHERE row_num = 1;
Fiddle on SQLize.online

Because of MySQL mode ONLY_FULL_GROUP_BY is disabled I think your query is not throwing error even though you have used non aggregated column saldo in the select clause.
Update after Clarification from OP
Another alternative to use window function first_value for saldo if you can,
select sum(pembelian) as Buying,
sum(penjualan) as Selling,
sum(penjualan)-sum(pembelian) as Benefit,
max(saldo) as Saldo,
max(tgl_lap) as tgl_lap
from
( select id_lap,pembelian,penjualan,tgl_lap,
first_value(saldo) over
(partition by date_format(tgl_lap,'%Y') order by tgl_lap desc) as saldo
from laporan
) l
group by date_format(tgl_lap,'%Y')

Your query is malformed. You have saldo in the SELECT, but it is not in the GROUP BY. You should be getting an error. An MySQL finally conforms to the SQL standard and to other databases in generating an error.
In MySQL 8.0, I would recommend conditional aggregation:
SELECT SUM(pembelian) AS Buying, SUM(penjualan) AS Selling,
SUM(penjualan)-SUM(pembelian) AS Benefit,
MAX(CASE WHEN seqnum = 1 THEN saldo END) as saldo,
MAX(tgl_lap)
FROM (SELECT l.*,
ROW_NUMBER() OVER (PARTITION BY YEAR(tgl_lap) ORDER BY tgl_lap DESC) as seqnum
FROM laporan l
) l
GROUP BY YEAR(tgl_lap);
Note that I replaced DATE_FORMAT() with YEAR(). It just seems clearer to me to use the appropriate date function when it is available.
In older versions, there is a hack to get the latest saldo value in each year:
SELECT SUM(pembelian) AS Buying, SUM(penjualan) AS Selling,
SUM(penjualan)-SUM(pembelian) AS Benefit,
SUBSTRING_INDEX(GROUP_CONCAT(saldo ORDER BY tgl_lap DESC), ',', 1) + 0 as saldo,
MAX(tgl_lap)
FROM laporan l
GROUP BY YEAR(tgl_lap);
This concatenates the saldo values into a string and then takes the first element. The only caveat is that the default internal length is about 1,000 characters for GROUP_CONCAT().

Related

I need to get last created eligible rider ids and pinged rider ids accordeing to a orderId using a sql query

I need to get my data set as this table
I am trying to get eligible set like this, need to group_concat pinged set also
x.id IN (SELECT MAX(x.id) FROM x WHERE ping rider id IS NULL GROUP BY orderId)
You can assign a group based on the cumulative number of non-null values in eligible_riders. Then aggregate and take the last value:
select og.*
from (select order_id, grp, max(eligible_riders) as eligible_riders,
group_concat(rider_id) as riders,
row_number() over (partition by order_id order by min(id) desc) as seqnum
from (select t.*,
sum(eligible_riders <> '') over (partition by order_id order by id) as grp
from t
) t
group by order_id, grp
) og
where seqnum = 1;
Hmmm . . . You could also do this with a correlated subquery, which might look a bit simpler:
select order_id, max(eligible_riders) as eligible_riders,
group_concat(rider_id) as riders
from t
where t.id >= (select max(t2.id)
from t t2
where t2.order_id = t.order_id and
t2.eligible_riders <> ''
)
group by order_id;
For performance, you want an index on (order_id, eligible_riders).

Get the last record from each month, from each MATERIAL id after a rolling sum

I have a table which I need to perform a rolling sum for each material. I've already done it using this:
SELECT *, SUM(`ESTOQUE_FINAL`) OVER (PARTITION BY MATERIAL ORDER BY CALDAY) as ESTOQUE
FROM bq_trusted.IINV_01
ORDER BY MATERIAL, CALDAY
The result is
in this screenshot
What i need now is to get the results on the red circled values. For each MATERIAL, I need the result of the rolling sum by the end of each month.
I can get those results using the following query, but I have to save the last query on a new table to use it.
WITH ESTOQUE_ATUAL AS (
SELECT IQ.*, ROW_NUMBER() OVER (PARTITION BY MATERIAL, MONTH_YEAR ORDER BY CALDAY DESC) AS RN
FROM bq_trusted.INVENTORY AS IQ
)
SELECT * FROM ESTOQUE_ATUAL WHERE RN = 1
ORDER BY MONTH_YEAR
How can I achieve this result using only one query?
Thanks in advance!
Is this what you want?
SELECT i.*
FROM (SELECT i.*,
SUM(ESTOQUE_FINAL) OVER (PARTITION BY MATERIAL ORDER BY CALDAY) as ESTOQUE,
ROW_NUMBER() OVER (PARTITION BY MATERIAL, MONTH_YEAR ORDER BY CALDAY DESC) AS seqnum
FROM bq_trusted.IINV_01 i
) i
WHERE seqnum = 1
ORDER BY MATERIAL, CALDAY
You can calculate both window functions at the same time.

Select latest record with date and time column with group by clause in mysql

I have this table from which I have to select the latest row on the basis of date and time column for each checkpost
I have tried the following queries but not returning the latest data for each checkpost.
SELECT checkpost_id,current_rate,date,time FROM revisionrates
WHERE date IN (SELECT max(date) FROM revisionrates GROUP BY checkpost_id)
The expected output is
You can use window functions:
select rr.*
from (select rr.*,
row_number() over (partition by checkpost_id order by date desc, time desc) as seqnum
from revisionrates rr
) rr
where seqnum = 1;
This requires MySQL 8.0. In earlier versions of MySQL, this is a bit trickier, but one method uses tuples
select rr.*
from rr
where (date, time) in (select rr2.date, rr2.time
from revisionrates rr2
where rr2.checkpoint_id = rr.checkpoint_id
order by rr2.date desc, rr2.time desc
limit 1
);

How can I RANK from AVG sql?

I'm having trouble retrieving the rankings from a single line that has some uuid from that:
SELECT uuid , AVG(nodebuff+debuff+archer+builduhc+uhc+gapple)/6 as Average
from elo_ranked group by uuid
order by AVG(nodebuff+debuff+archer+builduhc+uhc+gapple)/6 desc
limit 3
I specify that the above function works and has the expected result.
Is this what you want?
SELECT uuid, AVG(nodebuff+debuff+archer+builduhc+uhc+gapple)/6 as Average ,
RANK() OVER (ORDER BY AVG(nodebuff+debuff+archer+builduhc+uhc+gapple)/6 DESC) as ranking
FROM elo_ranked
GROUP BY uuid
ORDER BY AVG(nodebuff+debuff+archer+builduhc+uhc+gapple)/6 desc
LIMIT 3;
EDIT:
To rank a specific user, use a subquery:
SELECT u.*
FROM (SELECT uuid, AVG(nodebuff+debuff+archer+builduhc+uhc+gapple)/6 as Average ,
RANK() OVER (ORDER BY AVG(nodebuff+debuff+archer+builduhc+uhc+gapple)/6 DESC) as ranking
FROM elo_ranked
GROUP BY uuid
) u
WHERE uuid = ?;
Also, I'm not sure if you need the aggregation. That would only be needed if a user had multiple rows in the elo_ranked table. If not needed, then you should use:
SELECT u.*
FROM (SELECT uuid, (nodebuff+debuff+archer+builduhc+uhc+gapple)/6 as Average ,
RANK() OVER (ORDER BY (nodebuff+debuff+archer+builduhc+uhc+gapple)/6 DESC) as ranking
FROM elo_ranked
) u
WHERE uuid = ?;
The GROUP BY has a lot of overhead, so this should be faster (unless MySQL has sophisticated optimizations to avoid the aggregation when grouping by a primary key).

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