Diberikan string yang mungkin berisi beberapa contoh pembatas, saya ingin membuat semua substring dimulai setelah karakter itu.
Sebagai contoh, diberi string seperti 'a.b.c.d.e'
(atau array {a,b,c,d,e}
, saya kira), saya ingin menghasilkan array seperti:
{a.b.c.d.e, b.c.d.e, c.d.e, d.e, e}
Penggunaan yang dimaksud adalah sebagai pemicu untuk mengisi kolom agar lebih mudah untuk menanyakan bagian nama domain (mis. Temukan semua q.x.t.com
untuk kueri t.com
) setiap kali kolom lain ditulis.
Sepertinya cara yang canggung untuk menyelesaikan ini (dan itu mungkin sangat baik), tapi sekarang saya ingin tahu bagaimana fungsi seperti ini dapat ditulis dalam SQL (Postgres ').
Ini adalah nama domain email sehingga sulit untuk mengatakan berapa jumlah elemen maksimum yang mungkin, tetapi tentu saja sebagian besar adalah <5.
sumber
Jawaban:
Saya tidak berpikir Anda perlu kolom terpisah di sini; ini adalah masalah XY. Anda hanya mencoba melakukan pencarian sufiks. Ada dua cara utama untuk mengoptimalkannya.
Ubah kueri sufiks menjadi kueri awalan
Anda pada dasarnya melakukan ini dengan membalikkan semuanya.
Pertama-tama buat indeks di bagian belakang kolom Anda:
Kemudian kueri menggunakan yang sama:
Anda dapat melakukan
UPPER
panggilan jika Anda ingin membuatnya case-sensitive:Indeks Trigram
Pilihan lainnya adalah indeks trigram. Anda harus menggunakan ini jika Anda membutuhkan infiks kueri (
LIKE 'something%something'
atauLIKE '%something%'
ketik kueri).Pertama-tama aktifkan ekstensi indeks trigram:
(Seharusnya PostgreSQL keluar dari kotak tanpa instalasi tambahan.)
Kemudian buat indeks trigram pada kolom Anda:
Kemudian cukup pilih:
Sekali lagi, Anda bisa melempar dalam
UPPER
untuk membuatnya case sensitif jika Anda suka:Pertanyaan Anda ditulis
Indeks trigram sebenarnya bekerja menggunakan bentuk yang agak lebih umum dari apa yang Anda minta di bawah tenda. Ini memecah string menjadi beberapa bagian (trigram) dan membangun indeks berdasarkan itu. Indeks kemudian dapat digunakan untuk mencari kecocokan yang jauh lebih cepat daripada pemindaian berurutan, tetapi untuk infiks serta kueri dan awalan kueri. Selalu berusaha untuk menghindari menciptakan kembali apa yang telah dikembangkan orang lain ketika Anda bisa.
Kredit
Kedua solusi tersebut cukup banyak kata demi kata dari Memilih metode pencarian teks PostgreSQL . Saya sangat merekomendasikan untuk membaca analisis rinci opsi pencarian teks yang tersedia di PotsgreSQL.
sumber
Saya pikir ini adalah favorit saya.
BARIS
ARRAYS
sumber
BARIS
ATAU
ARRAYS
ATAU
sumber
Pertanyaan ditanyakan
Meja tes:
CTE rekursif dalam subquery LATERAL
The
CROSS JOIN LATERAL
(, LATERAL
untuk pendek) adalah aman, karena hasil agregat subquery selalu mengembalikan berturut-turut. Anda mendapatkan ...str = ''
di dalam tabel dasarstr IS NULL
di tabel dasarDibungkus dengan konstruktor array murah di subquery, jadi tidak ada agregasi di kueri luar.
Pameran fitur SQL, tetapi overhead rCTE dapat mencegah kinerja terbaik.
Brute force untuk sejumlah elemen sepele
Untuk kasus Anda dengan sejumlah kecil elemen , pendekatan sederhana tanpa subquery mungkin lebih cepat:
Dengan asumsi maksimum 5 elemen seperti Anda berkomentar. Anda dapat dengan mudah memperluas untuk lebih banyak.
Jika domain tertentu memiliki lebih sedikit elemen,
substring()
ekspresi berlebih mengembalikan NULL dan dihapus oleharray_remove()
.Sebenarnya, ekspresi dari atas (
right(str, strpos(str, '.')
), bersarang beberapa kali mungkin lebih cepat (meskipun canggung untuk dibaca) karena fungsi ekspresi reguler lebih mahal.Garpu dari permintaan @ Dudu
@ Permintaan pintar Dudu dapat ditingkatkan dengan
generate_subscripts()
:Juga menggunakan
LEFT JOIN LATERAL ... ON true
untuk mempertahankan kemungkinan baris dengan nilai NULL.Fungsi PL / pgSQL
Logika serupa dengan rCTE. Lebih sederhana dan lebih cepat dari yang Anda miliki:
The
OUT
parameter dikembalikan pada akhir fungsi otomatis.Tidak perlu menginisialisasi
result
, karenaNULL::text[] || text 'a' = '{a}'::text[]
.Ini hanya berfungsi dengan
'a'
diketik dengan benar.NULL::text[] || 'a'
(string literal) akan memunculkan kesalahan karena Postgres memiliharray || array
operator.strpos()
kembali0
jika tidak ada titik yang ditemukan, jadiright()
mengembalikan string kosong dan loop berakhir.Ini mungkin yang tercepat dari semua solusi di sini.
Semuanya bekerja di Postgres 9.3+
(kecuali untuk notasi array pendek
arr[3:]
. Saya menambahkan batas atas pada biola untuk membuatnya bekerja di halaman 9.3:.arr[3:999]
)SQL Fiddle.
Pendekatan berbeda untuk mengoptimalkan pencarian
Saya dengan @ jpmc26 (dan diri Anda sendiri): pendekatan yang sama sekali berbeda lebih disukai. Saya suka kombinasi jpmc26
reverse()
dan atext_pattern_ops
.Indeks trigram akan lebih baik untuk pertandingan parsial atau fuzzy. Tetapi karena Anda hanya tertarik pada seluruh kata , Pencarian Teks Lengkap adalah pilihan lain. Saya mengharapkan ukuran indeks yang jauh lebih kecil dan dengan demikian kinerja yang lebih baik.
pg_trgm serta FTS mendukung kueri tidak sensitif kasus , btw.
Nama host seperti
q.x.t.com
ataut.com
(kata-kata dengan titik sebaris) diidentifikasi sebagai tipe "host" dan diperlakukan sebagai satu kata. Tetapi ada juga pencocokan awalan di FTS (yang tampaknya kadang-kadang diabaikan). Manual:Menggunakan ide cerdas @ jpmc26 dengan
reverse()
, kita dapat membuat ini berfungsi:Yang didukung oleh indeks:
Perhatikan
'simple'
konfigurasi: Kami tidak ingin stemming atau tesaurus digunakan dengan'english'
konfigurasi default .Atau (dengan variasi yang lebih besar dari kemungkinan pertanyaan) kita bisa menggunakan kemampuan pencarian frasa baru dari pencarian teks di Postgres 9.6. Catatan rilis:
Pertanyaan:
Ganti dot (
'.'
) dengan spasi (' '
) untuk mencegah pengurai mengklasifikasikan 't.com' sebagai nama host dan alih-alih gunakan setiap kata sebagai leksem yang terpisah.Dan indeks yang cocok untuk digunakan:
sumber
Saya datang dengan sesuatu yang semi-bisa diterapkan, tapi saya suka umpan balik tentang pendekatan. Saya telah menulis sangat sedikit PL / pgSQL jadi merasa seperti semua yang saya lakukan cukup meretas dan saya terkejut ketika bekerja.
Meskipun demikian, di sinilah saya harus:
Ini berfungsi seperti ini:
sumber
Saya menggunakan fungsi jendela:
Hasil:
sumber
Varian dari solusi oleh @Dudu Markovitz, yang juga berfungsi dengan versi PostgreSQL yang belum (belum) mengenali [i:]:
sumber