Учитывая два числа n
и m
, я хочу создать серию вида
1, 2, ..., (n-1), n, n, (n-1), ... 2, 1
и повтори это m
раз.
Например, для n = 3
и m = 4
я хочу последовательность из следующих 24 чисел:
1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1
---------------- ---------------- ---------------- ----------------
Я знаю, как добиться этого результата в PostgreSQL любым из двух способов:
Используя следующий запрос, который использует generate_series
функцию, и несколько хитростей, чтобы гарантировать, что порядок правильный:
WITH parameters (n, m) AS
(
VALUES (3, 5)
)
SELECT
xi
FROM
(
SELECT
i, i AS xi
FROM
parameters, generate_series(1, parameters.n) AS x(i)
UNION ALL
SELECT
i + parameters.n, parameters.n + 1 - i AS xi
FROM
parameters, generate_series(1, parameters.n) AS x(i)
) AS s0
CROSS JOIN
generate_series (1, (SELECT m FROM parameters)) AS x(j)
ORDER BY
j, i ;
... или использовать функцию для той же цели с присоединенными и вложенными циклами:
CREATE FUNCTION generate_up_down_series(
_elements /* n */ integer,
_repetitions /* m */ integer)
RETURNS SETOF integer AS
$BODY$
declare
j INTEGER ;
i INTEGER ;
begin
for j in 1 .. _repetitions loop
for i in 1 .. _elements loop
return next i ;
end loop ;
for i in reverse _elements .. 1 loop
return next i ;
end loop ;
end loop ;
end ;
$BODY$
LANGUAGE plpgsql IMMUTABLE STRICT ;
Как я мог сделать эквивалент в стандартном SQL или в Transact-SQL / SQL Server?
источник
Postgres
Вы можете заставить его работать с одной
generate_series()
основной математикой (см. Математические функции ).Обернут в простую функцию SQL:
Вызов:
Создает желаемый результат. n и m может быть любым целым числом, где n * 2 * m не переполняется
int4
.Как?
В подзапросе:
Создайте желаемое общее количество строк ( n * 2 * m ) с простым возрастающим числом. Я называю это
n2m
. От 0 до N-1 (не от 1 до N ), чтобы упростить следующую операцию по модулю .Возьмем % n * 2 (
%
это оператор по модулю), чтобы получить последовательность из n возрастающих чисел, m раз. Я называю этоn2
.Во внешнем запросе:
Добавьте 1 к нижней половине ( n2 <n ).
Для верхней половины ( n2> = n ) зеркало нижней половины с n * 2 - n2 .
Я добавил,
ORDER BY
чтобы гарантировать запрошенный заказ. С текущими версиями или Postgres это также работает безORDER BY
простого запроса - но не обязательно в более сложных запросах! Это деталь реализации (и она не изменится), но не гарантируется стандартом SQL.К сожалению,
generate_series()
это специфический для Postgres, а не стандартный SQL, как уже было сказано. Но мы можем использовать ту же логику:Стандартный SQL
Вы можете генерировать серийные номера с помощью рекурсивного CTE вместо
generate_series()
или, более эффективно для многократного использования, создать таблицу с серийными целыми числами один раз. Любой может читать, никто не может писать в него!Тогда вышеупомянутое
SELECT
становится еще проще:источник
Если вам нужен простой SQL. Теоретически он должен работать на большинстве СУБД (проверено на PostgreSQL и SQLite):
объяснение
Генерация серии 1..n
При условии, что
n=3
Это довольно просто и может быть найдено почти в любой документации о рекурсивных CTE. Однако нам нужно два экземпляра каждого значения так
Генерировать серии 1,1, .., n, n
Здесь мы просто удваиваем начальное значение, в котором есть две строки, но вторая группа нам нужна в обратном порядке, поэтому мы немного введем порядок.
Прежде чем вводить порядок, обратите внимание, что это тоже вещь. У нас может быть две строки в начальном условии с тремя столбцами в каждом, наш
n<3
по-прежнему является условным столбцом. И мы все еще только увеличиваем ценностьn
.Аналогично, мы можем немного их перепутать, посмотреть, как меняется наше начальное условие : здесь мы имеем
(6,2)
,(1,1)
Генерация серии 1..n, n..1
Хитрость заключается в том, чтобы сгенерировать серию (1..n) дважды, а затем просто изменить порядок во втором наборе.
Здесь
i
порядок иz
номер последовательности (или половина последовательности, если хотите). Таким образом, для последовательности 1 мы увеличиваем порядок с 1 до 3, а для последовательности 2 мы уменьшаем порядок с 6 до 4. И, наконец,Умножьте ряд на
m
(см. первый запрос в ответе)
источник
Если вы хотите портативное решение, вы должны понимать, что это в основном математическая проблема.
Учитывая @n как наибольшее число последовательности и @x как позицию числа в этой последовательности (начиная с нуля), в SQL Server будет работать следующая функция:
Вы можете проверить это с этим CTE:
(Краткое объяснение: функция использует MODULO () для создания последовательности повторяющихся чисел и ABS (), чтобы превратить ее в зигзагообразную волну. Другие операции преобразуют эту волну в соответствии с желаемым результатом.)
источник
В PostgreSQL это легко,
источник
Это работает в MS-SQL, и я думаю, что может быть изменено для любой разновидности SQL.
источник
Способ сделать это в SQL Server с помощью рекурсивного cte.
1) Создайте необходимое количество членов в серии (для n = 3 и m = 4 это будет 24, что составляет 2 * n * m)
2) После этого, используя логику в
case
выражении, вы можете сгенерировать нужный ряд.Sample Demo
Как предлагает @AndriyM ..
case
выражение может быть упрощено доDemo
источник
Используя только базовые математические
+ - * /
и по модулю:Это не требует определенного SGBD.
С
numbers
таблицей чисел:Это создает таблицу чисел (1-1000) без использования рекурсивного CTE. Смотрите образец . 2 * n * m должно быть меньше номера строки в числах.
Выход с n = 3 и m = 4:
Для этой версии требуется таблица чисел меньшего размера (v> = n и v> = m):
Смотрите образец .
источник
Основная функция с использованием итераторов.
T-SQL
Postgres
источник
источник