Diberi dua angka n
dan m
, saya ingin menghasilkan serangkaian formulir
1, 2, ..., (n-1), n, n, (n-1), ... 2, 1
dan ulangi m
kali.
Misalnya, untuk n = 3
dan m = 4
, saya ingin urutan 24 nomor berikut:
1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1
---------------- ---------------- ---------------- ----------------
Saya tahu bagaimana mencapai hasil ini dalam PostgreSQL dengan salah satu dari dua metode berikut:
Menggunakan kueri berikut, yang menggunakan generate_series
fungsi, dan beberapa trik untuk menjamin bahwa urutannya benar:
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 ;
... atau gunakan fungsi untuk tujuan yang sama, dengan adjoin dan nested loop:
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 ;
Bagaimana saya bisa melakukan yang setara dalam SQL standar atau dalam Transact-SQL / SQL Server?
sumber
Postgres
Anda dapat membuatnya bekerja dengan matematika tunggal
generate_series()
dan dasar (lihat fungsi matematika ).Dibungkus menjadi fungsi SQL sederhana:
Panggilan:
Menghasilkan hasil yang diinginkan. n dan m dapat berupa bilangan bulat apa pun di mana n * 2 * m tidak meluap
int4
.Bagaimana?
Dalam subquery:
Hasilkan jumlah total baris yang diinginkan ( n * 2 * m ), dengan angka naik sederhana. Saya beri nama
n2m
. 0 hingga N-1 (bukan 1 ke N ) untuk menyederhanakan operasi modulo berikut .Ambillah % n * 2 (
%
adalah operator modulo) untuk mendapatkan serangkaian n angka naik, m kali. Saya beri naman2
.Di kueri luar:
Tambahkan 1 ke bagian bawah ( n2 <n ).
Untuk bagian atas ( n2> = n ) cermin bagian bawah dengan n * 2 - n2 .
Saya menambahkan
ORDER BY
untuk menjamin pesanan yang diminta. Dengan versi saat ini atau Postgres juga berfungsi tanpaORDER BY
untuk permintaan sederhana - tetapi tidak harus dalam permintaan yang lebih kompleks! Itu detail implementasi (dan itu tidak akan berubah) tetapi tidak dijamin oleh standar SQL.Sayangnya,
generate_series()
Postgres spesifik dan bukan SQL standar, seperti yang telah dikomentari. Tapi kita bisa menggunakan kembali logika yang sama:SQL standar
Anda dapat membuat nomor seri dengan CTE rekursif alih-alih
generate_series()
, atau, lebih efisien untuk penggunaan berulang, membuat tabel dengan nomor integer seri satu kali. Siapa pun dapat membaca, tidak ada yang bisa menulis untuk itu!Kemudian, di atas
SELECT
menjadi lebih sederhana:sumber
Jika Anda membutuhkan SQL biasa. Secara teoritis itu harus bekerja pada kebanyakan DBMS (diuji pada PostgreSQL dan SQLite):
Penjelasan
Hasilkan seri 1..n
Berasumsi bahwa
n=3
Ini cukup sederhana dan dapat ditemukan di hampir semua dokumen tentang CTE rekursif. Namun kami membutuhkan dua contoh dari masing-masing nilai jadi
Hasilkan seri 1,1, .., n, n
Di sini kita hanya menggandakan nilai awal, yang memiliki dua baris, tetapi tandan kedua yang kita butuhkan dalam urutan terbalik, jadi kami akan memperkenalkan urutannya sedikit.
Sebelum kami memperkenalkan pesanan perhatikan bahwa ini juga hal. Kami dapat memiliki dua baris dalam kondisi awal dengan masing-masing tiga kolom, kami
n<3
masih bersyarat kolom tunggal. Dan, kami masih saja meningkatkan nilainyan
.Demikian juga, kita dapat mencampurnya sedikit, perhatikan kondisi awal kita berubah di sini : di sini kita memiliki
(6,2)
,(1,1)
Hasilkan seri 1..n, n..1
Kuncinya di sini adalah untuk menghasilkan seri, (1..n) dua kali, dan kemudian cukup mengubah urutan pada set kedua.
Ini
i
adalah urutan danz
nomor urutan (atau setengah dari urutan jika Anda mau). Jadi untuk urutan 1 kami meningkatkan pesanan dari 1 menjadi 3 dan untuk urutan 2 kami mengurangi pesanan dari 6 menjadi 4. Dan akhirnyaLipat gandakan seri menjadi
m
(lihat pertanyaan pertama dalam jawabannya)
sumber
Jika Anda menginginkan solusi portabel, Anda perlu menyadari bahwa ini pada dasarnya adalah masalah matematika .
Diberikan @n sebagai jumlah urutan tertinggi dan @x sebagai posisi nomor dalam urutan tersebut (dimulai dengan nol), fungsi berikut akan bekerja di SQL Server:
Anda dapat memeriksanya dengan CTE ini:
(Penjelasan cepat: fungsi menggunakan MODULO () untuk membuat urutan angka berulang dan ABS () untuk mengubahnya menjadi gelombang zig-zag. Operasi lain mengubah gelombang itu agar sesuai dengan hasil yang diinginkan.)
sumber
Di PostgreSQL, ini mudah,
sumber
Ini berfungsi dalam MS-SQL dan saya pikir dapat dimodifikasi untuk setiap rasa SQL.
sumber
Cara untuk melakukannya di SQL Server menggunakan cyt rekursif.
1) Hasilkan jumlah anggota yang diperlukan dalam seri (untuk n = 3 dan m = 4 akan menjadi 24 yaitu 2 * n * m)
2) Setelah itu menggunakan logika dalam
case
ekspresi, Anda dapat menghasilkan seri yang diperlukan.Sample Demo
Seperti yang disarankan oleh @AndriyM ..
case
ekspresi dapat disederhanakanDemo
sumber
Hanya menggunakan Matematika
+ - * /
dan Modulo dasar :Ini tidak memerlukan SGBD tertentu.
Dengan
numbers
menjadi tabel angka:Ini menghasilkan tabel angka (1-1000) tanpa menggunakan CTE rekursif. Lihat Sampel . 2 * n * m harus lebih kecil dari jumlah baris dalam angka.
Output dengan n = 3 dan m = 4:
Versi ini membutuhkan tabel angka yang lebih kecil (v> = n dan v> = m):
Lihat Sampel .
sumber
Fungsi dasar menggunakan iterator.
T-SQL
Postgres
sumber
sumber