问题描述
我一直在尝试计算月度回报的逐年增长,并且连续数小时都在重写同样的查询,但是没有运气。我见过解决方案,但它们都是所有其他数据库解决方案。
我正在尝试基本实现以下目标:
这是我构建的查询,尽管由于子查询按行运行,所以我从未真正完成过该查询(运行15分钟以上)。
该表具有2m +行且具有良好的索引,该表速度很快,但子查询会杀死它。
SELECT
YEAR(thisyear.trandte) AS `Year`,MONTH(thisyear.trandte) AS `YearMonth`,SUM(lastyear.totamount) AS LastYearSales,SUM(thisyear.totamount) AS ThisYearSales
FROM
sync_invoice_lines thisyear
LEFT JOIN
sync_invoice_lines lastyear ON
MONTH(thisyear.trandte) = (MONTH(lastyear.trandte)) AND
YEAR(thisyear.trandte) = (YEAR(lastyear.trandte) - 1)
WHERE
thisyear.type = 'IN' AND
lastyear.type = 'IN' AND
thisyear.sync_active = 1 AND
lastyear.sync_active = 1 AND
GROUP BY `Year`,`YearMonth`
解决方法
假设您在表中所有月份的数据都没有任何间隙,那么您所需要的就是窗口函数LAG()
,以获取同一个月的去年totamount
的总和:
SELECT YEAR(trandte) AS Year,MONTH(trandte) AS Month,SUM(totamount) AS ThisYearSales,LAG(SUM(totamount),12) OVER (ORDER BY YEAR(trandte),MONTH(trandte)) AS LastYearSales
FROM sync_invoice_lines
WHERE type = 'IN' AND sync_active = 1
GROUP BY Year,Month
如果两个月之间有间隔,则从上面的查询中创建一个CTE
并对其进行LEFT
自联接:
WITH cte AS (
SELECT YEAR(trandte) AS Year,SUM(totamount) AS Sales
FROM sync_invoice_lines
WHERE type = 'IN' AND sync_active = 1
GROUP BY Year,Month
)
SELECT c1.Year,c1.Month,c1.Sales AS ThisYearSales,c2.Sales AS LastYearSales
FROM cte c1 LEFT JOIN cte c2
ON c2.Year = c1.Year - 1 AND c2.Month = c1.Month
,
您可以在单个表扫描中执行此操作(不进行联接或CTE),并考虑可能缺少的月份。为此,可将lag()
与range
子句一起使用,该子句精确定位去年的同一个月,就像这样:
select
year(trandte) as `year`,month(trandte) as `yearmonth`,lag(sum(totamount)) over(
order by concat(year(trandte),'-',month(trandte),'-01')
range between interval 1 year preceding and interval 1 year preceding
) as lastyearsales,sum(totamount) as thisyearsales
from sync_invoice_lines
where type = 'IN' and sync_active = 1
group by year(trandte),month(trandte)
order by year(trandte),month(trandte)
,
您可以使用CASE表达式分别计算去年和今年的销售总额。 这很简单。
查询如下:
SELECT
YEAR(CURRENT_DATE) AS `Year`,MONTH(trandte) AS `YearMonth`,SUM(CASE YEAR(trandte) WHEN YEAR(CURRENT_DATE)-1 THEN totamount END) AS LastYearSales,SUM(CASE YEAR(trandte) WHEN YEAR(CURRENT_DATE) THEN totamount END) AS ThisYearSales
FROM
sync_invoice_lines
WHERE type = 'IN' AND sync_active = 1
GROUP BY `YearMonth`
ORDER BY `YearMonth`;
您可以在YEAR(CURRENT_DATE)部分中指定任何年份。
,您可以使用数据透视表按年份显示您的销售额。
with monthly_sales as
(SELECT
YEAR(trandte) AS year,MONTH(trandte) AS month,SUM(totamount) AS sales
FROM
sync_invoice_lines
WHERE
type = 'IN' AND
sync_active = 1
GROUP BY YEAR(trandte),MONTH(trandte))
Select * from
(select month,year from monthly_sales)
pivot
(sum(sales)
for month in (2013,2014,2015)
)
order by month
,
第1步:计算所有每月小计,还没有按年计算 :
SELECT LEFT(trandte,7) AS yyyy_mm,SUM(totamount) AS sales
FROM sync_invoice_lines
WHERE ...
GROUP BY 1;
首先,查看它是否获得正确的数字,尽管顺序不正确。并查看其运行速度。
这可能就是您所需要的。
第2步:这将处理约30行,因此效率不是问题。可以将上述内容放入另一个表中,或者,由于您具有MySQL 8.0(或MariaDB 10.2),因此可以在WITH
中使用它来完成其余的工作。第2步可能是使用自联接来计算年复一年。
第3步:输出顺序-还是图形包重新排列数据以获取12套多年的数据?
从长远来看,考虑建立和维护一个“汇总表”,也许是每日小计。就像第1步,但要成千上万行,而不是数百万或数十行。这样,您就可以很快计算出每月金额。或每周一次。或其他范围。这样,庞大的任务(第1步)便建立在日常数据块中,其速度将快一千倍。