BigBossta
BigBossta

Reputation: 71

Display SUM and LAST_VALUE groups by Year

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:

the 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:

enter image description here

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?

Upvotes: 1

Views: 287

Answers (3)

Gordon Linoff
Gordon Linoff

Reputation: 1270011

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().

Upvotes: 1

Slava Rozhnev
Slava Rozhnev

Reputation: 10163

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

Upvotes: 1

Sujitmohanty30
Sujitmohanty30

Reputation: 3316

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')

Upvotes: 1

Related Questions