Are Cursors evil or not?
Tuesday 06 September 2011
Πριν τις καλοκαιρινές μου διακοπές είχα ένα τμήμα στο οποίο έκανα μάθημα όλη την σειρά σεμιναρίων του SQL Server 2008 R2. Το τμήμα αυτό είχε συναδέλφους που ήταν αποκλειστικά Oracle DBAs και Devs. Κατά την διάρκεια των σεμιναρίων είχα αρκετές ερωτήσεις του στυλ στην Oracle κάνουμε αυτό στο SQL το κάνουμε και πώς το κάνουμε. Από μια σύμπτωση η σειρά το σεμιναρίων ήταν πρώτα το administration, και μετά το programming και μετά το σεμινάριο με την T-SQL.
Μια ερώτηση που τέθηκε από την πρώτη μέρα από όλους αλλά ειδικά από τον Τ (ας τον πούμε έτσι για λόγους ανωνυμίας) ήταν αν ο SQL Server έχει cursors. Επειδή το θέμα θα το βλέπαμε διεξοδικά στο τελευταίο σεμινάριο, αλλά είναι και ένα θέμα που χωράει μεγάλη κουβέντα και ανάλυση του απάντησα όπως απαντώ όταν δεν έχω αρκετό χρόνο στην διάθεση μου ότι ναι έχει αλλά προσπαθούμε να τους αποφύγουμε.
Φυσικά η απάντηση μου αυτή δεν άρεσε σε κανένα συμμετέχοντα καθώς παιδιά της Oracle τα κάνουν όλα με cursors. Εξήγησα ότι θα το εξετάσουμε το θέμα όταν έρθει ή ώρα του. Το δέχθηκαν και συνεχίσαμε ομαλά. Φυσικά ο φίλος Τ πάντα ρωτούσε με cursor μπορώ να κάνω αυτό ή αυτό το κάνω πανεύκολα με ένα cursor όταν έδειχνα κάτι. Για να μην πλατειάσω στην εισαγωγή η απάντηση μου ήταν σταθερά η ίδια και φυσικά αυτό έκανε τον φίλο T έξαλλο (μέχρι που ήρθε η στιγμή και τους εξηγήσαμε αναλυτικά και ηρέμησε).
Αν ψάξουμε στο internet ή ρωτήσουμε ανθρώπους που ασχολούνται με τον SQL Server το σύνηθες είναι να διαβάσετε ή να ακούσετε ότι δεν είναι καλό στον SQL Server να χρησιμοποιούμε cursors γιατί
- καταναλώνουν αρκετή μνήμη,
- έχουν μεγάλο I/O,
- είναι αργοί σε απόκριση/ταχύτητα
- είναι κακός προγραμματισμός.
Τελικά ισχύουν όλα αυτά; Είναι τόσο κακοί οι cursors που δεν πρέπει να τους χρησιμοποιούμε;
Η αλήθεια είναι ότι οι cursors έχουν την φήμη αυτή γιατί έχουν κακοχρησιμοποιηθεί από junior devs ή από ανθρώπους που αγνοούν παντελώς την T-SQL γλώσσα και απλά κάποιος τους έδειξε τυχαία αυτό και το χρησιμοποιούν σαν ευαγγέλιο.
Αν για παράδειγμα πούμε ότι είναι κακός προγραμματισμός τότε όλοι οι devs του SQL Server στην Microsoft είναι κακοί προγραμματιστές καθώς αν κάνουμε το παρακάτω query
use master
go
select distinct id from sys.syscomments where text like '%cursor%'
go
θα δούμε πάνω από 200 system stored procedures και functions να κάνουν την χρήση cursor. Είναι όλοι αυτοί κακοί προγραμματιστές; Δεν το νομίζω…
Πολλοί, μέσα σε αυτούς και εγώ, πιστεύουν ότι οι cursors υπάρχουν καλώς μέσα στον SQL Server αρκεί να γνωρίζουμε το πως δουλεύουν καθώς είναι απαραίτητοι για εργασίες όπως
- dynamic operations που δεν μπορούν να υλοποιηθούν με set-based operations,
- είναι εύκολοι στην κατανόηση και εύχρηστοι,
- προσφέρουν την γραμμή-γραμμή επεξεργασία,
- είναι ιδανικοί για scrolling ενός μέρους από ένα μεγάλο result set.
Όμως έχουν και μειονεκτήματα όπως
- προσφέρονται για quick & dirty προγραμματισμό ειδικά από junior developers αλλά και από αυτούς που έμαθαν κάτι τυχαία και το έχουν κάνει ψωμοτύρι,
- εξαιτίας του ότι είναι memory resident set of pointers καταναλώνουν μνήμη,
- είναι γρηγορότεροι από ένα while loop αλλά έχουν περισσότερο overhead,
- όταν δεν ξέρεις πώς να τους χρησιμοποιήσεις τους χρησιμοποιείς με λάθος τρόπο και γράφει και λάθος τον κώδικα σε αυτούς.
Δόξα το Θεό τα BOL του SQL Server περιγράφουν λεπτομερέστατα του cursors και δεν χρειάζεται να κάνω μια διεξοδική αναφορά σε αυτό το post.
Αυτό που θα κάνω όμως είναι να εστιάσω την προσοχή σας στο πως δηλώνω ένα cursor με την παρατήρηση ότι υπάρχουν δύο συντάξεις η ISO και η T-SQL Extended, σας προτείνω να το διαβάσετε αναλυτικά και με προσοχή ιδιαίτερα την T-SQL Extended εκδοχή.
Γνωρίζοντας τα παραπάνω θα είστε σε θέση την επόμενη φορά να χρησιμοποιήσετε σωστά τους cursors δηλώνοντας το σωστό cursor option με το οποίο θα χρησιμοποιηθεί ιδανικά τόσο η tempdb όσο και η μνήμη του συστήματος σας χωρίς να υπάρχουν performance penalties. Είναι τόσο απλό….
/*antonch*/