sqlpostgresqlmathfunctions

SQRT в PostgreSQL: квадратный корень в SQL без сюрпризов

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

10 мин чтенияСправочникsql · postgresql · math · functions · analytics

SQRT — это функция, которая извлекает квадратный корень из числа.

Например:

SELECT SQRT(144);

Результат:

12

А так можно получить корень из двойки:

SELECT SQRT(2);

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

1.4142135623730951

На первый взгляд функция совсем простая: передали число — получили корень. Но в реальных запросах у SQRT быстро появляются важные нюансы.

Она нужна не только в школьной математике. В SQL квадратный корень часто встречается в аналитике, геометрии и статистике:

  • посчитать расстояние между двумя точками;
  • найти длину вектора;
  • посчитать стандартное отклонение вручную;
  • нормализовать значения;
  • измерить «разброс» данных.

Именно поэтому важно понимать не только синтаксис, но и поведение функции на плохих данных.

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

Синтаксис очень простой:

SQRT(number)

Функция принимает число и возвращает его квадратный корень.

SELECT
    SQRT(144) AS root_144,
    SQRT(25) AS root_25,
    SQRT(2) AS root_2;

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

root_144 | root_25 | root_2
---------+---------+-------------------
12       | 5       | 1.4142135623730951

Квадратный корень — это число, которое при умножении само на себя даёт исходное значение.

Например:

12 * 12 = 144
5 * 5 = 25

Поэтому SQRT(144) возвращает 12, а SQRT(25) возвращает 5.

Где SQRT встречается в SQL

В обычных бизнес-запросах SQRT нужна не каждый день. Но как только начинается аналитика, функция становится очень полезной.

Например, расстояние между двумя точками считается через корень:

distance = sqrt(dx * dx + dy * dy)

Стандартное отклонение тоже связано с квадратным корнем:

stddev = sqrt(variance)

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

Если зарплаты измеряются в рублях, дисперсия будет в «рублях в квадрате». Это неудобно для чтения. Квадратный корень возвращает результат обратно в рубли.

Типы: numeric и double precision

В PostgreSQL SQRT может работать с разными числовыми типами.

Если передать обычное число с плавающей точкой, результат будет типа double precision.

SELECT pg_typeof(SQRT(2::double precision)) AS result_type;

Результат:

double precision

Если передать numeric, PostgreSQL использует вариант функции для numeric, и результат тоже будет numeric.

SELECT pg_typeof(SQRT(2::numeric)) AS result_type;

Результат:

numeric

Разница важна.

numeric хранит числа точнее, особенно когда речь идёт о деньгах и аккуратных расчётах.

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

Когда выбирать numeric

Используйте numeric, если важна точность.

Например, если сумма заказа хранится как numeric, корень из неё тоже останется numeric.

SELECT
    amount,
    SQRT(amount) AS amount_root
FROM orders
WHERE status = 'paid';

Для денег обычно используют numeric, потому что финансовые значения не любят погрешности.

Да, SQRT(amount) в таком случае может быть медленнее, чем расчёт в double precision, но зато результат будет аккуратнее.

Когда выбирать double precision

Используйте double precision, если важна скорость и допустима небольшая погрешность.

Например, при расчёте расстояний на большом количестве строк.

SELECT
    id,
    SQRT(
        power((x - 10)::double precision, 2)
      + power((y - 20)::double precision, 2)
    ) AS distance
FROM points;

Для миллионов строк это может быть заметно быстрее, чем расчёты в numeric.

Практическое правило простое:

  • деньги и точные отчёты — чаще numeric;
  • геометрия, аналитика, статистика на больших данных — чаще double precision.

SQRT и отрицательные числа

Квадратный корень из отрицательного числа в обычной вещественной математике не определён.

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

SELECT SQRT(-1);

Результат:

ERROR: cannot take square root of a negative number

Это важный момент.

PostgreSQL не вернёт NULL, не подставит ноль и не сделает вид, что всё хорошо. Он остановит запрос.

В учебном примере это выглядит безобидно. Но в реальной таблице одна плохая строка может уронить весь отчёт.

SELECT
    id,
    SQRT(value) AS root_value
FROM metrics;

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

Почему отрицательное значение может появиться неожиданно

Иногда мы передаём в SQRT не готовую колонку, а выражение.

Например:

SELECT
    id,
    SQRT(amount - refund_amount) AS net_root
FROM orders;

Кажется, что amount должен быть больше refund_amount. Но в данных могут быть возвраты, корректировки, ошибки импорта или нестандартные кейсы.

Если выражение amount - refund_amount станет отрицательным, SQRT упадёт.

Поэтому перед корнем нужно подумать:

Может ли внутри оказаться минус?

Если да — нужно явно решить, что с ним делать.

Как защититься через ABS

Если знак не важен, можно взять модуль через ABS.

SELECT
    id,
    SQRT(ABS(amount - refund_amount)) AS magnitude
FROM orders;

ABS превращает отрицательное число в положительное.

ABS(-25) = 25
ABS(25) = 25

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

Например:

SELECT
    id,
    SQRT(ABS(amount - 100)) AS magnitude,
    SIGN(amount - 100) AS direction
FROM orders;

Здесь:

  • magnitude показывает размер отклонения;
  • direction показывает направление: значение выше или ниже базовой точки.

Как защититься через GREATEST

Иногда отрицательное значение появляется из-за маленькой погрешности округления.

Например, математически выражение должно быть равно нулю, но из-за вычислений в double precision получается что-то вроде -0.00000000001.

В таком случае удобно использовать GREATEST.

SELECT
    SQRT(GREATEST(value, 0)) AS safe_root
FROM metrics;

GREATEST(value, 0) выбирает большее из двух значений.

Если value положительное, оно останется как есть.

Если value отрицательное, вместо него будет 0.

GREATEST(25, 0) = 25
GREATEST(-3, 0) = 0

Это полезная защита в статистических формулах, где маленький минус может появиться не из-за смысла данных, а из-за погрешности вычислений.

NULL не вызывает ошибку

Отрицательное число вызывает ошибку, а вот NULL ведёт себя спокойно.

SELECT SQRT(NULL);

Результат:

NULL

Это обычное поведение SQL-функций: если вход неизвестен, результат тоже неизвестен.

Например:

SELECT
    id,
    SQRT(value) AS root_value
FROM metrics;

Если value равен NULL, то root_value тоже будет NULL. Запрос не упадёт.

Но если value равен -1, запрос завершится ошибкой.

Это разные ситуации:

  • NULL — значения нет;
  • отрицательное число — значение есть, но для квадратного корня оно недопустимо.

Евклидово расстояние между точками

Самое классическое применение SQRT — расстояние между двумя точками.

Представим координатную плоскость. Есть точка пользователя и точка офиса. Нужно понять, насколько пользователь далеко.

Расстояние считается по теореме Пифагора:

distance = sqrt(dx * dx + dy * dy)

Где:

  • dx — разница по первой координате;
  • dy — разница по второй координате.

Допустим, в таблице users есть координаты lat и lon.

Найдём расстояние до условной точки с координатами 40.0 и -3.7.

SELECT
    id,
    name,
    SQRT(
        power(lat - 40.0, 2)
      + power(lon - (-3.7), 2)
    ) AS dist
FROM users
ORDER BY dist
LIMIT 10;

Что происходит:

power(lat - 40.0, 2)

считает квадрат разницы по первой координате.

power(lon - (-3.7), 2)

считает квадрат разницы по второй координате.

Потом мы складываем эти квадраты и берём корень.

Так получаем расстояние.

Для учебной геометрии и простых задач этого достаточно. Для настоящих расстояний на Земле обычно нужны более сложные формулы, потому что Земля не плоская.

Когда SQRT можно не вызывать

Есть интересный приём: если вам нужно только отсортировать точки по близости, квадратный корень можно не считать.

Почему?

Потому что квадратный корень сохраняет порядок для неотрицательных чисел.

Если одно расстояние в квадрате меньше другого, то и само расстояние тоже меньше.

Например:

9 < 16
sqrt(9) < sqrt(16)
3 < 4

Поэтому вместо такого запроса:

SELECT
    id,
    name,
    SQRT(
        power(lat - 40.0, 2)
      + power(lon - (-3.7), 2)
    ) AS dist
FROM users
ORDER BY dist
LIMIT 10;

можно написать так:

SELECT
    id,
    name
FROM users
ORDER BY
    power(lat - 40.0, 2)
  + power(lon - (-3.7), 2)
LIMIT 10;

Результат по порядку будет тем же: ближайшие пользователи останутся ближайшими.

Но база не будет считать SQRT для каждой строки.

Если таблица маленькая, разницу можно не заметить. А на миллионах строк лишний корень в сортировке уже может стоить времени.

Правило:

Если нужно само расстояние — считайте SQRT. Если нужен только порядок — можно сортировать по квадрату расстояния.

power или оператор ^

В PostgreSQL степень можно записать через power.

SELECT power(5, 2) AS square;

Результат:

25

Также есть оператор ^.

SELECT 5 ^ 2 AS square;

Но для начинающих и для читаемости чаще лучше использовать power.

power(x, 2)

Так сразу видно: мы возводим значение в степень.

К тому же в некоторых языках программирования символ ^ означает не степень, а побитовое исключающее ИЛИ. Из-за этого он может путать людей, которые пришли в SQL из Python, JavaScript или Java.

Стандартное отклонение и SQRT

Ещё одно важное применение SQRT — стандартное отклонение.

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

Например, в одном отделе зарплаты почти одинаковые:

100000, 102000, 98000

В другом отделе разброс большой:

50000, 120000, 250000

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

В PostgreSQL уже есть готовые функции:

stddev_pop(value)
stddev_samp(value)

В реальных задачах лучше использовать их.

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

Стандартное отклонение руками

Одна из формул для генерального стандартного отклонения выглядит так:

stddev = sqrt(avg(x * x) - avg(x) * avg(x))

Посчитаем разброс зарплат по отделам.

SELECT
    dept,
    SQRT(AVG(power(salary, 2)) - power(AVG(salary), 2)) AS std_pop_manual,
    stddev_pop(salary) AS std_pop_check
FROM employees
GROUP BY dept;

Здесь:

  • AVG(power(salary, 2)) — среднее от квадратов зарплат;
  • power(AVG(salary), 2) — квадрат средней зарплаты;
  • разность между ними — дисперсия;
  • SQRT превращает дисперсию в стандартное отклонение.

Колонки std_pop_manual и std_pop_check должны получиться очень близкими.

Но в боевом коде всё равно лучше писать так:

SELECT
    dept,
    stddev_pop(salary) AS salary_std
FROM employees
GROUP BY dept;

Готовая функция короче, понятнее и лучше выражает намерение.

Оконный пример со стандартным отклонением

Формулу со SQRT можно использовать и в оконных функциях.

Например, покажем каждому сотруднику зарплату и стандартное отклонение зарплат внутри его отдела.

SELECT
    id,
    dept,
    salary,
    SQRT(
        AVG(power(salary, 2)) OVER w
      - power(AVG(salary) OVER w, 2)
    ) AS dept_std
FROM employees
WINDOW w AS (PARTITION BY dept);

Здесь окно:

WINDOW w AS (PARTITION BY dept)

говорит:

Считай средние значения отдельно внутри каждого отдела.

В результате каждая строка сотрудника получит рядом показатель разброса зарплат по своему отделу.

Для обучения это отличный пример: видно, как агрегаты, окна и SQRT складываются в настоящую аналитическую формулу.

Погрешность double precision

При работе с double precision иногда возникают маленькие погрешности.

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

-0.00000000001

Если передать его в SQRT, запрос упадёт.

Поэтому в ручных статистических формулах часто добавляют защиту:

SELECT
    dept,
    SQRT(
        GREATEST(
            AVG(power(salary::double precision, 2))
          - power(AVG(salary::double precision), 2),
            0
        )
    ) AS std_pop_manual
FROM employees
GROUP BY dept;

GREATEST(..., 0) не даёт случайному маленькому минусу попасть внутрь корня.

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

SQRT и агрегаты

SQRT часто используют вместе с агрегатами.

Например, можно посчитать длину вектора по сумме квадратов.

SELECT
    user_id,
    SQRT(SUM(power(score, 2))) AS vector_length
FROM user_scores
GROUP BY user_id;

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

  1. Для каждого пользователя берёт его значения score.
  2. Возводит каждое значение в квадрат.
  3. Складывает квадраты.
  4. Берёт квадратный корень.

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

Подобные формулы часто встречаются в аналитике, рекомендациях и машинном обучении. Даже если вы пока не пишете ML-модели в SQL, полезно узнавать этот шаблон:

sqrt(sum(x * x))

SQRT в отчётах: пример с отклонением от плана

Представим, что есть таблица продаж. У каждой строки есть план и факт.

sales_plan
sales_fact

Хотим измерить размер отклонения без учёта знака.

SELECT
    id,
    sales_plan,
    sales_fact,
    SQRT(power(sales_fact - sales_plan, 2)) AS absolute_deviation
FROM sales;

Математически это то же самое, что модуль:

SELECT
    id,
    sales_plan,
    sales_fact,
    ABS(sales_fact - sales_plan) AS absolute_deviation
FROM sales;

Для простого отклонения лучше использовать ABS: он короче и понятнее.

Но пример полезен, потому что показывает идею:

sqrt(x * x) = abs(x)

А когда отклонений несколько, например по двум координатам или нескольким признакам, появляется уже сумма квадратов и настоящий смысл SQRT.

Когда SQRT использовать не надо

SQRT не стоит добавлять в запрос просто «для красоты».

Если вы сортируете по расстоянию и не выводите само расстояние, можно сортировать по квадрату расстояния.

Если вы считаете стандартное отклонение, в боевом коде лучше использовать встроенную функцию stddev_pop или stddev_samp.

Если вам нужен модуль одного числа, используйте ABS, а не SQRT(power(x, 2)).

Например, так лучше:

SELECT ABS(amount - 100) AS diff
FROM orders;

А так избыточно:

SELECT SQRT(power(amount - 100, 2)) AS diff
FROM orders;

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

MySQL: отрицательный аргумент ведёт себя иначе

При переносе запросов важно помнить: разные СУБД могут по-разному реагировать на плохой вход.

В MySQL корень из отрицательного числа может вернуть NULL.

SELECT SQRT(-1);

То есть запрос не обязательно упадёт так же жёстко, как в PostgreSQL.

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

Например, если дальше результат участвует в агрегатах, пропущенные значения могут изменить итоговую картину.

Поэтому при переносе логики из PostgreSQL в MySQL проверяйте поведение на отрицательных значениях отдельно.

ClickHouse: возможен nan

В ClickHouse отрицательный аргумент для корня может дать специальное значение nan.

SELECT sqrt(-1);

nan означает «not a number», то есть «не число».

Опасность в том, что такое значение может пройти дальше по расчётам и испортить агрегаты или вывод.

PostgreSQL в этом смысле ведёт себя строже: он останавливает запрос ошибкой. Это неприятно в моменте, зато проблема становится видна сразу.

Как писать безопасные запросы с SQRT

Перед тем как использовать SQRT, полезно пройти короткую проверку.

Первый вопрос:

Может ли аргумент стать отрицательным?

Если да, выберите защиту.

Для величины без направления:

SELECT SQRT(ABS(value)) AS root_value
FROM metrics;

Для статистической формулы с возможной погрешностью:

SELECT SQRT(GREATEST(variance, 0)) AS stddev
FROM stats;

Второй вопрос:

Нужна ли точность numeric или хватит double precision?

Для точных денежных расчётов оставляйте numeric.

Для тяжёлой аналитики можно явно перейти к double precision.

SELECT SQRT(value::double precision) AS root_value
FROM metrics;

Третий вопрос:

Нужен ли сам корень или только порядок?

Если нужен только порядок, часто можно обойтись без SQRT.

SELECT
    id,
    name
FROM users
ORDER BY
    power(lat - 40.0, 2)
  + power(lon - (-3.7), 2)
LIMIT 10;

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

Первая ошибка — забыть про отрицательные значения.

SELECT SQRT(amount - refund_amount) AS root_value
FROM orders;

Если разность уйдёт в минус, запрос упадёт.

Вторая ошибка — использовать SQRT там, где достаточно ABS.

SELECT SQRT(power(amount - 100, 2)) AS diff
FROM orders;

Лучше так:

SELECT ABS(amount - 100) AS diff
FROM orders;

Третья ошибка — считать корень при сортировке, хотя нужен только порядок.

ORDER BY SQRT(power(x - 10, 2) + power(y - 20, 2))

Можно проще:

ORDER BY power(x - 10, 2) + power(y - 20, 2)

Четвёртая ошибка — вручную писать стандартное отклонение в продакшене, когда есть встроенная функция.

SELECT stddev_pop(salary) AS salary_std
FROM employees;

Пятая ошибка — не учитывать тип данных. numeric точнее, но медленнее. double precision быстрее, но приближённый.

Главное из статьи

SQRT в PostgreSQL извлекает квадратный корень из числа.

SELECT SQRT(144);

Результат:

12

Функция полезна в геометрии, статистике и аналитике: расстояния между точками, длины векторов, дисперсия и стандартное отклонение.

Главная опасность — отрицательный аргумент.

SELECT SQRT(-1);

В PostgreSQL такой запрос завершится ошибкой. Поэтому, если внутри корня может появиться минус, используйте ABS или GREATEST.

SELECT SQRT(ABS(value)) AS root_value
FROM metrics;
SELECT SQRT(GREATEST(variance, 0)) AS stddev
FROM stats;

Для точных расчётов используйте numeric, для быстрой аналитики чаще подходит double precision.

Если нужно только отсортировать точки по близости, сам корень можно не считать: порядок по квадрату расстояния будет тем же.

ORDER BY power(x - 10, 2) + power(y - 20, 2)

А если нужно стандартное отклонение, в реальном коде лучше брать встроенные функции PostgreSQL:

SELECT stddev_pop(value) AS stddev
FROM metrics;

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

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

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

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