sqlpostgresqlmathpower

The POWER Function in SQL: Exponents, Roots, and Compound Growth

How to use POWER for exponentiation, fractional roots, and compound-growth math in PostgreSQL, MySQL, and ClickHouse.

3 min de cititReferencesql · postgresql · math · power · functions
Acest articol este momentan în limba rusă — traducerea în engleză este în curs.

POWER(base, exponent) возводит число в степень: POWER(2, 10) равно 1024. Эта функция нужна там, где аналитическому запросу перестаёт хватать сложения и умножения — спрогнозировать сложный рост выручки, сжать длинный хвост сумм заказов кубическим корнем или извлечь корень любой степени через дробный показатель. Вся арифметика роста, нормализации метрик и корней держится на этом одном вызове.

Базовый синтаксис

POWER принимает два аргумента — основание и показатель степени — и возвращает число с плавающей точкой (в PostgreSQL это double precision). Псевдоним POW работает в MySQL и ClickHouse; в PostgreSQL живут оба имени, так что переносить запросы между ними проще.

SELECT POWER(2, 10) AS kib,      -- 1024
       POWER(10, 6) AS million,  -- 1000000
       POWER(2, -1) AS half;     -- 0.5

Отрицательный показатель даёт дробь, нулевой — всегда единицу. В PostgreSQL под рукой ещё и инфиксный оператор ^, который читается в формулах естественнее вызова функции:

SELECT 2 ^ 10 AS via_operator;   -- 1024 in PostgreSQL
  • В MySQL и ClickHouse ^ — это побитовый XOR, а вовсе не степень. Там берите POW, иначе получите молча неверный результат вместо ошибки.
  • Результат всегда вещественный; за целым числом оборачивайте вызов в ROUND(...)::bigint.

Дробные показатели и корни

Корень n-й степени — это та же степень, только показатель равен 1/n. У квадратного корня есть собственная функция SQRT, но POWER берёт корень любой степени, а не только второй.

SELECT POWER(27, 1.0 / 3) AS cube_root,    -- 3
       POWER(16, 0.25)     AS fourth_root,  -- 2
       SQRT(2)             AS sqrt2;        -- 1.4142...

Самое наглядное применение — сгладить разброс сумм заказов кубическим корнем. Это ходовой приём для дашбордов: он притягивает выбросы к остальной массе, и одна гигантская сделка перестаёт перетягивать на себя всю шкалу.

SELECT id,
       amount,
       ROUND(POWER(amount, 1.0 / 3), 2) AS dampened
FROM orders
WHERE status = 'paid'
ORDER BY amount DESC;

Геометрическое среднее зарплат по отделу — ещё один сюжет, где обычно блистают EXP и LN, но корень тоже выражается через POWER. На практике для произведения многих чисел берут именно логарифмический трюк — он устойчивее к переполнению:

SELECT dept,
       ROUND(EXP(AVG(LN(salary))), 2) AS geo_mean_salary
FROM employees
GROUP BY dept;
  • Пишите 1.0 / 3, а не 1 / 3. В PostgreSQL и MySQL целочисленное деление 1 / 3 обнулится в 0, показатель схлопнется, и любое основание превратится в 1. Грабли тихие: ошибки нет, просто весь столбец оказывается единицами.

Сложный процент и рост

Классическая формула сложного роста — base * (1 + rate) ^ periods. Прикинем, во что превратится средний чек, если он год подряд прибавляет по 3% в месяц:

SELECT ROUND(AVG(amount), 2)                          AS avg_now,
       ROUND(AVG(amount) * POWER(1 + 0.03, 12), 2)    AS avg_in_a_year
FROM orders
WHERE status = 'paid';

Обратная задача — найти средний месячный темп (CAGR) — тоже опирается на POWER, но с дробным показателем: мы извлекаем корень из накопленного множителя. Важно честно прочитать запрос ниже: он берёт корень 12-й степени из общего числа регистраций total, поэтому first_day и last_day здесь служат только подписями периода и в расчёт не входят. Это грубая прикидка «множителя за 12 шагов», а не строгий CAGR по отношению последнего значения к первому. Для настоящего CAGR множителем должно быть отношение last/first (например, last_count / first_count), показателем — единица, делённая на число периодов, то есть POWER(last_count / first_count, 1.0 / 12) - 1. Поэтому раскрытое выше выражение POWER(total, 1.0 / 12) стоит понимать как нормализацию объёма, а не как темп; прежде чем выносить такую колонку в витрину, замените total на корректное отношение граничных значений:

WITH bounds AS (
    SELECT MIN(created_at) AS first_day,
           MAX(created_at) AS last_day,
           COUNT(*)        AS total
    FROM users
)
SELECT ROUND(
         (POWER(total, 1.0 / 12) - 1) * 100, 2
       ) AS approx_monthly_growth_pct
FROM bounds;

Переполнение и подводные камни

Степени растут не линейно, а взрывообразно: каждый шаг показателя умножает результат заново, поэтому даже скромное основание мгновенно упирается в потолок типа. Большой показатель легко выскакивает за пределы double precision, и тогда PostgreSQL бросает value out of range: overflow вместо тихого NULL. Это, как ни странно, удобно — переполнение нельзя проглядеть, — но запрос приходится подстраховывать заранее: ограничивать диапазон показателя или переводить данные в numeric.

SELECT POWER(10, 308);   -- ok, near the double precision limit
SELECT POWER(10, 309);   -- ERROR: value out of range: overflow
  • Отрицательное основание с дробным показателем (POWER(-8, 0.5)) — это комплексное число; PostgreSQL вернёт ошибку, а не NaN.
  • В PostgreSQL POWER(numeric, numeric) точнее, но дороже версии для double precision. Для денег приводите к numeric осознанно, а не на всякий случай.
  • В ClickHouse pow всегда отдаёт Float64, так что на больших значениях ждите ошибок округления.
  • POWER(0, 0) равно 1 во всех трёх СУБД — это математическое соглашение, а не баг.

Держите показатели в разумных пределах, приводите типы явно и пишите 1.0 / n для корней — и POWER останется предсказуемым инструментом и для аналитики, и для финансовых формул.

Exersează pe probleme reale

Rezolvă probleme în antrenorul SQL cu notare instantanee și indicii.

Deschide antrenorul