Biorąc pod uwagę dwie liczby n
i m
chcę wygenerować serię formularza
1, 2, ..., (n-1), n, n, (n-1), ... 2, 1
i powtórz to m
razy.
Na przykład dla n = 3
i m = 4
chcę ciąg następujących 24 liczb:
1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1
---------------- ---------------- ---------------- ----------------
Wiem, jak osiągnąć ten wynik w PostgreSQL za pomocą jednej z dwóch metod:
Za pomocą następującego zapytania, które korzysta z generate_series
funkcji, i kilku sztuczek, aby zagwarantować, że kolejność jest właściwa:
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 ;
... lub użyj funkcji w tym samym celu, z pętlami przylegającymi i zagnieżdżonymi:
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 ;
Jak mogę zrobić ekwiwalent w standardowym SQL lub w Transact-SQL / SQL Server?
źródło
Postgres
Możesz sprawić, by działał z pojedynczą
generate_series()
i podstawową matematyką (patrz funkcje matematyczne ).Zawinięte w prostą funkcję SQL:
Połączenie:
Generuje pożądany wynik. n i m może być dowolną liczbą całkowitą, w której n * 2 * m nie przepełnia się
int4
.W jaki sposób?
W podzapytaniu:
Wygeneruj żądaną całkowitą liczbę rzędów ( n * 2 * m ), za pomocą prostej liczby rosnącej. Nazywam to
n2m
. 0 do N-1 (nie 1 do N ) w celu uproszczenia następującej operacji modulo .Weź to % n * 2 (
%
jest operatorem modulo), aby uzyskać serię n rosnących liczb, m razy. Nazywam ton2
.W zewnętrznym zapytaniu:
Dodaj 1 do dolnej połowy ( n2 <n ).
Dla górnej połowy ( n2> = n ) lustro dolnej połowy z n * 2 - n2 .
Dodałem,
ORDER BY
aby zagwarantować żądane zamówienie. W przypadku bieżących wersji lub Postgres działa również bezORDER BY
prostych zapytań - ale niekoniecznie w bardziej złożonych zapytaniach! Jest to szczegół implementacji (i to się nie zmieni), ale nie jest uzasadniony przez standard SQL.Niestety,
generate_series()
jak to zostało skomentowane , jest specyficzny dla Postgresa i niestandardowy SQL. Ale możemy ponownie użyć tej samej logiki:Standardowy SQL
Możesz wygenerować numery seryjne za pomocą rekurencyjnego CTE zamiast
generate_series()
lub, bardziej efektywnie do wielokrotnego użytku, utworzyć tabelę z numerami seryjnymi liczb całkowitych jeden raz. Każdy może czytać, nikt nie może do niego pisać!Następnie powyższe
SELECT
staje się jeszcze prostsze:źródło
Jeśli potrzebujesz zwykłego SQL. Teoretycznie powinien działać na większości DBMS (testowanych na PostgreSQL i SQLite):
Wyjaśnienie
Wygeneruj serie 1..n
Przy założeniu, że
n=3
Jest to dość proste i można je znaleźć w prawie wszystkich dokumentach na temat rekurencyjnych CTE. Potrzebujemy jednak dwóch wystąpień każdej wartości
Wygeneruj serie 1,1, .., n, n
Tutaj po prostu podwajamy wartość początkową, która ma dwa wiersze, ale potrzebna jest druga wiązka w odwrotnej kolejności, więc wprowadzimy kolejność za chwilę.
Zanim wprowadzimy zamówienie, zwróć uwagę, że to także jest rzecz. Możemy mieć dwa wiersze w stanie początkowym z trzema kolumnami,
n<3
nadal mamy warunek jednej kolumny. I wciąż tylko zwiększamy wartośćn
.Podobnie możemy je trochę pomieszać, obserwuj tutaj zmianę warunków początkowych : tutaj mamy
(6,2)
,(1,1)
Wygeneruj serie 1..n, n..1
Sztuczka polega na dwukrotnym wygenerowaniu szeregu (1..n), a następnie zmianie kolejności w drugim zestawie.
Oto
i
kolejność iz
numer sekwencji (lub połowa sekwencji, jeśli chcesz). Tak więc dla sekwencji 1 zwiększamy porządek z 1 do 3, a dla sekwencji 2 zmniejszamy porządek z 6 do 4. I na koniecPomnóż serię do
m
(patrz pierwsze zapytanie w odpowiedzi)
źródło
Jeśli chcesz przenośnego rozwiązania, musisz zdać sobie sprawę, że jest to w zasadzie problem matematyczny .
Biorąc pod uwagę @n jako najwyższą liczbę sekwencji i @x jako pozycję liczby w tej sekwencji (zaczynając od zera), następująca funkcja będzie działać w SQL Server:
Możesz to sprawdzić za pomocą tego CTE:
(Szybkie wyjaśnienie: funkcja używa MODULO () do utworzenia sekwencji powtarzających się liczb, a ABS () zamienia ją w falę zygzakowatą. Inne operacje przekształcają tę falę, aby pasowała do pożądanego wyniku.)
źródło
W PostgreSQL jest to łatwe,
źródło
Działa to w MS-SQL i myślę, że można je modyfikować dla dowolnego smaku SQL.
źródło
Sposób na wykonanie tego w SQL Server przy użyciu rekurencyjnego cte.
1) Wygeneruj wymaganą liczbę elementów w szeregu (dla n = 3 i m = 4 będzie to 24, co oznacza 2 * n * m)
2) Następnie za pomocą logiki w
case
wyrażeniu można wygenerować wymaganą serię.Sample Demo
Zgodnie z sugestią @AndriyM ..
case
wyrażenie można uprościćDemo
źródło
Używając tylko podstawowej matematyki
+ - * /
i Modulo:To nie wymaga określonego SGBD.
Z
numbers
bycia tabeli numer:Generuje to tabelę liczb (1-1000) bez użycia rekurencyjnego CTE. Zobacz próbkę . 2 * n * m musi być mniejsza niż liczba wierszy w liczbach.
Wyjście dla n = 3 i m = 4:
Ta wersja wymaga mniejszej tabeli liczb (v> = n oraz v> = m):
Zobacz próbkę .
źródło
Podstawowa funkcja wykorzystująca iteratory.
T-SQL
Postgres
źródło
źródło