Overview
Το να γράψω το συγκεκριμένο άρθρο ήταν μια προσωπική πρόκληση. Είμαι σίγουρος ότι θα υπάρξουν αντιδράσεις μιας και το GOTO statement έχει εξοβελιστεί στο πυρ το εξώτερον. Όμως δεν έχω καμία διάθεση ή πρόθεση να μπω σε αυτές.
Προσωπικά δεν θεωρώ το GOTO statement κακή πρακτική στο να χρησιμοποιηθεί, αρκεί αυτή να είναι σωστή.
Σαν προγραμματιστής δεν θεωρώ κακό κανένα statement αλλά διαφωνώ στην μη σωστή χρήση του.
Δεν θα κάνω ιστορική αναδρομή ούτε θα τονίσω το πόσο κακό μπορεί να κάνει η λανθασμένη χρήση του GOTO γιατί αυτά είναι γνωστά και μπορεί εύκολα ο καθένας να τα βρει στην εποχή του internet.
Σε αυτό το άρθρο θέλω να δείξω το πως μπορεί το GOTO να χρησιμοποιηθεί σωστά στο T-SQL code που γράφουμε ώστε να επιτύχουμε την "καθαρότητα" του κώδικα μας στην εκτέλεση και κυρίως στην ανάγνωση του.
Επίσης να επισημάνω ότι μιλάω μόνο για T-SQL code και σε καμία περίπτωση δεν κάνω αναγωγή σε γλώσσες προγραμματισμού καθώς εκεί έχουμε αρκετές δυνατότητες που δεν υπάρχουν στην SQL language για την αποφυγή της χρήσης του. Τέλος να αναφέρω ότι σε καμία περίπτωση δεν εγκρίνω την χρήση του που οδηγεί σε spaghetti code.
Existing usage in system stored procedures
Αν κοιτάξουμε με το παρακάτω query στα system object θα βρούμε πάνω από 250 stored procedures που περιέχουν GOTO και αν με την sp_helptext δούμε το κώδικα κάποιων από αυτές θα βρούμε αρκετά patterns που δείχνουν την χρήση του.
SQL Script
select object_schema_name(object_id) as [schema_name], object_name(object_id) as [object_name]
from sys.system_sql_modules
where definition like '%goto%'
Τα περισσότερα από αυτά έχουν να κάνουν με error trapping και transactions. Για να προλάβω αντιδράσεις να πω ότι αρκετές από αυτές είναι γραμμένες πριν την υποστήριξη του begin try/catch στο SQL Server.
Possible use cases
Γενικότερα θα δείτε και άλλα patterns που είναι χρήσιμα όμως θα εστιάσω σε δύο από αυτά καθώς κάνουν την ζωή μας ευκολότερη τόσο στην ανάγνωση όσο και στην συντήρηση του κώδικα που γράφουμε σε stored procedures, trigger κ.λ.π.
Avoid code repeat
Μια χρήση είναι για την αποφυγή επανάληψης του ίδιου κώδικα όπως στο παράδειγμα.
SQL Script
create procedure pname @p1 int
as
begin
... -- a lot of code lines
if ( @p1 = x )
begin
goto samecode;
end
if ( @p1 = y )
begin
goto samecode;
end
goto exitsp
samecode:
... -- code
exitsp:
... -- safe exit
return
end
go
Για να προλάβω πάλι τις αντιδράσεις που λένε να βάλω αυτό σε κάποιο άλλο function ή stored procedure και να το καλώ όπου χρειάζεται να πω ότι ναι είναι μια λύση αλλά αυξάνει τα dependencies και σε αρκετές περιπτώσεις το performance καθώς μιλάμε για executions plans από διαφορετικά objects που πρέπει να συνεργαστούν.
Ιδανικά θα ήθελα να έχω μια λογική gosub/return αλλά δεν την έχουμε διαθέσιμη στην T-SQL και ναι μεν μπορείς να το κάνεις με flags αλλά δεν μου αρέσει και τόσο γιατί πάμε στη λογική του μακαρονιού.
Multiple IF/ELSE cases
Μία χρήση που την χρησιμοποιώ αρκετά καθώς με βοηθάει στο να έχω καθαρό κώδικα και να μην χάνομαι μέσα σε αυτό είναι όταν έχω cases.
Δυστυχώς στην T-SQL δεν υπάρχει η λογική της switch όπως πχ σε C#. Ναι ξέρω θα μου πείτε για την CASE αλλά η CASE παίζει μόνο συνδυαστικά με κάποιο άλλο statement. Οπότε αναγκαστικά παίζεις με IF… ELSE και εκεί αν έχεις πολλά χάνεται το μάτι και ο έλεγχος ιδιαίτερα όταν στο κάθε IF υπάρχουν αρκετές γραμμές κώδικα, μάλιστα ούτε η χρήση tabs (indention) στο κώδικα δεν σε σώζει.
Δείτε το παρακάτω παράδειγμα και σκεφτείτε να υπάρχει αρκετός κώδικας μέσα σε κάθε IF...ELSE
SQL Script
create procedure pname @p1 int
as
begin
if ( @p1 = x )
begin
...
end
else if ( @p1 = y )
begin
....
end
else if ( @p1 = z )
begin
....
end
else
begin
...
end
return
end
go
Θα μπορούσε να γίνει όπως παρακάτω και αυτό βοηθάει αρκετά στην συντήρηση και ανάγνωση του κώδικα.
SQL Script
create procedure pname @p1 int
as
begin
if ( @p1 = x ) goto caseX
if ( @p1 = y ) goto caseY
if ( @p1 = z ) goto caseZ
else goto caseother
return
caseX:
....
return
caseY:
....
return
caseZ:
....
return
caseother:
....
return
end
go
Summary
Για ακόμα μια φορά θα πω ότι η χρήση της θέλει προγραμματιστική εμπειρία από αυτόν που γράφει τον κώδικα καθώς την ώρα που γράφει πρέπει να είναι σε θέση να αναγνωρίζει το πότε την χρησιμοποιεί σωστά και πότε ξεφεύγει η κατάσταση.
Τέλος να πω ότι τα παραδείγματα που παρατέθηκαν στον άρθρο αυτό επιδέχονται αλλαγών αλλά τα κράτησα απλά ώστε να γίνει κατανοητό το νόημα.