Pendekatan yang lebih baik untuk "SUKA ATAU SEPERTI, ATAU SEPERTI, ATAU SEPERTI, ATAU SEPERTI"

10

Dalam pertanyaan ini dia memiliki masalah yang sama dengan saya. Saya butuh sesuatu seperti:

select * from blablabla 
where product 
like '%rock%' or
like '%paper%' or
like '%scisor%' or
like '%car%' or
like '%pasta%' 

Ini jelek dan tidak menggunakan indeks .. Dalam hal ini, ini benar-benar satu-satunya cara untuk melakukan ini (untuk memilih beberapa kata di dalam string), atau haruskah saya menggunakan FULLTEXT?

Seperti yang saya mengerti, dengan fulltext, saya dapat memilih beberapa kata di dalam sebuah string.

Pertanyaan ini juga berbicara tentang Teks Lengkap

SQL pembalap
sumber
3
Apa tipe data dari kolom produk? Berapa banyak karakter rata-rata?
Joe Obbish

Jawaban:

17

Indeks teks lengkap umumnya bukan peluru ajaib, dan membutuhkan perawatan tambahan, ruang disk, dan perubahan yang cukup mengganggu untuk pola kueri.

Kecuali Anda benar-benar membutuhkan pengindeksan dokumen besar (pikirkan badan email, PDF, dokumen Word, dll.), Mereka berlebihan (dan jika kita jujur, saya akan mengambil proses itu dari SQL Server sepenuhnya dan gunakan Elasticsearch atau yang serupa).

Untuk kasus penggunaan yang lebih kecil, kolom yang dikomputasi umumnya merupakan pendekatan yang lebih baik.

Berikut ini adalah pengaturan demo cepat:

use tempdb

CREATE TABLE #fulltextindexesarestupid (Id INT PRIMARY KEY CLUSTERED, StopAbusingFeatures VARCHAR(100))

INSERT #fulltextindexesarestupid (Id)
SELECT TOP 1000000 ROW_NUMBER() OVER (ORDER BY (@@ROWCOUNT))
FROM sys.messages AS m
CROSS JOIN sys.messages AS m2

UPDATE #fulltextindexesarestupid
SET StopAbusingFeatures = CASE WHEN Id % 15 = 0 THEN 'Bad'
                               WHEN Id % 3 = 0 THEN 'Idea'
                               WHEN Id % 5 = 0 THEN 'Jeans'
                               END


ALTER TABLE #fulltextindexesarestupid 
ADD LessBad AS CONVERT(BIT, CASE WHEN StopAbusingFeatures LIKE '%Bad%' THEN 1
                    WHEN StopAbusingFeatures LIKE '%Idea%' THEN 1
                    ELSE 0 END)

CREATE UNIQUE NONCLUSTERED INDEX ix_whatever ON #fulltextindexesarestupid (LessBad, Id)

Permintaan berdasarkan bahkan pada kolom non-persisten memberi kita rencana yang 'menggunakan indeks' dan semuanya :)

SELECT COUNT(*)
FROM #fulltextindexesarestupid AS f
WHERE LessBad = 1

GILA

Erik Darling
sumber
-3

Jawaban sp_BlitzErik menyentuh banyak poin bagus, tapi saya pikir itu sebabnya Anda tidak boleh menggunakan Pencarian Teks Lengkap. Pencarian teks lengkap tidak ada untuk melakukan apa yang Anda pikirkan. Itu tidak ada di sana untuk mencari beberapa bidang. Itu ada di sana untuk membuat vektor kata konten dan memanfaatkan kamus, stubbing, lexers, gazetteers, stop-word eliminasi, dan banyak trik lain yang tidak ada yang berlaku. Atau, belum terbukti berlaku.

Saya juga tidak setuju dengan solusinya, meskipun saya tidak yakin bagaimana melakukan ini dengan lebih baik di SQL Server. Mari kita buat ulang datanya untuk PostgreSQL - ini juga jauh lebih bersih untuk dibuat di dalam PostgreSQL.

CREATE TABLE fulltextindexesarestupid
AS
  SELECT
    id,
    CASE WHEN Id % 15 = 0 THEN 'Bad'
      WHEN Id % 3 = 0 THEN 'Idea'
      WHEN Id % 5 = 0 THEN 'Jeans'
    END AS StopAbusingFeatures
  FROM generate_series(1,1000000) AS id;

Sekarang apa yang Anda inginkan adalah tipe enum,

CREATE TYPE foo AS ENUM ('Bad', 'Idea', 'Jeans');

ALTER TABLE fulltextindexesarestupid
  ALTER StopAbusingFeatures
  SET DATA TYPE foo
  USING StopAbusingFeatures::foo;

Sekarang Anda telah menciutkan string ke representasi integer. Tetapi bahkan lebih baik Anda dapat meminta mereka seperti sebelumnya.

SELECT *
FROM fulltextindexesarestupid
WHERE StopAbusingFeatures = 'Bad';

Ini memiliki efek.

  1. menyembunyikan fakta bahwa Anda kategori adalah tipe yang disebutkan. Kompleksitas itu dirangkum dalam tipe dan disembunyikan dari pengguna.
  2. itu juga menempatkan pemeliharaan pada kategori tersebut pada tipe.
  3. itu standar.
  4. itu tidak menumbuhkan ukuran baris.

Tanpa manfaat ini, Anda pada dasarnya hanya mencoba mengoptimalkan perbandingan string. Tapi sayangnya, saya bahkan tidak yakin bagaimana mendapatkan sp_BlitzErik untuk jawaban yang diberikan kode dalam saran,

like '%rock%' or
like '%paper%' or
like '%scisor%' or
like '%car%' or
like '%pasta%'

Anda dapat menutup token ke bilangan bulat menggunakan enum, atau metode linting tangan yang disarankan oleh sp_BlitzErik tetapi jika Anda dapat melakukan collapsing, mengapa Anda juga melakukan hal yang tidak-seperti? Yaitu, Jika Anda tahu '% pasta%' adalah token 'pasta' mengapa Anda memiliki %kedua sisinya. Tanpa '%' ini pemeriksaan kesetaraan dan seharusnya cukup cepat bahkan sebagai teks.

Evan Carroll
sumber