рассчитать разницу двух рассчитанных полей - PullRequest
4 голосов
/ 13 июля 2011

У меня есть этот запрос, который в основном получает средние расходы клиента за последний год, и за 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

Но мне также нужно получить процентную разницу средних расходов:

например. (псевдокод)

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

Ответы [ 4 ]

1 голос
/ 13 июля 2011

Будет ли NULL соответствовать значению N/A?Если это так, вы можете применить NULLIF() к знаменателю следующим образом:

a / NULLIF(b, 0) - 1

NULLIF возвращает NULL, если его первый аргумент равен второму аргументу.И если операндом является NULL, все выражение оценивается как NULL.

Как и @ Bohemian, я тоже предлагаю использовать подвыбор.Вот полный запрос:

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
1 голос
/ 13 июля 2011

Если вы счастливы использовать специальный код MySQL, вы можете использовать Определяемые пользователем переменные вот так (упрощенная версия):

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 будет оценивать @ сначала, а затем сделайте задание вторым. Тем не менее, изменение оператор (например, путем добавления GROUP BY, HAVING или ORDER BY пункт) может заставить MySQL выбрать план выполнения с другим порядок оценки.

Так что используйте с осторожностью и надлежащим тестированием!

1 голос
/ 13 июля 2011

Используйте внутренний выбор (это как временное представление) и выберите из этого.Это должно работать:

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
0 голосов
/ 13 июля 2011

Избавление от вызовов функций 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    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...