我有这个查询,它基本上得到了客户在过去一年和3个月的平均支出:
SELECT SQL_CALC_FOUND_ROWS
customer_id,
customer_name,
AVG(IF(
DATE(CONCAT(year_of_spend, "-", month_of_spend, "-01")) >=
DATE_FORMAT(NOW() - INTERVAL 1 YEAR, "%Y-%m-01"),
spend_amount,
NULL
)) AS 1_year_average_spend,
AVG(IF(
DATE(CONCAT(year_of_spend, "-", month_of_spend, "-01")) >=
DATE_FORMAT(NOW() - INTERVAL 3 MONTH, "%Y-%m-01"),
spend_amount,
NULL
)) AS 3_month_average_spend
FROM customer_spends
GROUP BY customer__id
但我还需要获得支出平均值的百分比差异:
E.g。 (伪代码)
if (1_year_average_spend = 0)
change = N/A
else
change = 3_month_average_spend / 1_year_average_spend - 1
如何,或者你建议我做什么来实现这个?
我能想到的唯一方法是可怕的:
IF(
AVG(IF(
DATE(CONCAT(year_of_spend, "-", month_of_spend, "-01")) >=
DATE_FORMAT(NOW() - INTERVAL 1 YEAR, "%Y-%m-01"),
`spend_amount`,
NULL
)) > 0,
AVG(IF(
DATE(CONCAT(year_of_spend, "-", month_of_spend, "-01")) >=
DATE_FORMAT(NOW() - INTERVAL 3 MONTH, "%Y-%m-01"),
spend_amount,
NULL
)) / AVG(IF(
DATE(CONCAT(year_of_spend, "-", month_of_spend, "-01")) >=
DATE_FORMAT(NOW() - INTERVAL 1 YEAR, "%Y-%m-01"),
`spend_amount`,
NULL
)) - 1,
"N/A"
) AS 3_month_performance
答案 0 :(得分:1)
使用内部选择(它就像一个临时视图)并从中进行选择。这应该有效:
SELECT
customer_id,
customer_name,
1_year_average_spend,
3_month_average_spend,
if (1_year_average_spend = 0, "N/A", (3_month_average_spend / 1_year_average_spend) - 1) AS 3_month_performance
FROM (SELECT
customer_id,
customer_name,
AVG(IF(DATE(CONCAT(year_of_spend, "-", month_of_spend, "-01")) >=
DATE_FORMAT(NOW() - INTERVAL 1 YEAR, "%Y-%m-01"), spend_amount, NULL)) AS 1_year_average_spend,
AVG(IF(DATE(CONCAT(year_of_spend, "-", month_of_spend, "-01")) >=
DATE_FORMAT(NOW() - INTERVAL 3 MONTH, "%Y-%m-01"), spend_amount, NULL)) AS 3_month_average_spend)
FROM customer_spends
GROUP BY customer_id, customer_name ) x
答案 1 :(得分:1)
NULL
会N/A
值吗?如果是这样,您可以将NULLIF()
应用于这样的分母:
a / NULLIF(b, 0) - 1
如果第一个参数等于第二个参数,则 NULLIF
返回NULL
。如果操作数为NULL
,则整个表达式的计算结果为NULL
。
与@Bohemian一样,我也建议使用subselect。这是完整的查询:
SELECT SQL_CALC_FOUND_ROWS
customer_id,
customer_name,
1_year_average_spend,
3_month_average_spend,
3_month_average_spend / NULLIF(1_year_average_spend, 0) - 1 AS change
FROM (
SELECT
customer_id,
customer_name,
AVG(IF(
DATE(CONCAT(year_of_spend, "-", month_of_spend, "-01")) >=
DATE_FORMAT(NOW() - INTERVAL 1 YEAR, "%Y-%m-01"),
spend_amount,
NULL
)) AS 1_year_average_spend,
AVG(IF(
DATE(CONCAT(year_of_spend, "-", month_of_spend, "-01")) >=
DATE_FORMAT(NOW() - INTERVAL 3 MONTH, "%Y-%m-01"),
spend_amount,
NULL
)) AS 3_month_average_spend
FROM customer_spends
GROUP BY customer__id
) s
答案 2 :(得分:1)
如果您乐意使用MySQL特定代码,可以使用User-Defined Variables这样的(简化版):
SELECT @avg1 := ROUND((1 + 2 + 3) / 3, 2) AS avg1,
@avg2 := ROUND((4 + 5 + 6) / 3, 2) AS avg2,
IF( @avg1, ROUND(@avg2 / @avg1 - 1, 2), NULL ) AS result;
+------+------+--------+
| avg1 | avg2 | result |
+------+------+--------+
| 2.00 | 5.00 | 1.50 |
+------+------+--------+
那将成为:
SELECT SQL_CALC_FOUND_ROWS
customer_id,
customer_name,
@1_year_average_spend := AVG(IF(
DATE(CONCAT(year_of_spend, "-", month_of_spend, "-01")) >=
DATE_FORMAT(NOW() - INTERVAL 1 YEAR, "%Y-%m-01"),
spend_amount,
NULL
)) AS 1_year_average_spend,
@3_month_average_spend := AVG(IF(
DATE(CONCAT(year_of_spend, "-", month_of_spend, "-01")) >=
DATE_FORMAT(NOW() - INTERVAL 3 MONTH, "%Y-%m-01"),
spend_amount,
NULL
)) AS 3_month_average_spend,
IF( @1_year_average_spend,
@3_month_average_spend / @1_year_average_spend - 1,
NULL
) AS diff
FROM customer_spends
GROUP BY customer__id
注1:我已使用diff
作为差异的列名,因为change
是保留字,因此最终可能会导致问题。
注2:您需要了解文档中的以下警告,因为它们可能会影响您的结果:
十进制值和实数值的分配不会保留精度 或者价值的规模。
和
作为一般规则,您不应该为用户变量赋值 并在同一语句中读取值。你可能会得到 你期望的结果,但这不能保证。的顺序 涉及用户变量的表达式的评估是未定义的 可能会根据给定语句中包含的元素进行更改。 在SELECT @ a,@ a:= @ a + 1,...中,您可能会认为MySQL会进行评估 首先是@a,然后是第二个任务。但是,改变了 声明(例如,通过添加GROUP BY,HAVING或ORDER BY 子句)可能导致MySQL选择具有不同的执行计划 评估顺序。
因此请谨慎使用,并进行适当的测试!
答案 3 :(得分:0)
摆脱IF()
,DATE()
和CONCAT()
函数调用。您的查询现在必须扫描整个customer_spends
表并检查所有行的复杂条件,即使它们是10年的数据。
这也会使用(year_of_spend, month_of_spend)
或(customer_id, year_of_spend, month_of_spend)
的索引来加速查询:
SELECT c.customer_id
, c.customer_name
, 1_year_average_spend
, 3_month_average_spend
, CASE WHEN 1_year_average_spend = 0
THEN 'N/A'
ELSE (3_month_average_spend / 1_year_average_spend) - 1
END AS percent_difference
FROM
customer AS c
JOIN
( SELECT customer_id
, AVG(spend_amount) AS 1_year_average_spend
FROM customer_spends
WHERE (year_of_spend, month_of_spend) >=
( YEAR(CUR_DATE() - INTERVAL 1 YEAR)
, MONTH(CUR_DATE() - INTERVAL 1 YEAR)
)
GROUP BY customer_id
) AS grp1year
ON grp1year.customer_id = c.customer_id
LEFT JOIN
( SELECT customer_id
, AVG(spend_amount) AS 3_month_average_spend
FROM customer_spends
WHERE (year_of_spend, month_of_spend) >=
( YEAR(CUR_DATE() - INTERVAL 3 MONTH)
, MONTH(CUR_DATE() - INTERVAL 3 MONTH)
)
GROUP BY customer_id
) AS grp3month
ON grp3month.customer_id = c.customer_id