sqlpostgresqllogmath

LOG в PostgreSQL: логарифмы, лог-шкалы и главная ловушка с MySQL

В PostgreSQL LOG(x) — десятичный логарифм, а LOG(b, x) — по любому основанию; разбираем отличие от MySQL и применение логарифмов для шкал и порядков величины.

10 мин чтенияСправочникsql · postgresql · log · math · mysql · clickhouse

LOG в PostgreSQL считает логарифм числа.

Если по-простому, логарифм отвечает на вопрос:

В какую степень нужно возвести основание, чтобы получить это число?

Например:

SELECT
  LOG(100) AS result;

Результат будет 2, потому что:

10^2 = 100

То есть LOG(100) в PostgreSQL означает «логарифм 100 по основанию 10».

На практике логарифмы нужны не только математикам. В SQL они пригождаются в отчётах, аналитике и графиках, когда значения слишком сильно отличаются друг от друга: один заказ на 100, другой на 100000, третий на 10000000. Обычная шкала в таком случае растягивается, а логарифм помогает аккуратно сжать значения и увидеть картину.

Но у LOG есть важная ловушка: в PostgreSQL LOG(x) — это десятичный логарифм, а в MySQL LOG(x) — натуральный логарифм. Один и тот же запрос в разных базах может молча вернуть разные числа.

Разберём спокойно: как работает LOG, чем он отличается от LN, где его применять и как не попасть в неприятную несовместимость между СУБД.

Что такое логарифм простыми словами

Логарифм — это обратная операция к возведению в степень.

Если:

10^3 = 1000

то:

log10(1000) = 3

Потому что вопрос такой:

В какую степень нужно возвести 10, чтобы получить 1000?

Ответ: в третью.

Ещё пример:

2^10 = 1024

Значит:

log2(1024) = 10

В SQL это можно посчитать так:

SELECT
  LOG(2, 1024) AS result;

Результат:

10

Именно поэтому логарифм удобно использовать там, где нас интересует не само число, а его порядок величины.

Две формы LOG в PostgreSQL

В PostgreSQL у LOG есть две формы.

Первая форма:

LOG(x)

Она считает логарифм x по основанию 10.

Вторая форма:

LOG(b, x)

Она считает логарифм x по основанию b.

Примеры:

SELECT
  LOG(100) AS a,
  LOG(1000) AS b,
  LOG(2, 8) AS c,
  LOG(2, 1024) AS d;

Результат будет примерно таким:

a b c d
2 3 3 10

Почему?

  • LOG(100) равно 2, потому что 10^2 = 100;
  • LOG(1000) равно 3, потому что 10^3 = 1000;
  • LOG(2, 8) равно 3, потому что 2^3 = 8;
  • LOG(2, 1024) равно 10, потому что 2^10 = 1024.

Запомните порядок аргументов:

LOG(base, value)

Сначала основание, потом число.

LOG(x) в PostgreSQL — это основание 10

Это нужно запомнить отдельно.

В PostgreSQL:

SELECT
  LOG(100) AS result;

вернёт 2.

Потому что PostgreSQL считает:

log10(100)

То есть логарифм по основанию 10.

Если нужен натуральный логарифм, по основанию e, используйте не LOG, а LN.

SELECT
  LN(100) AS result;

Результат будет примерно:

4.60517018598809

Для новичка здесь главный вывод такой:

В PostgreSQL LOG(x) и LN(x) — не одно и то же.

LOG(x) — десятичный логарифм.

LN(x) — натуральный логарифм.

Главная ловушка: в MySQL LOG(x) означает другое

Вот место, где часто ошибаются при переносе запросов между базами.

В PostgreSQL:

SELECT
  LOG(100) AS result;

вернёт 2.

А в MySQL похожий запрос вернёт примерно 4.605, потому что в MySQL LOG(x) с одним аргументом означает натуральный логарифм.

То есть:

СУБД LOG(100) означает Результат
PostgreSQL log10(100) 2
MySQL ln(100) 4.605...

И это особенно неприятно, потому что ошибки не будет. Запрос выполнится. Просто результат окажется другим.

Поэтому в переносимом SQL лучше не писать одинокий LOG(x), если есть риск, что запрос будет жить в разных СУБД.

Лучше писать явно.

Для десятичного логарифма:

SELECT
  LOG10(100) AS result;

Для натурального логарифма:

SELECT
  LN(100) AS result;

Так намерение видно сразу.

LOG10 и LN: безопасные имена

Если вам нужен логарифм по основанию 10, используйте LOG10.

SELECT
  LOG10(1000) AS result;

Результат:

3

Если нужен натуральный логарифм, используйте LN.

SELECT
  LN(1000) AS result;

Такой код легче читать и безопаснее переносить.

Даже в PostgreSQL, где LOG(1000) и LOG10(1000) дадут одно и то же, второй вариант иногда понятнее:

SELECT
  LOG10(amount) AS amount_log10
FROM orders;

По названию сразу видно: речь про основание 10.

Ограничения: число должно быть положительным

Логарифм нельзя посчитать для нуля и отрицательных чисел.

В PostgreSQL такой запрос упадёт с ошибкой:

SELECT
  LOG(0) AS result;

И такой тоже:

SELECT
  LOG(-5) AS result;

LOG не вернёт аккуратный NULL. Он остановит выполнение запроса ошибкой.

Поэтому перед логарифмом всегда нужно убедиться, что значение строго больше нуля.

Например:

SELECT
  id,
  LOG(amount) AS amount_log
FROM orders
WHERE amount > 0;

Если в таблице могут быть возвраты, скидки, нулевые суммы или технические записи, фильтр обязателен.

Как безопасно считать LOG для грязных данных

Иногда вы не хотите выкидывать строки из результата, но хотите считать логарифм только там, где это возможно.

Тогда удобно использовать CASE.

SELECT
  id,
  amount,
  CASE
    WHEN amount > 0 THEN LOG(amount)
    ELSE NULL
  END AS amount_log
FROM orders;

Так запрос не упадёт.

Для положительных сумм он посчитает логарифм, для нуля и отрицательных значений вернёт NULL.

Иногда для защиты от нуля используют NULLIF.

SELECT
  id,
  LOG(NULLIF(amount, 0)) AS amount_log
FROM orders;

Но этот вариант защищает только от нуля. Если в данных есть отрицательные числа, запрос всё равно может упасть. Поэтому универсальнее писать через CASE WHEN amount > 0.

Основание тоже должно быть правильным

Во второй форме:

LOG(b, x)

важны оба числа.

x должен быть строго больше нуля.

Основание b тоже должно быть строго больше нуля и не должно быть равно 1.

Почему основание 1 нельзя?

Потому что 1 в любой степени остаётся 1.

1^1 = 1
1^2 = 1
1^100 = 1

Из такого основания нельзя нормально получить, например, 8 или 100.

Поэтому такие значения для основания не подходят.

Правильные примеры:

SELECT
  LOG(2, 8) AS log2_8,
  LOG(10, 1000) AS log10_1000,
  LOG(5, 125) AS log5_125;

Где LOG полезен в реальных задачах

Логарифмы особенно полезны, когда значения отличаются на порядки.

Например, есть суммы заказов:

amount
5
20
300
7000
120000

На обычной шкале маленькие суммы почти исчезнут рядом с большими. Логарифм сжимает шкалу и помогает группировать значения по порядку величины.

Порядок величины — это примерно ответ на вопрос:

Число ближе к единицам, десяткам, сотням, тысячам или миллионам?

Например:

  • 19 — порядок 0;
  • 1099 — порядок 1;
  • 100999 — порядок 2;
  • 10009999 — порядок 3.

Для этого удобно использовать LOG10 или LOG в PostgreSQL.

Корзины по порядку величины

Допустим, нужно понять, как распределены оплаченные заказы по размеру суммы.

SELECT
  FLOOR(LOG(amount))::int AS magnitude,
  COUNT(*) AS orders_count
FROM orders
WHERE status = 'paid'
  AND amount > 0
GROUP BY FLOOR(LOG(amount))::int
ORDER BY magnitude;

Что делает запрос:

  1. Берёт только оплаченные заказы.
  2. Отбрасывает неположительные суммы.
  3. Считает десятичный логарифм суммы.
  4. Округляет вниз через FLOOR.
  5. Группирует заказы по порядку величины.

Пример результата:

magnitude orders_count
0 18
1 240
2 810
3 135
4 9

Как это читать:

  • 0 — суммы от 1 до 9;
  • 1 — суммы от 10 до 99;
  • 2 — суммы от 100 до 999;
  • 3 — суммы от 1000 до 9999;
  • 4 — суммы от 10000 до 99999.

Это очень удобно для быстрых аналитических срезов.

Почему тут нужен FLOOR

LOG(500) не равен целому числу.

Он примерно равен 2.699.

Потому что 500 лежит между 100 и 1000.

10^2 = 100
10^3 = 1000

Чтобы превратить логарифм в номер корзины, мы берём FLOOR.

SELECT
  FLOOR(LOG(500)) AS magnitude;

Результат:

2

То есть число 500 попадает в корзину сотен.

Подсчёт количества цифр

Ещё один полезный трюк: через логарифм можно посчитать количество цифр в положительном целом числе.

Формула такая:

floor(log10(n)) + 1

Например:

  • 7 имеет 1 цифру;
  • 42 имеет 2 цифры;
  • 999 имеет 3 цифры;
  • 1000 имеет 4 цифры.

В PostgreSQL:

SELECT
  id,
  salary,
  FLOOR(LOG10(salary))::int + 1 AS digits_count
FROM employees
WHERE salary > 0
ORDER BY salary DESC;

Можно использовать и LOG(salary), потому что в PostgreSQL LOG(x) — это основание 10.

Но LOG10(salary) читается яснее.

SELECT
  id,
  salary,
  FLOOR(LOG10(salary))::int + 1 AS digits_count
FROM employees
WHERE salary > 0;

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

Логарифм для графиков

Логарифм часто используют перед построением графиков.

Например, если на графике есть значения 10, 100, 1000, 1000000, обычная шкала может стать неудобной: маленькие значения прижмутся к нулю, а одно огромное значение растянет весь график.

Логарифм делает так, что расстояния между порядками становятся равными:

  • от 10 до 100;
  • от 100 до 1000;
  • от 1000 до 10000.

В SQL можно заранее подготовить значение для графика:

SELECT
  id,
  amount,
  LOG10(amount) AS amount_log10
FROM orders
WHERE amount > 0;

Потом уже строить график по amount_log10.

Важно: логарифм меняет шкалу. Это не «настоящая сумма», а преобразованное значение. В отчёте лучше явно подписывать такие колонки, чтобы читатель не перепутал.

Связь LOG и LN

LOG, LOG10 и LN считают логарифмы, но с разными основаниями.

В PostgreSQL:

SELECT
  LOG(100) AS log_default,
  LOG10(100) AS log10_value,
  LN(100) AS natural_log;

LOG(100) и LOG10(100) дадут 2.

LN(100) даст примерно 4.605.

Почему LN отдельно?

Потому что натуральный логарифм по основанию e часто используется в математике, статистике, финансах и некоторых аналитических формулах.

Для обычных бизнес-корзин вроде «единицы, десятки, сотни, тысячи» чаще удобнее основание 10.

Как поменять основание вручную

Если в СУБД нет удобной формы LOG(b, x), логарифм по любому основанию можно собрать через формулу смены основания:

log_b(x) = ln(x) / ln(b)

В PostgreSQL это тоже работает:

SELECT
  LN(8) / LN(2) AS log2_via_ln,
  LOG(2, 8) AS log2_builtin;

Оба результата будут равны 3.

То же самое можно сделать через десятичный логарифм:

SELECT
  LOG10(8) / LOG10(2) AS log2_via_log10;

Главное — делить логарифмы, посчитанные в одном и том же основании.

LOG в разных СУБД

Вот короткая таблица, которую стоит держать под рукой.

Задача PostgreSQL MySQL ClickHouse
Десятичный логарифм LOG(x) или LOG10(x) LOG10(x) log10(x)
Натуральный логарифм LN(x) LOG(x) или LN(x) log(x) или ln(x)
Логарифм по основанию 2 LOG(2, x) LOG2(x) или LOG(2, x) log2(x)
Любое основание LOG(b, x) LOG(b, x) log(x) / log(b)

Главная несовместимость:

SELECT
  LOG(100) AS result;

В PostgreSQL это основание 10.

В MySQL это основание e.

Поэтому для переносимого кода лучше писать явно:

SELECT
  LOG10(100) AS log10_value,
  LN(100) AS natural_log;

Почему лучше не полагаться на умолчания

Допустим, вы написали в PostgreSQL:

SELECT
  LOG(amount) AS amount_log
FROM orders;

Вы имели в виду десятичный логарифм.

Потом этот запрос кто-то переносит в MySQL. Запрос не падает. Он продолжает работать. Но теперь LOG(amount) означает натуральный логарифм.

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

Поэтому хороший стиль:

  • для основания 10 писать LOG10(x);
  • для основания e писать LN(x);
  • для другого основания писать LOG(b, x), если СУБД это поддерживает.

Так запрос легче читать и труднее сломать при переносе.

Тип результата и точность

В PostgreSQL LOG для числовых значений возвращает числовой результат с хорошей точностью.

Например:

SELECT
  LOG(3) AS result;

Результат будет не целым числом, потому что 3 не является точной степенью 10.

Для большинства учебных и аналитических задач об этом достаточно знать две вещи:

  • результат логарифма часто дробный;
  • сравнивать такие значения лучше аккуратно, без ожидания идеальной точности до последнего знака.

Если вам нужна высокая производительность на больших объёмах и достаточно приблизительной точности, в реальных проектах иногда отдельно думают о типах данных и приведении к double precision. Но для начинающего главное — сначала правильно понять основание и ограничения по положительным значениям.

Частые ошибки

Первая ошибка — думать, что LOG(x) везде означает одно и то же.

SELECT
  LOG(100) AS result;

В PostgreSQL результат будет 2.

В MySQL результат будет примерно 4.605.

Для понятного кода лучше так:

SELECT
  LOG10(100) AS result;

Вторая ошибка — считать логарифм от нуля.

SELECT
  LOG(0) AS result;

Такой запрос упадёт.

Лучше заранее фильтровать данные:

SELECT
  id,
  LOG10(amount) AS amount_log10
FROM orders
WHERE amount > 0;

Третья ошибка — защищаться только от нуля, когда в данных бывают отрицательные значения.

SELECT
  LOG10(NULLIF(amount, 0)) AS amount_log10
FROM orders;

Если amount = -10, проблема останется.

Надёжнее так:

SELECT
  id,
  CASE
    WHEN amount > 0 THEN LOG10(amount)
    ELSE NULL
  END AS amount_log10
FROM orders;

Четвёртая ошибка — перепутать порядок аргументов.

В PostgreSQL правильно:

SELECT
  LOG(2, 8) AS result;

Это значит:

log2(8)

Результат будет 3.

Пятая ошибка — забывать, что логарифм меняет смысл шкалы.

SELECT
  LOG10(amount) AS amount_log10
FROM orders;

Это уже не сумма заказа. Это логарифм суммы. В отчёте такую колонку нужно называть явно, чтобы не вводить людей в заблуждение.

Практический пример: распределение заказов по масштабу

Допустим, у нас есть интернет-магазин. Заказы бывают маленькие, средние и очень крупные. Нужно быстро понять, в каких порядках величины живёт основная масса оплат.

SELECT
  FLOOR(LOG10(amount))::int AS magnitude,
  COUNT(*) AS orders_count,
  MIN(amount) AS min_amount,
  MAX(amount) AS max_amount
FROM orders
WHERE status = 'paid'
  AND amount > 0
GROUP BY FLOOR(LOG10(amount))::int
ORDER BY magnitude;

Пример результата:

magnitude orders_count min_amount max_amount
0 12 1 9
1 345 10 99
2 1280 100 999
3 220 1000 9990
4 8 12000 85000

Такой отчёт сразу показывает, где основная масса заказов, а где редкие крупные значения.

Для обычной группировки по диапазонам можно было бы написать CASE WHEN. Но логарифм удобен, когда диапазоны идут по степеням десяти: десятки, сотни, тысячи, десятки тысяч.

Главное

LOG в PostgreSQL считает логарифм.

У него есть две основные формы:

SELECT
  LOG(x) AS log10_value;

Это логарифм по основанию 10.

SELECT
  LOG(b, x) AS custom_base_log;

Это логарифм x по основанию b.

Для натурального логарифма используйте LN.

SELECT
  LN(x) AS natural_log;

Самая важная ловушка: LOG(x) в PostgreSQL и LOG(x) в MySQL означают разное.

В PostgreSQL:

LOG(x) = log10(x)

В MySQL:

LOG(x) = ln(x)

Чтобы не путаться, пишите явно:

SELECT
  LOG10(amount) AS amount_log10,
  LN(amount) AS amount_ln
FROM orders
WHERE amount > 0;

Логарифм можно считать только для положительных значений. Если в данных бывают нули или отрицательные числа, защищайте запрос через WHERE amount > 0 или CASE.

LOG полезен для лог-шкал, группировки по порядкам величины, анализа больших разбросов и подсчёта количества цифр. Но используйте его осознанно: логарифм не просто красиво форматирует число, а меняет шкалу и смысл значения.

Закрепи на практике

Решай задачи в SQL-тренажёре с мгновенной проверкой и подсказками.

Открыть тренажёр