Petunjuk NOLOCK mengubah urutan catatan yang dikembalikan

11

Ada indeks berkerumun di Clientbidang tabel LastName.

Ketika saya cukup membuang semua catatan dari tabel, mereka muncul dalam urutan abjad kecuali (nolock)petunjuk digunakan seperti dalam kueri yang dimaksud. Petunjuk itu mengubah urutan catatan. Haruskah itu? Saya yakin tidak ada sesi lain yang memiliki transaksi terbuka dengan perubahan pada tabel tersebut (setidaknya sp_who2tidak menunjukkan kepada saya apa pun).

Bagaimana perbedaan urutan dijelaskan?

Informasi tambahan diambil dari komentar:

  1. Tidak ada pesanan oleh. Haruskah indeks yang tidak berkerumun menegakkan pesanan?

  2. Kueri masih mengembalikan urutan yang berbeda bahkan saat menggunakan petunjuk indeks yang menentukan indeks yang dikelompokkan. Haruskah mereka Saya bertanya-tanya mengapa nolockmengubah urutan catatan yang dikembalikan tanpa perubahan rencana yang terlihat.

  3. Saya melakukan WinDiff pada mereka - sama kecuali untuk [ (nolock)petunjuk].

ajeh
sumber
21
Petunjuk tidak mengubah urutan - karena tidak ada urutan yang dapat diubah
a_horse_with_no_name

Jawaban:

47

Munculnya set hasil yang dipesan, tanpa ORDER BYklausa, seringkali merupakan hasil dari pemindaian baris pencarian dalam urutan indeks. Salah satu alasan mengapa pemindaian indeks-urutan umumnya dipilih di bawah READ COMMITTEDtingkat isolasi default adalah bahwa hal itu mengurangi kemungkinan anomali konkurensi yang tidak diinginkan seperti menemukan baris yang sama beberapa kali, atau sepenuhnya melewatkan beberapa baris. Ini dirinci di beberapa tempat, termasuk dalam seri artikel ini tentang tingkat isolasi.

Dengan NOLOCKpetunjuk tabel, perilaku ini santai, dan akses ke tabel dilakukan di bawah READ UNCOMMITTEDtingkat isolasi yang lebih toleran , yang dapat memindai data dalam urutan alokasi alih-alih urutan indeks. Seperti yang dijelaskan dalam tautan itu, keputusan tentang apakah akan menggunakan pemindaian alokasi atau urutan indeks diserahkan ke mesin penyimpanan. Pilihan ini dapat berubah di antara eksekusi tanpa perubahan dalam rencana kueri .

Ini mungkin terdengar sangat abstrak, tetapi dapat lebih mudah ditunjukkan dengan beberapa permintaan menggunakan fungsi tidak berdokumen terhadap database AdventureWorks2012 .

USE AdventureWorks2012;
GO
-- Appears to be ordered by BusinessEntityID
-- File:Page:Slot goes up and down several times
-- Show physical locations with sys.fn_PhysLocFormatter (undocumented)
SELECT
    P.BusinessEntityID,
    [(File:Page:Slot)] =
        sys.fn_PhysLocFormatter(%%physloc%%)
FROM Person.Person AS P;

-- Same query with TABLOCK or NOLOCK
-- Allocation-order (IAM) scan
-- Now appears to be ordered by File:Page:Slot instead of BusinessEntityID
SELECT P.BusinessEntityID,
    [(File:Page:Slot)] =
        sys.fn_PhysLocFormatter(%%physloc%%)
FROM Person.Person AS P WITH (NOLOCK);

Hasil Permintaan

Pertanyaan dipinjam dengan sedikit modifikasi dari Paul White .

Akhirnya, hanya untuk menjadi jelas, jawaban ini adalah tentang penampilan set hasil yang dipesan. Tidak ada jaminan presentasi tanpa tingkat atas ORDER BY.

Pemindaian urutan alokasi dapat terjadi dalam berbagai keadaan lain, seperti ketika kunci tingkat tabel diperoleh, atau database dalam mode read-only. Paralelisme juga dapat memengaruhi urutan data yang dikembalikan. Poin utamanya adalah bahwa tanpa ORDER BY, urutan data yang dikembalikan dapat bervariasi dari waktu ke waktu menurut desain.

James L.
sumber
7
Bagus, jauh lebih spesifik daripada upaya saya yang tidak dihargai. Intinya adalah dalam dua paragraf terakhir Anda, tentu saja. "Kenapa" pada akhirnya tidak terlalu penting. Mungkin Anda akan memiliki keberuntungan yang lebih baik untuk memukul akord itu daripada saya.
Aaron Bertrand
12

Ketika saya hanya membuang semua catatan dari tabel

... maka Anda seharusnya tidak mengharapkan pesanan apa pun. Bahkan kueri yang sama berjalan beberapa kali bisa kembali dalam urutan berbeda tanpa peringatan. Alasannya adalah bahwa dua kueri Anda - yang merupakan kueri "berbeda" kemungkinan besar karena teks kueri yang berbeda, bukan karena petunjuknya - memiliki rencana eksekusi yang berbeda (dan perbedaannya mungkin tidak kentara, seperti iterator yang terlihat sama di keduanya rencana, tetapi ordered: truehanya memiliki satu, atau jumlah baris perkiraan yang sangat berbeda karena satu dikompilasi sebelum perubahan data utama). Bisa juga pemindaian urutan alokasi sedang dilakukan, seperti yang dijabarkan James dengan tepat dalam jawabannya.

Dalam kasus apa pun, karena Anda menjalankan kueri tanpa ORDER BY, SQL Server menyimpulkan bahwa Anda tidak peduli tentang pesanan, sehingga berbunyi dan menentukan cara paling efisien untuk mengembalikan baris (tidak peduli tentang pesanan jika Anda tidak ).

Biarkan saya memperjelas hal ini:

Jika Anda menginginkan atau mengharapkan pesanan tertentu, tambahkan klausa ORDER BY

Ini telah muncul sebelumnya:

Dan saya sudah membuat blog tentang itu:

Berikut adalah kutipan dari yang terakhir yang mengulangi apa yang saya katakan untuk menanggapi komentar Anda tentang menggunakan petunjuk indeks alih-alih ORDER BYklausa:

Bahkan dalam kasus indeks yang diisyaratkan, indeks akan digunakan (jika mungkin, atau kesalahan dalam kebanyakan kasus), tetapi hanya karena indeks yang digunakan tidak berarti hasilnya akan dikembalikan diurutkan dengan kunci di indeks itu. Anda masih perlu ORDER BYmemastikan penyortiran menurut kunci indeks.

Conor Cunningham - seorang pria yang cukup pintar, bertanggung jawab langsung atas sebagian besar yang dilakukan SQL Server ketika memproses kueri untuk Anda - juga membuat blog tentang hal itu di sini:

Silakan membaca dan menambahkan ORDER BYklausa ke pertanyaan Anda sebelum mengeluh tentang penyortiran yang berbeda atau tidak terduga.

Aaron Bertrand
sumber
11

James dengan baik menjelaskan bagaimana ini bekerja, tetapi saya hanya ingin mengulangi satu hal: kecuali jika Anda menggunakan fungsi pemesanan, urutan baris dalam set hasil tidak ditentukan . Jika Anda membutuhkan pemesanan tertentu, gunakan order byklausa eksplisit - jika Anda tidak menentukannya, Anda pada dasarnya mengatakan "Saya sama sekali tidak peduli dengan pesanan", bukan "Memesannya dengan indeks berkerumun".

Luaan
sumber