Εισαγωγή
Αν και δεν είμαι αρκετά σίγουρος ότι όλοι έχουν καταλάβει ότι θα πρέπει να αποφεύγουν όπως ο διάολος το λιβάνι τα implicit conversions εντούτοις αυτό είναι κάτι γνωστό και πηγή δεινών αν αυτό γίνεται στην T-SQL. Υπάρχουν αρκετά άρθρα που το αναφέρουν αυτό και με αυτό το post θα συμπληρώσω και εγώ ακόμα ένα παράδειγμα με σκοπό μήπως και κάποιοι συνετιστούν και σταματήσουν να το κάνουν.
Αυτό το post αποφάσισα να το γράψω μετά από ένα πραγματικό γεγονός που έγινε σε πραγματικό περιβάλλον εργασίας και το οποίο δημιουργούσε αρκετά προβλήματα.
Για να γίνει κατανοητό αυτό ας έρθουμε να δούμε ένα παράδειγμα και για αυτό το λόγο ας έρθουμε να φτιάξουμε μια βάση με ένα πίνακα που θα παίξει το ρόλο του πειραματόζωου και θα είναι ένας πίνακας πελατών όπου η απόφαση που έχουμε είναι να υπάρχει το ΑΦΜ σαν σημείο αναφοράς για το record άρα και primary key. Σε αυτόν θα βάλουμε 10.000 rows. Όλα αυτά μπορούν να γίνουν με τα παρακάτω script
Το Παράδειγμα
CREATE DATABASE LocalTest;
GO
USE LOCALTEST;
GO
CREATE TABLE Customers
(
AFM CHAR(9) NOT NULL ,
CustomerName VARCHAR(250) NOT NULL,
ContactName VARCHAR(50) NOT NULL
);
GO
ALTER TABLE Customers
ADD CONSTRAINT PK_Customers PRIMARY KEY (AFM);
GO
SET NOCOUNT ON;
DECLARE @I INT=100000000;
WHILE @I < 100010000
BEGIN
INSERT INTO Customers
VALUES
(
CAST(@I AS VARCHAR(9)),
'Customer :'+ CONVERT(VARCHAR,@I),
'Contact :'+ CONVERT(VARCHAR,@I)
);
SET @I+=1;
END;
GO
SELECT COUNT(*)
FROM Customers;
GO
SELECT AFM,
CustomerName,
ContactName
FROM Customers;
GO
Ελπίζοντας μέχρι εδώ όλα να είναι κατανοητά θα προχωρήσουμε στο επόμενο μας βήμα με το οποίο θα δείξω πως όλα μπορούν να γίνουν άσχημα ξεκινώντας από το ποιο απλό, το οποίο δεν είναι άλλο από το ζητήσω ένα πελάτη με συγκεκριμένο ΑΦΜ.
Έχω δύο developers ο ένας γράφει αυτό ως εξής
SELECT AFM,
CustomerName,
ContactName
FROM Customers
WHERE AFM='100000010';
GO
Και ο άλλος όπως παρακάτω
SELECT AFM,
CustomerName,
ContactName
FROM Customers
WHERE AFM=100000010;
Και οι δύο παίρνουν το ίδιο αποτέλεσμα. Αν όμως δω τα execution plans θα διαπιστώσω ότι το ένα έκανε seek που είναι και το λογικό και αυτό ήθελα να γίνει ενώ στο δεύτερο έκανε scan!!!!!. Ωραίο ε;
Ας δούμε όμως και το κόστος τους; Το πρώτο έχει 4% και το δεύτερο 96%!!!
Η Επεξήγηση
Τι όμως έκανε το δεύτερο να έχει άλλη συμπεριφορά στην εκτέλεση με συνέπεια να έχω και μεγαλύτερο κόστος;
Αν στα εxecution plans έρθουμε στο δεύτερο query περάσουμε το mouse στο Clustered Index Scan θα δούμε εμφανίζεται ένα tooltip όπως στην παρακάτω εικόνα
Αυτό μπορούμε να το δούμε και επιλέγοντας το node στο execution plan πατήσουμε F4 και μας εμφανιστεί το Properies window
Και στις δύο εικόνες θα παρατηρήσουμε ότι στο Pedicate λέει ξεκάθαρα ότι έκανε CONVERT_IMPLICIT(int,[LocalTest].[dbo].[Customers].[AFM],0)=[@1], κάτι το οποίο στο πρώτο δεν γίνεται.
Επίλογος
Όλα αυτά έγιναν διότι ο δεύτερος developer αποφάσισε σκεπτόμενος ότι το ΑΦΜ είναι νούμερο και μπορεί να το γράψει όπως το έγγραψε και ο SQL Server το δέχεται καθώς κάνει implicit conversions. Αναρωτιέμαι αν θα το έκανε κάτι τέτοιο αν έγγραφε σε C# και μη μου πείτε ότι εκεί δεν σου επιτρέπει να κάνεις κάτι τέτοιο, στο επιτρέπει αλλά δεν το κάνεις γιατί σου έχει περάσει με ένεση μέσα στο αίμα σου ότι κάτι τέτοιο είναι κακό.