Lambat permintaan ketika memiliki 'mengandung' dan '=' bersama di mana klausa

8

Permintaan berikut membutuhkan waktu sekitar 10 detik untuk diselesaikan di atas meja dengan catatan 12k

select top (5) *
from "Physician"
where "id" = 1 or contains("lastName", '"a*"')

Tetapi jika saya mengubah klausa di mana juga

where "id" = 1

atau

where contains("lastName", '"a*"')

Itu akan kembali secara instan.

Kedua kolom diindeks dan kolom lastName juga teks lengkap diindeks.

CREATE TABLE Physician
(
   id         int identity    NOT NULL,
   firstName  nvarchar(100)   NOT NULL,
   lastName   nvarchar(100)   NOT NULL
);

ALTER TABLE Physician
  ADD CONSTRAINT Physician_PK
  PRIMARY KEY CLUSTERED (id);

CREATE NONCLUSTERED INDEX Physician_IX2
   ON Physician (firstName ASC);

CREATE NONCLUSTERED INDEX Physician_IX3
   ON Physician (lastName ASC);

CREATE FULLTEXT INDEX
    ON "Physician" ("firstName" LANGUAGE 0x0, "lastName" LANGUAGE 0x0)
    KEY INDEX "Physician_PK"
    ON "the_catalog"
    WITH stoplist = off;

Berikut adalah Rencana Eksekusi

Apa yang bisa menjadi masalah?

Hooman Valibeigi
sumber
Saya baru saja menambahkan definisi tabel
Hooman Valibeigi

Jawaban:

11

Rencana eksekusi Anda

Saat melihat rencana kueri, kita dapat melihat bahwa satu indeks disentuh untuk melayani dua operasi filter.

masukkan deskripsi gambar di sini

Sederhananya, karena operator TOP, tujuan baris ditetapkan. Lebih banyak informasi & prasyarat tujuan baris dapat ditemukan di sini

Dari sumber yang sama:

Strategi sasaran baris pada umumnya berarti mendukung operasi navigasi yang tidak menghalangi (misalnya, sambungan loop bersarang, indeks pencarian, dan pencarian) lebih dari memblokir, operasi berbasis set seperti menyortir dan hashing. Ini bisa berguna kapan saja klien dapat memperoleh manfaat dari start-up yang cepat dan aliran yang stabil (dengan mungkin waktu eksekusi keseluruhan yang lebih panjang - lihat posting Rob Farley di atas). Ada juga penggunaan yang lebih jelas dan tradisional misalnya dalam menyajikan hasil halaman pada suatu waktu.

Seluruh tabel akan diselidiki ke dalam filter dengan menggunakan setengah bergabung kiri yang memiliki sasaran baris, berharap untuk mengembalikan 5 baris secepat dan seefisien mungkin.

Ini tidak terjadi, menghasilkan banyak iterasi atas .Fulltextmatch TVF.

masukkan deskripsi gambar di sini


Rekreasi

Berdasarkan rencana Anda , saya dapat membuat kembali masalah Anda:

CREATE TABLE dbo.Person(id int not null,lastname varchar(max));

CREATE UNIQUE INDEX ui_id ON  dbo.Person(id)
CREATE FULLTEXT CATALOG ft AS DEFAULT;  
CREATE FULLTEXT INDEX ON dbo.Person(lastname)   
   KEY INDEX ui_id   
   WITH STOPLIST = SYSTEM;  
GO  

INSERT INTO dbo.Person(id,lastname)
SELECT top(12000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)),
REPLICATE(CAST('A' as nvarchar(max)),80000)+ CAST(ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as varchar(10))
FROM master..spt_values spt1
CROSS APPLY master..spt_values spt2;
CREATE CLUSTERED INDEX cx_Id on dbo.Person(id);

Menjalankan kueri

SELECT TOP (5) *
FROM dbo.Person
WHERE "id" = 1 OR contains("lastName", '"B*"');

Hasil menjadi paket permintaan yang sebanding dengan milik Anda:

masukkan deskripsi gambar di sini

Dalam contoh di atas, B tidak ada dalam indeks teks lengkap. Sebagai hasilnya tergantung pada parameter & data seberapa efisiennya rencana kueri.

Penjelasan yang lebih baik tentang ini dapat ditemukan di Row Goals, Bagian 2: Semi Joins oleh Paul White

... Dengan kata lain, pada setiap iterasi dari suatu aplikasi, kita dapat berhenti melihat input B segera setelah kecocokan pertama ditemukan, menggunakan predikat join push-down. Ini adalah hal yang tepat untuk tujuan baris: menghasilkan bagian dari rencana yang dioptimalkan untuk mengembalikan n baris pencocokan pertama dengan cepat (di mana n = 1 di sini).

Misalnya, mengubah predikat sehingga hasilnya lebih cepat ditemukan (di awal pemindaian).

select top (5) *
from dbo.Person
where "id" = 124 
or contains("lastName", '"A*"');

masukkan deskripsi gambar di sini

yang where "id" = 124akan dihilangkan karena indeks predikat fulltext sudah kembali 5 baris, memuaskan TOP()predikat.

Hasilnya menunjukkan ini juga

id lastname 
1  'AAA...'   
2  'AAA...'
3  'AAA...'
4  'AAA...'
5  'AAA...'

Dan eksekusi TVF:

masukkan deskripsi gambar di sini

Memasukkan beberapa baris baru

INSERT INTO dbo.Person
SELECT 12001, REPLICATE(CAST('B' as nvarchar(max)),80000);
INSERT INTO dbo.Person
SELECT 12002, REPLICATE(CAST('B' as nvarchar(max)),80000);

Menjalankan kueri untuk menemukan baris yang dimasukkan sebelumnya ini

SELECT TOP (2) *
from dbo.Person
where "id" = 1
or contains("lastName", '"B*"');

Sekali lagi ini menghasilkan terlalu banyak iterasi pada hampir semua baris untuk mengembalikan nilai terakhir tetapi satu yang ditemukan.

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

id   lastname
1     'AAA...'
12001 'BBB...'

Menyelesaikan

Saat menghapus sasaran baris dengan menggunakan traceflag 4138

SELECT TOP (5) *
FROM dbo.Person
WHERE "id" = 124 
OR contains("lastName", '"B*"')
OPTION(QUERYTRACEON 4138 );

Pengoptimal menggunakan pola bergabung lebih dekat ke menerapkan UNION, dalam kasus kami ini menguntungkan karena mendorong predikat ke masing-masing indeks pencarian berkerumun, dan tidak menggunakan baris yang menyatu dengan operator setengah bergabung kiri.

masukkan deskripsi gambar di sini

Cara lain untuk menulis ini, tanpa menggunakan traceflag yang disebutkan di atas:

SELECT top (5) *
FROM
(
SELECT * 
FROM dbo.Person
WHERE "id" = 1 
UNION
SELECT * 
FROM dbo.Person
WHERE contains("lastName", '"B*"')
 ) as A;

Dengan rencana kueri yang dihasilkan:

masukkan deskripsi gambar di sini

di mana fungsi teks lengkap diterapkan secara langsung

masukkan deskripsi gambar di sini

Sebagai sidenote, untuk op, kueri hotfix optimizer traceflag 4199 menyelesaikan masalahnya. Dia menerapkan ini dengan menambahkan OPTION(QUERYTRACEON(4199))kueri. Saya tidak dapat mereproduksi perilaku itu di pihak saya. Perbaikan terbaru ini mengandung pengoptimalan setengah bergabung:

Bendera Jejak: 4102 Fungsi: SQL 9 - Kinerja kueri lambat jika rencana pelaksanaan kueri berisi operator semi join. Biasanya, operator semi join dihasilkan ketika kueri berisi kata kunci IN atau kata kunci EXISTS. Aktifkan flag 4102 dan 4118 untuk mengatasinya.

Sumber


Tambahan

Selama pengoptimalan berbasis biaya, pengoptimal juga dapat menambahkan spool indeks ke rencana eksekusi, dilaksanakan oleh LogOp_Spool Index on fly Eager (atau mitra fisik)

Ini dilakukan dengan dataset saya untuk TOP(3)tetapi tidak untukTOP(2)

SELECT TOP (3) *
from dbo.Physician
where "id" = 1
or contains("lastName", '"B*"')  

masukkan deskripsi gambar di sini

Pada eksekusi pertama, spool yang bersemangat membaca dan menyimpan seluruh input sebelum mengembalikan subset baris yang diminta oleh eksekusi Predicate Later membaca dan mengembalikan subset baris yang sama atau berbeda dari meja kerja, tanpa harus mengeksekusi anak node lagi.

Sumber

Dengan predikat pencarian yang diterapkan pada spool eager indeks ini:

masukkan deskripsi gambar di sini

Randi Vertongen
sumber
Bisakah Anda menjelaskan penggunaan Bendera Jejak? Agak tidak jelas dari kode Anda apa yang mereka lakukan
George.Palacios
1
@ George.Palacios Ya saya membuat sedikit kekacauan dari semuanya: ^). Akan lakukan terima kasih atas umpan baliknya!
Randi Vertongen
Bukan bendera QUERYTRACEON yang Anda sarankan (4138, 3604, 8607, 8612) yang berfungsi, tetapi QUERYTRACEON 4199 akan memperbaiki masalah !!!!
Hooman Valibeigi
Perhatikan bahwa permintaannya lambat bahkan tanpa operator TOP
Hooman Valibeigi
@HoomanValibeigi apakah Anda mencoba solusi serikat di bagian bawah?
Randi Vertongen