Mengapa orang sangat membenci kursor SQL? [tertutup]

Saya dapat memahami keinginan untuk menghindari penggunaan kursor karena overhead dan ketidaknyamanan, tetapi sepertinya ada beberapa fobia-kursor serius yang terjadi di mana orang-orang berusaha keras untuk menghindari penggunaan kursor.

Misalnya, satu pertanyaan menanyakan bagaimana melakukan sesuatu yang jelas-jelas sepele dengan kursor dan jawaban yang diterima diajukan menggunakan kueri rekursif ekspresi tabel umum (CTE) dengan fungsi kustom rekursif, meskipun ini membatasi jumlah baris yang dapat diproses hingga 32 (karena batas panggilan fungsi rekursif di server sql). Menurut saya ini adalah solusi yang buruk untuk umur panjang sistem, belum lagi upaya yang luar biasa hanya untuk menghindari penggunaan kursor sederhana.

Apa alasan dari tingkat kebencian yang gila ini? Apakah ada 'otoritas terkenal' yang mengeluarkan fatwa yang melarang kursor? Apakah ada kejahatan yang tak terkatakan mengintai di hati kursor yang merusak moral anak-anak atau semacamnya?

Pertanyaan Wiki, lebih tertarik pada jawabannya daripada perwakilannya.

Informasi Terkait:

Kursor Maju Cepat SQL Server

EDIT: izinkan saya lebih tepatnya: Saya memahami bahwa kursor tidak boleh digunakan sebagai pengganti operasi relasional normal; itu tidak perlu dipikirkan lagi. Apa yang saya tidak mengerti adalah orang-orang berusaha keras menghindari kursor seolah-olah mereka punya cooties atau semacamnya, bahkan ketika kursor adalah solusi yang lebih sederhana dan/atau lebih efisien. Yang membuat saya bingung adalah kebencian yang tidak masuk akal, bukan efisiensi teknis yang terlihat jelas.


person Community    schedule 13.11.2008    source sumber
comment
Saya pikir Editan Anda menjelaskan semuanya... Di hampir semua situasi (yang saya temui) ada cara untuk mengganti kursor dengan situasi berbasis kumpulan yang berkinerja lebih baik. Anda mengatakan tidak perlu khawatir, tetapi Anda memahami perbedaannya.   -  person StingyJack    schedule 13.11.2008
comment
Saya suka tag pada pertanyaan ini!   -  person sep332    schedule 14.11.2008
comment
Bagian tentang batas CTE rekursif menjadi 32 adalah omong kosong. Mungkin Anda memikirkan pemicu rekursif dan maksimal @@NESTLEVEL dari 32. Itu dapat diatur dalam kueri dengan OPTION (MAXRECURSION N) dengan default 100 dan 0 artinya tidak terbatas.   -  person Martin Smith    schedule 02.11.2012
comment
@MartinSmith: batas default sekarang adalah 100, dan maksimumnya adalah 32K sql-server-helper.com/error-messages/msg-310.aspx   -  person Steven A. Lowe    schedule 12.01.2016
comment
Tidak, ini masih sama persis seperti ketika saya membuat komentar dan di semua versi SQL Server yang mendukung CTE rekursif. Seperti yang dikatakan tautan Anda Ketika 0 ditentukan, tidak ada batasan yang diterapkan.   -  person Martin Smith    schedule 12.01.2016
comment
@MartinSmith: terima kasih, kesalahan saya - sebenarnya ada dua kesalahan;) yang pertama salah membaca referensi (saya berasumsi 32K limit = 'unlimited') dan yang kedua adalah penyebab yang salah - dalam contoh yang dikutip, batas rekursi 32 berasal fungsi rekursif, bukan CTE. Saya mungkin menggunakan SQL Server 2000, atau mungkin 2008, semoga sekarang lebih baik :). Pertanyaan diedit untuk memperjelas - hargai koreksi Anda!   -  person Steven A. Lowe    schedule 12.01.2016


Jawaban (13)


"Overhead" dengan kursor hanyalah bagian dari API. Kursor adalah cara kerja bagian RDBMS. Seringkali CREATE TABLE dan INSERT memiliki pernyataan SELECT, dan implementasinya adalah implementasi kursor internal yang jelas.

Menggunakan "operator berbasis set" tingkat yang lebih tinggi menggabungkan hasil kursor ke dalam satu set hasil, yang berarti lebih sedikit API yang bolak-balik.

Kursor sudah ada sebelum bahasa modern yang menyediakan koleksi kelas satu. C lama, COBOL, Fortran, dll., harus memproses baris satu per satu karena tidak ada pengertian "koleksi" yang dapat digunakan secara luas. Java, C#, Python, dll., memiliki struktur daftar kelas satu untuk menampung kumpulan hasil.

Masalah Lambat

Di beberapa kalangan, gabungan relasional adalah sebuah misteri, dan orang-orang akan menulis kursor bersarang daripada gabungan sederhana. Saya telah melihat operasi loop bersarang yang benar-benar epik ditulis sebagai banyak sekali kursor. Mengalahkan optimasi RDBMS. Dan berjalan sangat lambat.

Penulisan ulang SQL sederhana untuk menggantikan loop kursor bersarang dengan gabungan dan satu loop kursor datar dapat membuat program berjalan ke-100 kalinya. [Mereka mengira saya adalah dewa pengoptimalan. Yang saya lakukan hanyalah mengganti loop bersarang dengan gabungan. Masih menggunakan kursor.]

Kebingungan ini seringkali berujung pada tuduhan terhadap kursor. Namun, bukan kursornya, melainkan penyalahgunaan kursor itulah masalahnya.

Masalah Ukuran

Untuk kumpulan hasil yang benar-benar epik (yaitu, membuang tabel ke file), kursor sangat penting. Operasi berbasis kumpulan tidak dapat mewujudkan kumpulan hasil yang sangat besar sebagai satu kumpulan di memori.

Alternatif

Saya mencoba menggunakan lapisan ORM sebanyak mungkin. Tapi itu memiliki dua tujuan. Pertama, kursor dikelola oleh komponen ORM. Kedua, SQL dipisahkan dari aplikasi ke dalam file konfigurasi. Bukan berarti kursornya buruk. Pengkodean semua pembukaan, penutupan, dan pengambilan bukanlah pemrograman yang bernilai tambah.

person Community    schedule 13.11.2008
comment
Kursor adalah cara kerja RDBMS. Jika yang Anda maksud secara spesifik adalah SQL Server, oke, baiklah, saya tidak mengetahuinya. Tapi saya telah mengerjakan internal beberapa RDBMS (dan ORDBMS) (di bawah Stonebraker) dan tidak ada satupun yang melakukan itu. Misalnya: Ingres menggunakan jumlah hasil kumpulan tupel secara internal. - person Richard T; 28.12.2008
comment
@Richard T: Saya sedang mengerjakan informasi bekas tentang sumber RDBMS; Saya akan mengubah pernyataan itu. - person S.Lott; 28.12.2008
comment
Saya telah melihat operasi loop bersarang yang benar-benar epik ditulis sebagai banyak sekali kursor. Saya juga terus bertemu mereka. Sulit dipercaya. - person RussellH; 30.12.2008

Kursor membuat orang terlalu menerapkan pola pikir prosedural pada lingkungan berbasis himpunan.

Dan semuanya LAMBAT!!!

Dari SQLTeam:

Harap dicatat bahwa kursor adalah cara paling lambat untuk mengakses data di dalam SQL Server. Ini hanya boleh digunakan ketika Anda benar-benar perlu mengakses satu baris dalam satu waktu. Satu-satunya alasan yang dapat saya pikirkan adalah memanggil prosedur tersimpan di setiap baris. Dalam artikel Kinerja Kursor saya menemukan bahwa kursor lebih dari tiga puluh kali lebih lambat daripada yang disetel alternatif berbasis.

person Community    schedule 13.11.2008
comment
artikel itu sudah berumur 7 tahun, apakah menurut Anda mungkin ada banyak hal yang berubah untuk sementara waktu? - person Steven A. Lowe; 13.11.2008
comment
Menurut saya, kursor sangat lambat dan secara umum harus dihindari. Namun, jika OP mengacu pada pertanyaan yang saya kira, maka kursor adalah solusi yang tepat di sana (streaming catatan satu per satu karena keterbatasan memori). - person rmeador; 13.11.2008
comment
artikel yang diperbarui tidak mengoreksi pengukuran kecepatan relatif, namun memberikan beberapa pengoptimalan dan alternatif yang baik. Perhatikan bahwa artikel asli mengatakan bahwa kursor 50 kali lebih cepat daripada loop while, dan ini menarik - person Steven A. Lowe; 13.11.2008
comment
Menurut saya pribadi, jika Anda memerlukan kursor, Anda belum mendesain database dengan benar. - person BoltBait; 13.11.2008
comment
@BoltBait: Saya pribadi berpikir jika Anda membuat pernyataan menyeluruh seperti itu, Anda tidak mungkin berusia 45 tahun :-P - person Steven A. Lowe; 14.11.2008
comment
Kursor tidak terlalu lambat di Oracle, di mana Anda dapat menggunakan klausa seperti BULK-COLLECT untuk kode berdasarkan kinerja. Akan menyenangkan untuk melakukan benchmark. - person Camilo Díaz Repka; 14.11.2008
comment
@Steven: Ya, saya sudah tua... dan sangat keras kepala! - person BoltBait; 17.11.2008
comment
@BoltBait: Kalian keluar dari halaman rumputku! - person Steven A. Lowe; 20.11.2008
comment
Saya rasa itu adalah kutipan Edwin Dijkstra: optimasi prematur adalah akar segala kejahatan... jadi terkadang saya berpikir, persetan dengan argumen kinerja. Gunakan himpunan di mana himpunan harus diterapkan juga dalam logika agar algoritme Anda dapat dimengerti. Namun terkadang logika bisnis bisa menjadi jauh lebih jelas dan lebih mudah beradaptasi jika dilakukan secara berurutan. Ya, itu pendapat saya, tentu saja. - person Paul; 14.02.2014
comment
@Paul: itu kutipan Knuth, dan itu Edgar Dijkstra, tapi saya setuju dengan sisa kalimat Anda :) - person Steven A. Lowe; 12.01.2016
comment
Lucunya menurut saya logika bisnis hampir selalu lebih jelas dalam kode berbasis set. - person HLGEM; 21.08.2017

Ada jawaban di atas yang mengatakan "kursor adalah cara paling lambat untuk mengakses data di dalam SQL Server... kursor lebih dari tiga puluh kali lebih lambat daripada alternatif berbasis set."

Pernyataan ini mungkin benar dalam banyak keadaan, namun sebagai pernyataan menyeluruh, pernyataan ini bermasalah. Misalnya, saya telah memanfaatkan kursor dengan baik dalam situasi di mana saya ingin melakukan operasi pembaruan atau penghapusan yang memengaruhi banyak baris tabel besar yang menerima pembacaan produksi konstan. Menjalankan prosedur tersimpan yang melakukan pembaruan ini satu per satu baris akan menjadi lebih cepat daripada operasi berbasis set, karena operasi berbasis set bertentangan dengan operasi baca dan akhirnya menyebabkan masalah penguncian yang parah (dan dapat mematikan sistem produksi sepenuhnya, dalam kasus ekstrim).

Dengan tidak adanya aktivitas database lainnya, operasi berbasis set secara umum lebih cepat. Dalam sistem produksi, itu tergantung.

person Community    schedule 13.11.2008
comment
Kedengarannya seperti pengecualian yang membuktikan aturan tersebut. - person Joel Coehoorn; 13.11.2008
comment
@[Joel Coehoorn]: Saya tidak pernah mengerti perkataan itu. - person Steven A. Lowe; 13.11.2008
comment
@[Steven A. Lowe] phrases.org.uk /meanings/Exception-that-proves-the-rule.html memahami pengecualian sebagai apa yang ditinggalkan dan perhatikan bahwa aturan di sini adalah sesuatu seperti di sebagian besar situasi, kursor buruk. - person David Lay; 13.11.2008
comment
@delm: terima kasih atas tautannya, sekarang saya semakin kurang memahami frasa tersebut! - person Steven A. Lowe; 13.11.2008
comment
@[Steven A. Lowe] Pada dasarnya dikatakan bahwa jika Anda melanggar aturan dengan subkasus, harus ada aturan umum yang dilanggar, sehingga aturan itu ada. misalnya Dari Tautan: (Jika kami memiliki pernyataan seperti 'tiket masuk gratis pada hari Minggu', kami dapat berasumsi bahwa, sebagai aturan umum, tiket masuk dikenakan biaya.) - person Fry; 13.11.2008
comment
@Fry: oke itu masuk akal - jadi bagaimana penerapannya di sini? - person Steven A. Lowe; 13.11.2008
comment
Saya pikir dia mengatakan bahwa ketika ada masalah penguncian/keterbatasan memori gunakan kursor. Ini menyiratkan bahwa Anda tidak boleh menggunakan kursor sebaliknya. - person SapphireSun; 27.02.2010
comment
Contoh lain penggunaan kursor yang sesuai: support.microsoft.com/kb/973849 . Versi aslinya menggunakan teknik berbasis set dan memiliki masalah penguncian. - person Moe Sisko; 02.07.2013
comment
Dan yang menarik, jenis kumpulan data yang mereka bicarakan (data status sesi dari aplikasi web) benar-benar memenuhi kriteria yang saya sebutkan di postingan asli-- kumpulan data di mana banyak baris dipengaruhi oleh suatu operasi, tetapi tabel itu sendiri menerima pembacaan produksi yang konstan. Versi aslinya akan menjadi kematian bagi situs dengan volume lalu lintas yang besar. - person davidcl; 16.07.2013

Kursor cenderung digunakan oleh pengembang SQL pemula di tempat di mana operasi berbasis set akan lebih baik. Khususnya ketika orang mempelajari SQL setelah mempelajari bahasa pemrograman tradisional, mentalitas "mengulangi catatan-catatan ini" cenderung membuat orang menggunakan kursor secara tidak tepat.

Kebanyakan buku SQL yang serius menyertakan bab yang memerintahkan penggunaan kursor; yang ditulis dengan baik memperjelas bahwa kursor memiliki tempatnya tetapi tidak boleh digunakan untuk operasi berbasis set.

Jelas ada situasi di mana kursor adalah pilihan yang tepat, atau setidaknya pilihan yang benar.

person Community    schedule 13.11.2008

Pengoptimal sering kali tidak dapat menggunakan aljabar relasional untuk mengubah masalah ketika metode kursor digunakan. Seringkali kursor adalah cara yang bagus untuk menyelesaikan suatu masalah, namun SQL adalah bahasa deklaratif, dan terdapat banyak informasi dalam database, mulai dari batasan, hingga statistik dan indeks yang berarti bahwa pengoptimal memiliki banyak opsi untuk menyelesaikan masalah. masalah, sedangkan kursor secara eksplisit mengarahkan solusinya.

person Community    schedule 13.11.2008

Di Oracle PL/SQL, kursor tidak akan menghasilkan kunci tabel dan dimungkinkan untuk menggunakan pengumpulan massal/pengambilan massal.

Di Oracle 10 kursor implisit yang sering digunakan

  for x in (select ....) loop
    --do something 
  end loop;

mengambil secara implisit 100 baris sekaligus. Pengumpulan massal/pengambilan massal secara eksplisit juga dimungkinkan.

Namun kursor PL/SQL adalah pilihan terakhir, gunakan kursor tersebut ketika Anda tidak dapat menyelesaikan masalah dengan SQL berbasis set.

Alasan lainnya adalah paralelisasi, lebih mudah bagi database untuk memparalelkan pernyataan berbasis himpunan besar daripada kode imperatif baris demi baris. Ini adalah alasan yang sama mengapa pemrograman fungsional menjadi semakin populer (Haskell, F#, Lisp, C# LINQ, MapReduce ...), pemrograman fungsional membuat paralelisasi lebih mudah. Jumlah CPU per komputer meningkat sehingga paralelisasi menjadi semakin menjadi masalah.

person Community    schedule 10.01.2009

Secara umum, karena pada database relasional, performa kode yang menggunakan kursor jauh lebih buruk daripada operasi berbasis set.

person Community    schedule 13.11.2008
comment
apakah anda punya patokan atau referensi untuk ini? saya belum melihat adanya penurunan kinerja drastis seperti itu... tapi mungkin tabel saya tidak memiliki cukup baris untuk menjadi penting (biasanya satu juta atau kurang)? - person Steven A. Lowe; 13.11.2008
comment
oh tunggu, saya mengerti maksud Anda - tetapi saya tidak akan pernah menganjurkan penggunaan kursor alih-alih operasi yang ditetapkan, hanya saja tidak berlebihan untuk menghindari kursor - person Steven A. Lowe; 13.11.2008
comment
Saya ingat pertama kali saya melakukan SQL, Kami harus mengimpor file data harian 50k dari mainframe ke database SQL Server... Saya menggunakan kursor, dan menemukan, bahwa impor memakan waktu sekitar 26 jam menggunakan kursor.. Ketika saya mengubah ke operasi berbasis set, prosesnya memakan waktu 20 menit. - person Charles Bretana; 13.11.2008

Jawaban di atas belum cukup menekankan pentingnya mengunci. Saya bukan penggemar kursor karena sering mengakibatkan kunci tingkat tabel.

person Community    schedule 28.12.2008
comment
ya terima kasih! Tanpa pilihan untuk mencegahnya (hanya baca, teruskan saja, dll) mereka pasti akan melakukannya, seperti halnya operasi (server sql) apa pun yang menempati beberapa baris dan kemudian beberapa halaman baris. - person Steven A. Lowe; 29.12.2008
comment
?? Itu masalah dengan strategi penguncian Anda, BUKAN kursor. Bahkan pernyataan SELECT akan menambahkan kunci baca. - person Adam; 13.03.2019

Untuk apa nilainya, saya telah membaca bahwa "satu" tempat kursor akan melakukan kinerja mitranya yang berbasis set berada dalam total berjalan. Pada tabel kecil, kecepatan menjumlahkan baris berdasarkan urutan kolom mendukung operasi berbasis himpunan, tetapi seiring bertambahnya ukuran baris tabel, kursor akan menjadi lebih cepat karena kursor dapat dengan mudah membawa nilai total yang berjalan ke lintasan berikutnya. lingkaran. Sekarang di mana Anda harus melakukan running total adalah argumen yang berbeda...

person Community    schedule 14.11.2008
comment
Jika yang Anda maksud dengan menjalankan total suatu jenis agregasi (min, maks, jumlah), setiap DBMS yang kompeten akan mengalahkan solusi sisi klien, berbasis kursor, jika hanya karena fungsi tersebut dilakukan di mesin dan tidak ada klien ‹--› overhead server. Mungkin SQL Server tidak kompeten? - person Richard T; 28.12.2008
comment
@[Richard T]: kita sedang mendiskusikan kursor sisi server, seperti dalam prosedur tersimpan, bukan kursor sisi klien; maaf bila membingungkan! - person Steven A. Lowe; 29.12.2008

Di luar masalah kinerja (bukan), menurut saya kegagalan terbesar kursor adalah sulitnya melakukan debug. Terutama dibandingkan dengan kode di sebagian besar aplikasi klien di mana proses debug cenderung relatif mudah dan fitur bahasa cenderung lebih mudah. Faktanya, saya berpendapat bahwa hampir semua hal yang dilakukan seseorang dalam SQL dengan kursor mungkin seharusnya terjadi di aplikasi klien.

person Community    schedule 09.06.2009
comment
SQL sulit untuk di-debug, bahkan tanpa kursor. Alat langkah-langkah MS SQL di Visual Studio sepertinya tidak menyukai saya (mereka sering hang, atau tidak tersandung breakpoint sama sekali), jadi saya biasanya hanya menggunakan pernyataan PRINT ;-) - person Steven A. Lowe; 10.06.2009

Bisakah Anda memposting contoh kursor atau tautan ke pertanyaan? Mungkin ada cara yang lebih baik daripada CTE rekursif.

Selain komentar lain, kursor bila digunakan secara tidak benar (yang sering kali) menyebabkan kunci halaman/baris yang tidak perlu.

person Community    schedule 13.11.2008
comment
ada cara yang lebih baik - kursor aneh ;-) - person Steven A. Lowe; 13.11.2008

Anda mungkin bisa menyimpulkan pertanyaan Anda setelah paragraf kedua, daripada menyebut orang "gila" hanya karena mereka memiliki sudut pandang yang berbeda dari Anda dan mencoba mengejek para profesional yang mungkin memiliki alasan bagus untuk merasakan apa yang mereka rasakan.

Mengenai pertanyaan Anda, walaupun ada situasi di mana kursor mungkin diperlukan, menurut pengalaman saya, pengembang memutuskan bahwa kursor "harus" digunakan JAUH lebih sering daripada yang sebenarnya terjadi. Kemungkinan seseorang melakukan kesalahan karena terlalu banyak menggunakan kursor vs. tidak menggunakannya pada saat yang seharusnya JAUH lebih tinggi menurut saya.

person Community    schedule 13.11.2008
comment
tolong baca lebih hati-hati, Tom - ungkapan yang tepat adalah kebencian yang gila; dibenci adalah objek dari kata sifat gila, bukan orang. Bahasa Inggris terkadang agak sulit ;-) - person Steven A. Lowe; 13.11.2008

pada dasarnya 2 blok kode yang melakukan hal yang sama. mungkin ini contoh yang agak aneh tapi itu membuktikan maksudnya. SQLServer 2005:

SELECT * INTO #temp FROM master..spt_values
DECLARE @startTime DATETIME

BEGIN TRAN 

SELECT @startTime = GETDATE()
UPDATE #temp
SET number = 0
select DATEDIFF(ms, @startTime, GETDATE())

ROLLBACK 

BEGIN TRAN 
DECLARE @name VARCHAR

DECLARE tempCursor CURSOR
    FOR SELECT name FROM #temp

OPEN tempCursor

FETCH NEXT FROM tempCursor 
INTO @name

SELECT @startTime = GETDATE()
WHILE @@FETCH_STATUS = 0
BEGIN

    UPDATE #temp SET number = 0 WHERE NAME = @name
    FETCH NEXT FROM tempCursor 
    INTO @name

END 
select DATEDIFF(ms, @startTime, GETDATE())
CLOSE tempCursor
DEALLOCATE tempCursor

ROLLBACK 
DROP TABLE #temp

pembaruan tunggal membutuhkan waktu 156 mdtk sedangkan kursor membutuhkan waktu 2016 mdtk.

person Community    schedule 13.11.2008
comment
ya, ini membuktikan bahwa ini adalah cara yang sangat bodoh untuk menggunakan kursor! tetapi bagaimana jika pembaruan setiap baris bergantung pada nilai baris sebelumnya dalam urutan tanggal? - person Steven A. Lowe; 13.11.2008
comment
BEGIN TRAN SELECT TOP 1 baseval FROM table ORDER BY timestamp DESC INSERT table (fields) VALUES (vals, termasuk nilai turunan dari record sebelumnya) COMMIT TRAN - person dkretz; 13.11.2008
comment
@doofledorfer: itu akan menyisipkan satu baris berdasarkan baris terakhir berdasarkan tanggal, bukan memperbarui setiap baris dengan nilai dari baris sebelumnya dalam urutan tanggal - person Steven A. Lowe; 14.11.2008
comment
Untuk benar-benar menggunakan kursor Anda harus menggunakan WHERE CURRENT OF dalam pembaruan - person erikkallen; 28.12.2008