sqlpostgresqlstringsrepeat

SQL REPEAT: Repeat Strings for Separators, Placeholders and Text Bar Charts

How REPEAT(str, n) repeats a string n times and why it is a handy tool for separators, placeholders and text bar charts right inside SQL.

4 min di letturaReferencesql · postgresql · strings · repeat · mysql · formatting
Questo articolo è attualmente in russo — la traduzione in inglese è in corso.

REPEAT(str, n) повторяет строку str ровно n раз подряд и возвращает результат как одно значение text. Это рабочий инструмент строкового форматирования: из него собираются разделители для отчётов, каркасы плейсхолдеров вида ?, ?, ? под динамические запросы и компактные текстовые бар-чарты прямо в выдаче SQL — без цикла на стороне приложения.

Главное преимущество REPEAT в том, что длина повтора задаётся числом, а не хардкодом: вместо строки из сорока дефисов вы пишете REPEAT('-', 40), а само число можно вычислить из данных — например из длины имени или из значения метрики. Повтор остаётся внутри запроса, поэтому ширина разделителя, размер бара и форма плейсхолдера всегда согласованы с тем, что реально вернул SELECT. Ниже разобраны четыре сценария, где это особенно удобно, и граничные случаи n <= 0 и NULL, на которых чаще всего спотыкаются.

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

Аргументов два: что повторять и сколько раз. Результат — обычный text.

SELECT REPEAT('ab', 3);   -- ababab
SELECT REPEAT('-', 20);   -- a 20-dash rule
SELECT REPEAT(' ', 4) || 'indented';

Что стоит держать в голове:

  • Повторяется любая строка, не только один символ: REPEAT('=-', 5) даёт =-=-=-=-=-.
  • Результат легко склеивать через || с другим текстом.
  • Тип возвращаемого значения всегда строковый, даже если внутри цифры: REPEAT('0', 3) — это '000', а не число 0.

В PostgreSQL и MySQL функция называется и работает одинаково — REPEAT(str, n). В ClickHouse есть repeat(s, n) с тем же смыслом.

Разделители и заголовки отчётов

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

SELECT REPEAT('=', 40) AS header_rule
UNION ALL
SELECT '  MONTHLY REVENUE REPORT'
UNION ALL
SELECT REPEAT('=', 40);

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

SELECT
  name,
  REPEAT('-', LENGTH(name)) AS underline
FROM users
ORDER BY name
LIMIT 5;

Плейсхолдеры для динамических запросов

Иногда нужно собрать список ?, ?, ? или $1, $2, $3 под массив параметров переменной длины. REPEAT строит каркас, а лишний хвостовой разделитель убирается через TRIM или RTRIM.

-- Build "?,?,?" for an IN-list of 3 values
SELECT RTRIM(REPEAT('?,', 3), ',');   -- ?,?,?

Тот же приём годится для нумерованных плейсхолдеров PostgreSQL, если их генерирует код приложения, а вам нужно лишь проверить форму строки. Главное правило: повторяйте «элемент плюс разделитель», а потом отрезайте последний разделитель — так не придётся возиться с условием «не ставить запятую после последнего».

На практике число повторений почти всегда динамическое: его подставляют из длины массива параметров. Поэтому n стоит считать в коде или в подзапросе и передавать в REPEAT уже готовым целым, а не зашивать константу. Так одна и та же заготовка строки работает и для трёх значений, и для тридцати.

Текстовые бар-чарты и LPAD

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

SELECT
  country,
  COUNT(*)                              AS users_cnt,
  REPEAT('#', (COUNT(*) / 10)::int)     AS bar
FROM users
GROUP BY country
ORDER BY users_cnt DESC;

Каждый символ # здесь стоит за 10 пользователей. Чтобы столбцы выровнялись в аккуратную колонку, добавьте LPAD — он дополняет строку пробелами слева до фиксированной ширины:

SELECT
  dept,
  ROUND(AVG(salary))                                  AS avg_salary,
  LPAD(REPEAT('#', (AVG(salary) / 1000)::int), 30)    AS bar
FROM employees
GROUP BY dept
ORDER BY avg_salary DESC;

REPEAT рисует столбец, а LPAD(..., 30) гарантирует, что все строки занимают ровно 30 знаков — диаграмма не «съезжает». Связка REPEAT + LPAD/RPAD — основной рабочий приём для ASCII-визуализаций.

Граничные случаи с n <= 0

Здесь прячется главная ловушка. Если n равно нулю или отрицательно, REPEAT возвращает пустую строку, а не NULL и не ошибку.

SELECT REPEAT('x', 0);    -- '' (empty string)
SELECT REPEAT('x', -5);   -- '' (empty string)

Из этого следуют важные выводы:

  • В бар-чарте метрика, давшая 0 блоков, нарисует пустую строку — столбца просто не будет видно. Если нужен хотя бы маркер, подставьте минимум: REPEAT('#', GREATEST(value, 1)).
  • Если n оказывается NULL (например, из-за NULL в данных), весь результат REPEAT становится NULL. Подстрахуйтесь через COALESCE(n, 0).
  • Дробное n молча округляется/усекается под целое, поэтому считайте n заранее и приводите к int явно через ::int.

Опасность REPEAT почти всегда не в самой функции, а в том, откуда приходит n. На счастливом пути со статичной константой все три движка ведут себя одинаково, но как только n берётся из метрики, длины массива или результата COUNT, в него легко просачивается ноль, отрицательное число или NULL из данных. Поэтому стоит прогнать пограничные значения отдельно: REPEAT('#', 0), REPEAT('#', -1) и REPEAT('#', NULL) — чтобы увидеть, где появится пустая строка, а где весь столбец станет NULL, прежде чем это всплывёт в отчёте.

Ещё одна тонкость — размер результата. REPEAT('ab', 1000000) строит строку на два миллиона символов, и если n приходит из недоверенного ввода, такой повтор легко раздувает память и ширину строки в выдаче. Для текстовых баров ограничивайте множитель сверху (например, через LEAST), а саму формулу для n считайте и приводите к int заранее, а не зашивайте предположение прямо внутрь вызова REPEAT.

Ловушка: легко перепутать REPEAT с конкатенацией. REPEAT('ab', 3) — это 'ababab' (одна строка из шести символов), а не три отдельные строки. Если нужны именно строки таблицы, используйте generate_series, а REPEAT оставьте для форматирования текста.

Esercitati su esercizi reali

Risolvi esercizi nel trainer SQL con valutazione e suggerimenti istantanei.

Apri il trainer