Mysql рассчитать рост на основе кварталов
У меня есть база данных с двумя таблицами - компании и отчеты. Я хочу рассчитать изменение с q1 (квартал 1) до q2 (квартал 2). Я пытался использовать (следующий) подзапрос, но тогда основной запрос не удается...
FROM
(SELECT revenue FROM reports WHERE quarter = 'q2' AND fiscal_year = 2018) AS q,
(SELECT revenue FROM reports WHERE quarter = 'q1' AND fiscal_year = 2017) AS lq
Вот БД Fiddle, чтобы помочь вам понять проблему и схему:
https://www.db-fiddle.com/f/eE8SNRojn45h7Rc1rPCEVN/4
Текущий простой запрос.
SELECT
c.name, r.quarter, r.fiscal_year, r.revenue, r.taxes, r.employees
FROM
companies c
JOIN
reports r
ON
r.company_id = c.id
WHERE
c.is_marked = 1;
Ожидаемые результаты (это то, что мне нужно):
+---------+----------+----------------+----------+--------------+-----------+------------------+
| Name | Revenue | Revenue_change | Taxes | Taxes_change | Employees | Employees_change |
+---------+----------+----------------+----------+--------------+-----------+------------------+
| ABC INC | 11056 | +54.77 | 35000.86 | -28.57% | 568 | -32 |
| XYZ INC | 5000 | null | null | null | 10 | +5 |
+---------+----------+----------------+----------+--------------+-----------+------------------+
Буду очень признателен за помощь в создании этого запроса. Заранее спасибо.
Ответы
Ответ 1
Использование оконных функций MySQL 8.0:
WITH cte AS (
SELECT c.name, quarter, fiscal_year
,revenue,100*(revenue-LAG(revenue) OVER s)/NULLIF(revenue,0) AS change_revenue
,taxes,100*(taxes-LAG(taxes) OVER s)/NULLIF(taxes,0) AS change_taxes
,employees,employees-LAG(employees) OVER s AS change_employees
FROM companies c
JOIN reports r ON r.company_id = c.id
WINDOW s AS (PARTITION BY r.company_id ORDER BY fiscal_year, quarter)
)
SELECT *
FROM cte
WHERE quarter = 'Q2'; -- only to get specific quarter
-- comment this condition to get quarter to quarter comparison
ДБ <> Fiddle Demo
Ответ 2
Вы можете сделать это простым SQL, если вам нужно сравнить только две четверти. Программирование не требуется.
Подзапросов нет, просто присоединяйте компании к отчетам дважды по кварталу.
select
c.name,
r2.revenue,
100 * (r2.revenue - r1.revenue) / r2.revenue as revenue_change,
r2.taxes,
100 * (r2.taxes - r1.taxes) / r2.taxes as taxes_change,
r2.employees,
r2.employees - r1.employees as employees_change
from
companies c
LEFT JOIN reports r1 ON (c.id = r1.company_id and r1.quarter = 'q1')
LEFT JOIN reports r2 ON (c.id = r2.company_id and r2.quarter = 'q2')
См. Https://www.db-fiddle.com/f/6hwbPofSwAiqGBPFZWKxhi/0
Ответ 3
Очень сложно получить результат, используя чистый SQL. Но я делаю это.
Выполните следующие sql. Я надеюсь, что вы получите помощь с этим SQL.
select
qd2.name as Name,
qd2.Revenue as Revenue,
qd2.Revenue - qd1.Revenue as Revenue_Change,
qd2.Taxes as Taxes,
(qd2.Taxes - qd1.Taxes) * 100 / qd1.Taxes as Taxes_Change,
qd2.Employees as Employees,
(qd2.Employees - qd1.Employees) as Employees_Change
from
(
SELECT
(@cnt := @cnt + 1) AS rowNumber,
c.name as name,
r.revenue as Revenue,
r.taxes as Taxes,
r.employees as Employees
FROM
companies c
JOIN
reports r
CROSS JOIN (SELECT @cnt := 0) AS dummy
ON
r.company_id = c.id and
r.quarter = "q2"
order by name
) as qd2
JOIN
(
SELECT
(@cnt2 := @cnt2 + 1) AS rowNumber,
c.name as name,
r.revenue as Revenue,
r.taxes as Taxes,
r.employees as Employees
FROM
companies c
JOIN
reports r
CROSS JOIN (SELECT @cnt2 := 0) AS dummy
ON
r.company_id = c.id and
r.quarter = "q1"
order by name
) as qd1
ON qd1.rowNumber = qd2.rowNumber
Результаты следующие
Name Revenue Taxes Employees Revenue_Change Taxes_Change Employees_Change
ABC INC 11056 35000.86 568 6056 -22.221798 -32
XYZ LLC 5000 null 10 null null 5
Ответ 4
Я предоставил вам свой подход к этому, используя только одно соединение с группами по выражениям и агрегатным функциям. Последний шаг вычисления pecentajes - использование производной таблицы из первого подхода:
SELECT
der.name, der.quarter, der.fiscal_year,
der.revenue,
100 * der.revenue_change / der.revenue AS revenue_change,
der.taxes,
100 * der.taxes_change / der.taxes AS taxes_change,
der.employees,
der.employees_change
FROM
(
/* First approach (with raw changes) */
SELECT
c.name,
MAX(r.quarter) AS quarter,
ANY_VALUE(r.fiscal_year) AS fiscal_year,
SUM(CASE WHEN r.quarter = "Q2" THEN r.revenue END) AS revenue,
IF (COUNT(r.revenue) = 2,
SUM(CASE WHEN r.quarter = "Q1" THEN (-1 * r.revenue) ELSE r.revenue END),
NULL) AS revenue_change,
SUM(CASE WHEN r.quarter = "Q2" THEN r.taxes END) AS taxes,
IF (COUNT(r.taxes) = 2,
SUM(CASE WHEN r.quarter = "Q1" THEN (-1 * r.taxes) ELSE r.taxes END),
NULL) AS taxes_change,
SUM(CASE WHEN r.quarter = "Q2" THEN r.employees END) AS employees,
IF (COUNT(r.employees) = 2,
SUM(CASE WHEN r.quarter = "Q1" THEN (-1 * r.employees) ELSE r.employees END),
NULL) AS employees_change
FROM
companies AS c
LEFT JOIN
reports AS r ON r.company_id = c.id AND r.quarter IN ("Q1","Q2")
GROUP BY
c.id
) AS der
Вы можете проверить прогрессию до окончательного запроса по следующей ссылке:
https://www.db-fiddle.com/f/2tUC1gGJusVnXSyGhHGWc/3
Обобщение запроса
Я обобщил предыдущий запрос для сравнения кварталов за другой финансовый год, как вы спросили в комментариях, вот как я это сделаю:
SET @q1Year = 2017;
SET @q2Year = 2018;
SET @q1 = "Q1" COLLATE utf8mb4_unicode_ci;
SET @q2 = "Q2" COLLATE utf8mb4_unicode_ci;
SELECT
der.name,
der.compared_quarters,
der.quarter,
der.fiscal_year,
der.revenue,
100 * der.revenue_change / der.revenue AS revenue_change,
der.taxes,
100 * der.taxes_change / der.taxes AS taxes_change,
der.employees,
der.employees_change
FROM
(
/* This query generate raw changes on revenue, taxes and employees */
SELECT
c.name,
CONCAT(@q1,"-",@q1Year," vs ",@q2,"-",@q2Year) AS compared_quarters,
@q2 AS quarter,
@q2Year AS fiscal_year,
SUM(IF(r.quarter = @q2, r.revenue, 0)) AS revenue,
IF (COUNT(r.revenue) = 2,
SUM(IF(r.quarter = @q1, -1 * r.revenue, r.revenue)),
NULL) AS revenue_change,
SUM(IF(r.quarter = @q2, r.taxes, 0)) AS taxes,
IF (COUNT(r.taxes) = 2,
SUM(IF(r.quarter = @q1, -1 * r.taxes, r.taxes)),
NULL) AS taxes_change,
SUM(IF(r.quarter = @q2, r.employees, 0)) AS employees,
IF (COUNT(r.employees) = 2,
SUM(IF(r.quarter = @q1, -1 * r.employees, r.employees)),
NULL) AS employees_change
FROM
companies AS c
LEFT JOIN
reports AS r ON r.company_id = c.id
AND
((r.quarter = @q1 AND r.fiscal_year = @q1Year) OR (r.quarter = @q2 AND r.fiscal_year = @q2Year))
GROUP BY
c.id
) AS der;
Вы можете проверить пример по следующей ссылке:
https://www.db-fiddle.com/f/2tUC1gGJusVnXSyGhHGWc/4
Ответ 5
Я дал ему попробовать. Вот как бы я это сделал.
SELECT
c.name,
reportsQ2.quarter,
reportsQ2.revenue as revenu1,
reportsQ1.revenue as revenue2,
(COALESCE(reportsQ2.revenue, 0) - COALESCE(reportsQ1.revenue, 0)) as difference
FROM
reports as reportsQ2
LEFT JOIN
reports as reportsQ1 ON reportsQ1.company_id = reportsQ2.company_id
AND
reportsQ1.quarter = 'Q1'
LEFT JOIN
companies as c ON c.id = reportsQ2.company_id
WHERE
reportsQ2.quarter = 'Q2'
Поэтому я вначале выбрал все отчеты за второй квартал. Затем я присоединился к первому кварталу с тем же идентификатором компании.
На данный момент у вас уже есть вся необходимая информация, чтобы приступить к расчетам. Я использовал COALESCE()
чтобы убедиться, что нулевые значения не мешают вычислениям, чтобы вывести нулевое значение вместо действительного числа.
Наконец, я присоединился к столу companies
чтобы получить имена.
Я сделал математику со строками дохода и вывел их как difference
.
Надеюсь, это поможет!
Ответ 6
Это дало мне вывод, что вам нужно.
select
c.name,
r2.revenue,
concat(TRUNCATE(100 * (r2.revenue - r1.revenue) / r2.revenue,2),"%") as revenue_change,
r2.taxes,
concat(TRUNCATE( 100 * (r2.taxes - r1.taxes) / r2.taxes,2),"%") as taxes_change,
r2.employees,
r2.employees - r1.employees as employees_change
from
companies c
LEFT JOIN reports r1 ON (c.id = r1.company_id and r1.quarter = 'q1')
LEFT JOIN reports r2 ON (c.id = r2.company_id and r2.quarter = 'q2')
Выход:
![]()
Спасибо!!!