процентные изменения могут быть рассчитаны от первоначальных пользователей
pct_change(i) = init_users * prod_from_0_i(pct_change)
, где pct_change
представлен как 1 +/- percent_users_change / 100
например,
# for language 1, init_users = 10
1991 (change 10%) -> 10 * (1.10) = 11
1993 (change 7.5%) -> 10 * (1.10)(1.075) = 11.825
Для этого нам нужна агрегатная функция Product, которая не реализована в SQL, однако мы можем реализовать ее с помощью логарифмов, мы можем применить агрегатную функцию SUM
.
Это работает из-заследующие тождества:
log(xy) = log(x) + log(y)
, обобщающие это для серии xs
log(xs) = log(x0) + log(x1) + ..., where x0,x1,... <- xs
= sum(log(x | x <- xs))
и
x = exp(log(x))
, то есть exp
& log
являются обратными функциями.
Также обратите внимание, что log
не определено для 0 и отрицательных значений.
комбинируя эти две тождества, мы получим:
exp(SUM(ln(x) | x <- xs, x > 0)) === prod(xs)
Таким образом, мы можем записатьзапрос в виде:
WITH united AS (
SELECT
EXTRACT(YEAR FROM initial_release) yr
, id
, initial_users :: NUMERIC users
, 1.0 change
FROM programming_language
UNION ALL
SELECT
survey_year yr
, language_id id
, null
, case when increase_or_decrease
then 1 + (percent_users_change / 100.0)
else 1 - (percent_users_change / 100.0) end change
FROM usage_data
)
SELECT yr, id,
FIRST_VALUE(users) OVER w * EXP(SUM(LN(u.change)) OVER w) users
FROM united u
WINDOW w as (PARTITION BY id ORDER BY yr ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
, где сначала объединяются начальные данные и более поздние данные об использовании, и вычисляется столбец change
, а затем users
для последовательных лет вычисляется из начальных users
.