Jump to content

antonch

Administrators
  • Posts

    1030
  • Joined

  • Last visited

  • Days Won

    7

Blog Entries posted by antonch

  1. antonch
    Ένας DBA στην καθημερινότητα του χρειάζεται να διαβάζει αρκετές φορές τα logs που ο SQL Server παράγει είτε για το ίδιο το SQL Server service είτε για το SQL Server Agent service.
    Φυσικά και υπάρχει γραφικό περιβάλλον που μπορεί εύκολα κανείς να κάνει αυτό. Υπάρχει όμως και μη γραφικός τρόπος που διευκολύνει περισσότερο, κατά την γνώμη μου, στην ανάγνωση των logs ιδιαίτερα όταν αυτά έχουν μεγάλο όγκο από καταχωρήσεις.
     
    http://www.sqlschool.gr/blog/using-t-sql-commands-to-read-sql-log-files-1004.aspx
  2. antonch
    Οι περισσότεροι από εσάς όταν θέλετε να αλλάξετε την δομή ενός πίνακα σε μια βάση χρησιμοποιείτε το SQL Server Management Studio (SSMS) όπου με δεξί κλικ στον πίνακα κάνετε design και αφού κάνετε την αλλαγή που θέλετε πατάτε το save button από την toolbar.
     
    Είμαι σίγουρος έχετε κάνει πολλά πράγματα έτσι και έχετε μείνει απόλυτα ευχαριστημένοι. Θεωρώ επίσης βέβαιο ότι κάποια στιγμή που κάτι πάλι θέλατε να αλλάξετε αυτό δεν μπορούσε να γίνει καθώς ο SSMS σας εμφάνισε ένα παράθυρο όπως το παρακάτω.
     
    http://www.sqlschool.gr/blog/do-not-uncheck-%E2%80%9Cprevent-saving-changes-that-require-table-re-creation%E2%80%9D-option-on-ssms-1009.aspx
  3. antonch
    Όπως θα έχετε δει τον τελευταίο καιρό και συγκεκριμένα το τελευταίο μήνα ασχολούμαι συστηματικά με την αναμόρφωση του SqlSchool.gr.
    Αυτό ήταν κάτι το οποίο έπρεπε να γίνει εδώ και πολύ καιρό αλλά μια το ένα μια το άλλο δεν μου άφηναν τον χρόνο που χρειαζόμουν για το κάνω αυτό, βέβαια ούτε και τώρα τον είχα αλλά πήρα την απόφαση να το κάνω καθώς ήταν κάτι που δεν έπρεπε να μείνει στην παλαιότερη κατάσταση του.
    Στην αρχή κοίταξα μήπως και χρησιμοποιήσω κάποια έτοιμη πλατφόρμα και να γλυτώσω χρόνο όμως δεν μου άρεσε κάτι ιδιαίτερα και έτσι κάθισα και έφτιαξα μια δικιά μου που πρωτίστως να ικανοποιεί τις ανάγκες μου που ήταν και είναι αρκετές αλλά και δευτερευόντως ήθελα να έχω τον πλήρη έλεγχο σε αυτή σε επίπεδο κώδικα.
    Αυτό τον εγχείρημα ξεκίνησε πριν 40 ημέρες και καθημερινά από τις 9 το βράδυ μέχρι τις 3 τα ξημερώματα έγραφα κώδικα σαν τρελός. Πραγματικά ήταν κάτι το οποίο ευχαριστήθηκα καθώς είχα καιρό να γράψω κάτι τόσο μεγάλο, καθώς πλέον δεν γράφω κώδικα μιας και η καθημερινές μου αρμοδιότητες στο χώρο της εργασία μου είναι άλλες. Παρόλα αυτά όμως διαπίστωσα ότι τελικά είναι σαν το ποδήλατο και αυτό που έκανα τα τελευταία από το 1984 ερασιτεχνικά και από το 1988 επαγγελματικά μέχρι πριν 3-4 χρόνια δεν με έχει εγκαταλείψει.
    Για την υλοποίηση του site χρησιμοποίησα όλες τις πρόσφατες τεχνολογίες όπως .NET 4.0, CSS3, ASP.NET 4.0, WCF, AJAX, JQuery και πολλά ακόμα, όχι EF δεν χρησιμοποίησα. Δυστυχώς δεν μπόρεσα να χρησιμοποιήσω SQL Server 2012 καθώς ο hosting provider ακόμα δεν τον έχει εγκαταστήσει αλλά όμως θα γίνει σύντομα και αυτό! J. Όμως από τις ανάγκες μου στην υλοποίηση δημιούργησα κάποια καλά κομμάτια κώδικα σε T-SQL που θα μοιραστώ μαζί σας σύντομα.
    Ήθελα αυτό που θα φτιαχτεί να έχει μεγάλη διάρκεια ζωής ώστε να ασχοληθώ περισσότερο με το περιεχόμενο. Προσπάθησα να το φτιάξω έτσι ώστε να παίζει σε όλους τους νέους browsers χωρίς προβλήματα και από ότι φαίνεται αυτό έχει επιτευχθεί.
    Επίσης ένα ακόμα σημείο που ήθελα να προσέξω αρκετά και το πρόσεξα ήταν η εύρεση του site από τις μηχανές αναζήτησης. Ομολογώ ότι στο παρελθόν δεν είχα ασχοληθεί αρκετά με το Search Engine Optimization (SEO) αλλά τελικά πότε δεν είναι αργά. Εδώ και μερικές ημέρες βλέπω ότι οι προσπάθειες μου έχουν αποδώσει καρπούς και πλέον στις αναζητήσεις είναι το sqlschool.gr πάντα στην πρώτη σελίδα. Φυσικά χρειάζεται ακόμα δουλειά σε αυτό αλλά οι βάσεις έχουν μπει.
    Από τα πράγματα που ήθελα να κάνω από την αρχή ήταν το sqlschool.gr να αποτελέσει ένα πόλο για τον SQL Server. Για αυτό το λόγο πριν από λίγο έκανα migrate το blog μου από το wordpress στην πλατφόρμα που έφτιαξα για το sqlschool.gr. Πλέον όλα μου τα posts είναι στο sqlschool.gr και όλα τα νέα θα είναι σε αυτό. Για λίγο καιρό ακόμα θα υπάρχει το blog στο wordpress και θα ενημερώνω αυτό με τα νέα μου post απλά με τα links. Για το λόγο αυτό σχόλια παρατηρήσεις και αξιολογήσεις δεν θα φαίνονται στο wordpress αλλά αυτές θα πρέπει να γίνονται από εσάς στο sqlschool.gr. Έτσι θα είναι όλα σε ένα σημείο το οποίο νομίζω ότι καλύτερο για όλους.
    Αν θέλετε να ενημερώσετε τα bookmarks σας με το νέο blog δεν έχετε παρά να βάλετε το www.sqlschool.gr/blogs/antonch
    Φυσικά παρατηρήσεις, επιθυμίες και ότι άλλο θέλετε είναι ευπρόσδεκτες. Γνωρίζω ότι η πλατφόρμα μου έχω φτιάξει χρειάζεται και άλλα πράγματα και έχουν δρομολογηθεί αλλά σιγά σιγά.
    Σας ευχαριστώ για μια ακόμα φορά για την εμπιστοσύνη που μου δείχνετε.
    Φιλικά
    Αντώνης
  4. antonch
    Είναι πρωινό της 27/10/2014. Κάνει κρύο για τα δικά μου δεδομένα και είμαι στο γραφείο στην δουλειά μου. Χτυπάει το τηλέφωνο και είναι ο Σοφοκλής.
     
    http://www.sqlschool.gr/blog/do-not-forget-to-update-index-statistics-1005.aspx
  5. antonch
    Ένα ερώτημα που με ρωτάνε συνέχεια όλοι. Μάλιστα στο πρώτο SQL Saturday που έγινε στην Ελλάδα το Σάββατο 4/10/2014 η συγκεκριμένη ερώτηση είχε μεγάλο σουξέ.
    Για αυτό λοιπόν θα την απαντήσω και γραπτώς ώστε να είναι εύκολα αναγνώσιμη από όλους σας.
    http://www.sqlschool.gr/blog/how-often-i-have-to-do-rebuild-or-reorganize-indexes-in-my-database-1003.aspx
  6. antonch
    Ένα από τα πράγματα τα οποία προσωπικά θεωρώ από τα διαμάντια του SQL Server που έχουμε διαθέσιμα από την έκδοση του SQL Server 2005 (Standard & Enterprise) SP1 είναι το Database Mirroring.

    Μέχρι την εμφάνιση του για να έχω αυτό που όλοι θέλουμε και δεν είναι άλλο από το database availability έπρεπε να καταφύγουμε σε κάποιες λύσεις όπως:

    1. Clustering

    Ιδανική λύση αλλά στοιχίζει αρκετά σε χρήμα τόσο για την υλοποίηση της όσο και στη διαχείριση του.

    2. Replication

    Αξιόπιστη λύση, με κάποια θεματάκια στην υλοποίηση της, αλλά και με κάποιες παραδοχές που πρέπει να λάβουμε ανάλογα με το τι είδος replication θα υλοποιήσουμε και αφορούν στην παλαιότητα της πληροφορίας που θα έχουμε στην replica.

    3. Log Shipping

    Μια ιδανική λύση για αυτούς που δεν έχουν χρήματα για να υλοποιήσουν τις παραπάνω λύσεις, αλλά έχουν την δυνατότητα να υποστούν απώλεια δεδομένων πχ τα transactions των τελευταίων 15 λεπτών. Αλλά έχουν και την πολυτέλεια να έχουν ένα αρκετά υψηλό downtime μέχρι το αντίγραφο να έρθει σε κατάσταση λειτουργίας και να ενημερωθούν όλες οι εφαρμογές να χρησιμοποιούν αυτό πλέον.

    Προσωπικά και χαριτολογώντας πάντα αυτό το ονομάζω το cluster του φτωχού.

    Έχοντας υλοποιήσει στο παρελθόν και τις τρεις λύσεις, έχω μόνο καλά λόγια να πω και για τις τρεις. Απλά θέλω να υποσημειώσω ότι κάθε μία έχει υλοποιηθεί με οδηγό τις ανάγκες του κάθε πελάτη. Για παράδειγμα αν αυτός δεν μπορεί να έχει data loss και θέλει zero downtime δεν θα του βάλεις την 2 και την 3 λυση.



    Σημείωση

    Αν και με την λύση 2 έχοντας ένα transactional replication σε ένα πολύ γρήγορο δίκτυο μπορείς να έχεις παλαιότητα πληροφορίας κάτω από 1 sec

    Τι είναι όμως το Database Mirroring;

    Είναι δυνατότητα που μας δίνει ο SQL Server ώστε να έχω ένα hot standby αντίγραφο της βάσης μου σε ένα άλλο server που είναι σε διαφορετική γεωγραφική περιοχή. Η μόνη απώλεια δεδομένων που θα έχω θα είναι μόνο τα transactions τα οποία ήταν σε εξέλιξη (δεν είχαν προλάβει να γίνουν commit) κατά την στιγμή της καταστροφής της βάσης ή του server, όπως ακριβώς συμβαίνει και με την λύση του cluster.

    Σημείωση

    Το Database Mirroring είναι σε επίπεδο database και όχι σε επίπεδο server όπως το clustering. Αυτό σημαίνει ότι αν στο server της παραγωγής έχω 10 databases θα πρέπει να κάνω Mirror κάθε μια ξεχωριστά.



    ΔΕΝ ΜΠΟΡΩ ΝΑ ΚΑΝΩ MIRROR ΣΤΙΣ SYSTEM DATABASES (MASTER, MSDB, MODEL, TEMPDB)

    Ποια είναι η Αρχιτεκτονική του Database Mirroring;

    Η αρχιτεκτονική του database mirroring είναι σχετικά απλή όπως φαίνεται στο παρακάτω σχεδιάγραμμα.



    Αυτή είναι η απλή λύση όπου έχω τους δύο servers μου. Τον Server της παραγωγής με την βάση μου το ονομάζουν Principal Server ενώ το mirror server τον ονομάζουν Mirror Server. Σε αυτή την περίπτωση όμως αν "σκάσει" ο Principal θα πρέπει με το "χέρι" να φέρω στην ζωή τον Mirror ο οποίος θα γίνει μετά από αυτή την διαδικασία Principal.

    Μα καλά θα πει κάποιος τι σόι διαθεσιμότητα είναι αυτή αν πρέπει να το κάνω με το "χερι"; Είπαμε αυτή είναι η απλή λύση.

    Υπάρχει η κανονική λύση η οποία είναι η παρακάτω:



    Σε αυτή την περίπτωση εμπλέκεται ακόμα ένας server o Witness Server του οποίου η δουλειά είναι να παρακολουθεί το database mirroring έτσι ώστε αν "πέσει" o principal αυτόματα να κάνει principal τον mirror server.

    Σημείωση

    Συνηθίζουμε να ονομάζουμε τους τρεις αυτούς servers (Principal, Mirror, Witness) σαν Mirror Partners ή Partners.



    Προϋποθέσεις και μικρά αλλά σημαντικά μυστικά για την Υλοποίηση

    1. Όλοι οι servers πρέπει να είναι στην ίδια version του SQL Server π.χ 2008. Δεν μπορεί κάποιος από αυτούς να είναι σε 2005

    2. Ο Principal και ο Mirror πρέπει να είναι στην ίδια edition πχ. Standard ή Enterprise

    3. O Witness πρέπει να είναι σε ένα reliable computer system και μπορεί να είναι Standard, Enterprise, Workgroup ή Express edition.

    4. O Mirror server πρέπει να έχει τον χώρο που χρειάζεται η βάση που έχουμε στον Principal.

    5. Στην περίπτωση που θα υλοποιήσουμε το σενάριο high safety with automatic failover σε όλους τους partners το cpu load να είναι μικρότερο από το 50% (ειδικά για τον principal και τον mirror). Και αυτό διότι αν έχουμε cpu overload o failover partner μπορεί να μην μπορέσει να κάνει ping τα άλλα server instances με αποτέλεσμα να έχω unnecessary failover!!!. Εάν αυτό δεν μπορεί να γίνει τότε διαλέξετε κάποιο άλλο από τα διαθέσιμα σενάρια (high safety without automatic failover ή high performance).

    6. Όλοι οι SQL Servers που συμμετέχουν στο database mirroring πρέπει στην master database να έχουν το ίδιο code page και collation, αλλιώς θα έχουμε προβλήματα κατά την διάρκεια του database mirroring setup!.

    7. Για καλύτερο performance είναι καλύτερα να χρησιμοποιηθεί dedicated network adapter για το mirroring.

    8. Εάν έχουμε 32bit system, τότε το database mirroring μπορεί να υποστηρίξει μέχρι 10 databases ανά SQL Server instance εξαιτίας του αριθμού των worker threads τα οποία καταναλώνονται για κάθε database mirroring session.

    9. Το database mirroring υποστηρίζει οποιοδήποτε database compatibility level.

    10. To RESTORE του BACKUP από την principal server στον mirror server πρέπει να γίνει με WITH NORECOVERY.

    11. Μόνο το FULL RECOVERY model υποστηρίζεται στο database mirroring. Αυτό σημαίνει ότι η database που θα κάνω Mirror θα πρέπει να έχει αυτό το recovery model, κανένα

    12. Databases που έχουν Filestream enable δεν μπορούν να γίνουν mirror.

    Ποια είναι τα πλεονεκτήματα του Database Mirroring;

    1. Αυξάνει την ασφάλεια των δεδομένων

    2. Αυξάνει την διαθεσιμότητα της database.

    3. Αυξάνει την διαθεσιμότητα του παραγωγικού περιβάλλοντος στην περίπτωση που θελήσουμε σε αυτό να κάνουμε upgrades.

    Πώς δουλεύει το Database Mirroring;



    Ο principal και ο mirror server έχοντας ένα database mirror session μεταξύ τους και στην ουσία του πράγματος ότι γίνεται στον principal server γινεται redo στον mirror server. Αυτό γίνεται όσο το δυνατό γρηγορότερα και στην πραγματικότητα ο principal server στέλνει κάθε ενεργό transaction log record στον mirror server, ο οποίος με την σειρά του το κάνει στην mirror database.

    Σημείωση

    Δεν είναι όπως στο transactional replication το οποίο είναι σε logical level, εδώ είναι σε physical log record level.

    Το database mirroring session τρέχει είτε σύγχρονα είτε ασύγχρονα. Το αν θα χρησιμοποιήσω κάποιο από αυτά εξαρτάται από τον τρόπο με τον οποίο θέλω να γίνεται το database mirroring, επιλέγοντας ένα από τους παρακάτω:

    1. High Safety (synchronous operation)

    Κάθε φορά που ένα transaction γίνεται στην database του principal, αυτός στέλνει τα active log στον mirror server. O mirror με την σειρά του γράφει το active log που πήρε το δυνατόν γρηγορότερα και αφού το κάνει αυτό τότε o principal server παίρνει ένα μήνυμα ότι mirror server έχει κάνει την δουλειά του και κάνει commit (ο principal).



    Σημείωση

    Ο Mirror δεν κάνει commit απλά γράφει το log στο δίσκο μιας και όπως θα δούμε παρακάτω η database στον mirror server είναι σε φάση restore.

    Μετά από αυτό το status τον database είναι synchronized και όσο διατηρείται το database mirroring session αυτό θα είναι πάντα το ίδιο.

    Στον συγκεκριμένο τρόπο έχω δύο ακόμα επιλογές και αυτές είναι αν θα έχω automatic failover ή όχι. Απλά στην πρώτη περίπτωση πρέπει να έχω και witness server, ο οποίος κοιτάζει αν ο principal είναι στην ζωή και αν δεν τότε σηκώνει τον mirror μόνος του. Στην άλλη περίπτωση θα πρέπει να τον σηκώσω εγώ κάνοντας αλλαγή ρόλου είτε με Τ-SQL είτε μέσα από τα UI

    Σημείωση για τους Developers

    Εάν θέλετε οι εφαρμογές που έχετε φτιάξει αυτόματα να βλέπουν τον mirror όταν πέσει ο principal τότε καλό είναι να χρησιμοποιήσετε το SQL Native Client Access σαν provider στο connection string βάζοντας ακόμα μέσα σε αυτό την επιλογή failover parter στην οποία θα βάζεται το όνομα του Mirror Server ενώ στο Server/Data Source θα έχετε κανονικά τον principal π.χ



    Provider=SQLNCLI10.1; Data Source=ac-demosrv; Failover Partner=ac-demosrv\inst01;…

    Στην περίπτωση που έχετε legacy εφαρμογές υπάρχουν δύο λύσεις που σας προτείνω:

    a) Να έχετε 2 connection strings το ένα να δείχνει στον principal και το άλλο στον mirror. Πάντα χρησιμοποιούμε το πρώτο και αν αυτό είναι κάτω πιάνουμε το timeout error και μετά χρησιμοποιούμε το άλλο

    Με κάποιον τρόπο έχουμε εκτός εφαρμογής το connection string και το αλλάζουμε με το χέρι πχ udl file , registry entry με ότι βέβαια συνεπάγεται αυτό από κόστος σε χρόνο για να γίνει.



    Σημείωση

    Εάν θέλω να αλλάξω τους ρόλους με t-sql θα πρέπει να εκτελέσω το εξής command

    ALTER DATABASE SET PARTNER FAILOVER;

    To ίδιο μπορώ να το κάνω από εάν κάνω δεξί κλικ Properties πάνω στην βάση μου και πάω στην επιλογή Mirroring και πατήσω το κουμπί Failover



    Εδώ πρέπει να τονίσω ότι υπάρχουν και μερικές παραλλαγές αλλά για αυτές δείτε τα BOL.



    Ο τρόπος αυτός προσφέρει ασφάλεια και εγγυάται ότι δεν θα έχω απώλεια δεδεμένων στον mirror server όμως έχω κάποιο performance penalty στον principal όσον αφορά το transaction

    2. High Performance (asynchronous operation)

    Εδώ αν και έχω καλύτερο performance μιας και ή όλη διαδικασία γίνεται ασύγχρονα υπάρχει η πιθανότητα να έχω απώλεια δεδομένων στον Mirror Server. Και αυτό διότι όταν γίνεται το transaction στον Principal Server αυτός στέλνει στον Mirror Server το log αλλά δεν περιμένει να πάρει απάντηση για αν το έκανε ή όχι apply αλλά κάνει commit και φυσικά στέλνει στον client ότι όλα είναι ΟΚ. Αυτό σημαίνει ότι ο Mirror Server μπορεί να είναι πίσω όσον αφορά τα δεδομένα που έχει πάρει και σε περίπτωση που πέσει ο Principal και έρθει στην ζωή θα έχει λιγότερα δεδομένα. Στην περίπτωση αυτή ο Mirror Server είναι DISCONNECTED.

    Αυτός ο τρόπος είναι ιδανικός στην περίπτωση που έχω Principal που είναι oveloaded ή στην περίπτωση που έχω τον Mirror σε WAN το οποίο δεν έχει κάλο δίκτυο.

    Υπάρχει ακόμα ένα θέμα στο τρόπο αυτό που έχει να κάνει με τη χρήση του Witness Server. Αν και μπορεί να υπάρχει σε αυτό υπάρχουν περιπτώσεις όπου μπορεί να δημιουργήσει πρόβλημα όπως


    Εάν χαθεί ο Mirror Server ο Principal πρέπει να κάνει connect στον Witness αν δεν τα καταφέρει τότε ο Principal κάνει την database offline ακόμα και αν μετά συνδεθεί είτε ο Mirror είτε ο Witness.
    Στην περίπτωση που χαθεί ο Principal ο Mirror με την σειρά του πρέπει να κάνει connect στον Witness και αν αυτός είναι μη διαθέσιμος τότε η database δεν θα γίνει διαθέσιμη.
    Bήματα Yλοποίησης του Database Mirrorning με T-SQL (εύκολος τρόπος ;-) )

    Aς δούμε την υλοποίηση με κώδικα t-sql στο demo που ακολουθεί

    Demo Video

    Τον κώδικα της παρουσίασης μπορείτε να τον κάνετε download από εδώ

    Bήματα Yλοποίησης του Database Mirrorning με την χρήση του SSMS (δύσκολος τρόπος ;-) )

    Aφού είδαμε το εύκολο τρόπο ας δούμε τον δυσκολο τρόπο υλοποίησης

    Demo Video

    Σας ευχαριστώ για την προσοχή σας

    Αντώνης
  7. antonch
    Περιμένοντας να έρθουν συγγενείς και φίλοι σπίτι να μου ευχηθούν για την γιορτή μου, σκεφτόμουν πώς να ευχαριστήσω όλους που σήμερα είτε με email είτε με μηνύματα στο facebook ή στο messenger μου ευχήθηκαν χρόνια πολλά.
    Στο μυαλό μου ήρθε κάτι το οποίο το είδα πάλι να χρησιμοποιείται αλλά όχι και τόσο καλά το αντίθετο θα έλεγα. Aυτό δεν είναι από το sql server data type uniqueidentifier σαν primary key σε table.
    Ας δούμε λοιπόν τα πράγματα με την σειρά για να καταλάβουμε το πρόβλημα
    Φτιάχνουμε μια νέα βάση που θα γίνει το πείραμα μας
    USE master
    GO

    CREATE DATABASE test
    GO

    Σε αυτή φτιάχνουμε ένα πίνακα με την εξής δομή


    USE test
    GO
    CREATE TABLE t1
    (
    id UNIQUEIDENTIFIER NOT NULL PRIMARY KEY DEFAULT(NEWID()),
    aa INT IDENTITY NOT NULL,
    otherdata NCHAR(2000) DEFAULT (N'other data')
    )

    Ο πίνακας αυτός είναι έτσι φτιαγμένος ώστε να χωράνε δύο rows ανά page (1 page = 8K)


    Όπως βλέπετε έχω το πεδίο id το οποίο είναι uniquidentifier και το έχω ορίσει σαν primary key και επειδή αυτό πρέπει να παίρνει τιμές έχω βάλει σαν default την function NEWID() η οποία σε κάθε εκτέλεση της μας δίνει ένα μοναδικό GUID.


    Επίσης έχω βάλει ένα πεδίο aa το οποίο όπως βλέπετε είναι identity που σημαίνει ότι για κάθε έγγραφή δίνει σειριακό αριθμό ξεκινώντας από το ένα.


    Τέλος για φτιάξω τη λογική που ανέφερα παραπάνω να χωράνε δηλαδή δύο rows ανά σελίδα βάζω το πεδίο otherdata nchar(2000) ώστε να έχω ένα record length 4024 bytes απλά του βάζω μια default τιμή ώστε να έχει κάτι.


    Εκτελώντας το παραπάνω create command φτιάχνω τον πίνακα μου.


    Όπως είναι γνωστό ο SQL Server όταν φτιάχνω ένα πίνακα και του ορίζω το primary key αυτός φτιάχνει ένα unique index για να το υλοποιήσει. Στη περίπτωση δε που δεν υπάρχει ήδη clustered index αυτός είναι unique clustered index, όπως και στο παράδειγμα μας.


    Αυτό είναι εύκολα μπορούμε να το επιβεβαιώσουμε εκτελώντας την


    sp_helpindex t1
    GO

    index_name index_description index_keys
    ------------------------ ------------------------------------------------- ----------
    PK__t1__3213E83F7F60ED59 clustered, unique, primary key located on PRIMARY id

    Αφού λοιπόν έχουμε επιβεβαιώσει την υπαρξη του index ας έρθουμε να δούμε πως αυτό είναι φυσικά δομημένος, θυμίζω ότι ακόμα δεν έχουμε βάλει δεδομένα στον πίνακα μας. Αυτό μπορούμε να το δούμε εύκολα με τη χρήση ενός DMV το οποίο δεν είναι άλλο από το sys.dm_db_index_physical_stats.


    Έτσι εκτελώντας την παρακάτω εντολή


    SELECT index_id, avg_fragmentation_in_percent, page_count
    FROM sys.dm_db_index_physical_stats(DB_ID('test'),OBJECT_ID('t1'),1,NULL,'LIMITED')
    GO


    έχουμε το παρακάτω αποτέλεσμα


    index_id avg_fragmentation_in_percent page_count
    ----------- ---------------------------- --------------------
    1 0 0

    Παρατηρούμε ότι όλα τα πεδία μετά από index_id είναι μηδενικά και αυτό είναι σωστό καθώς δεν έχουμε ακόμα βάλει δεδομένα στο πίνακα μας.


    Ας έρθουμε όμως να βάλουμε δύο γραμμές σε αυτόν


    INSERT INTO t1 DEFAULT VALUES
    INSERT INTO t1 DEFAULT VALUES
    GO

    Και ας ξαναδούμε πως είναι τώρα ο index μας


    SELECT index_id, avg_fragmentation_in_percent, page_count
    FROM sys.dm_db_index_physical_stats(DB_ID('test'),OBJECT_ID('t1'),1,NULL,'LIMITED')
    GO

    Το αποτέλεσμα θα είναι το παρακάτω


    index_id avg_fragmentation_in_percent page_count
    ----------- ---------------------------- --------------------
    1 0 1

    Θα παρατηρήσουμε ότι το page_count είναι ίσο με το ένα. Άρα έχουμε δύο rows/page. Περίφημα μέχρι έδω!!!


    Ας βάλουμε ακόμα δύο γραμμές στον πίνακα μας


    INSERT INTO t1 DEFAULT VALUES
    INSERT INTO t1 DEFAULT VALUES
    GO

    Και ας ξαναδούμε την δομή του index μας.


    SELECT index_id, avg_fragmentation_in_percent, page_count
    FROM sys.dm_db_index_physical_stats(DB_ID('test'),OBJECT_ID('t1'),1,NULL,'LIMITED')
    GO

    Το αποτέλεσμα είναι το παρακάτω


    index_id avg_fragmentation_in_percent page_count
    ----------- ---------------------------- --------------------
    1 66,6666666666667 3

    Εδώ τώρα παρατηρώ κάτι πολύ περίεργο πρώτα από όλα ότι το row_count είναι ίσο με τρία!!! Και το avg_fragmentation_in_percent είναι ίσο με 66,6666666666667


    Αυτό δεν μου αρέσει καθόλου μα καθόλου και φυσικά δεν έχει καμία πιθανότητα να έχω ένα σωστά δομημένο index καθώς επίσης δεν πρόκειται να έχω το επιθυμητό αποτέλεσμα από την χρήση του index.


    Πριν προχωρήσω στην αποκάλυψη του γιατί αυτό έχει συμβεί θα βάλω ακόμα μερικές γραμμές στο πίνακα μου ώστε στο σύνολο να είναι εκατό


    INSERT INTO t1 DEFAULT VALUES
    GO 96

    Ας έρθω να δω τι γίνεται με τον index μου


    SELECT index_id, avg_fragmentation_in_percent, page_count
    FROM sys.dm_db_index_physical_stats(DB_ID('test'),OBJECT_ID('t1'),1,NULL,'LIMITED')
    GO

    Το αποτέλεσμα είναι το παρακάτω και είναι άκρως απογοητευτικό


    index_id avg_fragmentation_in_percent page_count
    ----------- ---------------------------- --------------------
    1 98,4615384615385 65

    Το fragementation είναι στο θεό αλλά έχω 65 σελίδες αντί για 50 που θα έπρεπε να έχω. Οι 15 αυτές σελίδες διαφορά είναι μόνο για τις 100 γραμμές και φυσικά είναι ένα αρκετά σημαντικό performance penalty καθώς αυτό σημαίνει ΙΟ.


    Ας δούμε όμως τα δεδομένα μας


    SELECT * FROM t1
    GO

    Το αποτέλεσμα είναι το παρακάτω


    id aa otherdata
    ------------------------------------ ----------- -----------
    F028B3AF-1415-4081-A006-06DDA3D9FAD2 66 other data
    D58551AF-B632-4297-90A2-10D97AB854D0 51 other data
    A28C272C-1FA3-4506-8C62-1230E0E6926F 75 other data
    2B510EF2-E831-4CC0-BF7C-18B08C932934 24 other data
    394B5429-C8CD-4360-9768-18CE5775A8AE 73 other data
    5950B503-923D-4F33-9B21-1BEECBA1286C 79 other data
    16132ED8-B445-4217-B1E0-1E2D9FA40302 84 other data
    9887FCE3-F229-4236-AEA0-1EBFB5A73CA5 25 other data
    147DEAA4-8725-4797-9EBC-2B341889F9BD 57 other data
    BACC0008-F522-44EE-8A76-2E11BFAFCF80 33 other data
    5DEFA505-EFC3-44AB-9746-2F3AF2838E2D 20 other data
    5BD55DF8-757D-47FA-A53C-35E9AE7B428C 87 other data
    17F09983-33CF-4C48-8D06-36B64CDE3371 98 other data
    9EF858ED-E7B1-4A57-9491-38CB31F62844 90 other data
    3D26646B-DBFC-4D7C-8D9B-419BFF7D0350 47 other data
    7E04E79D-B728-4D8C-8F9E-47700C332433 97 other data
    8B4D0F05-B1F8-40BD-A32C-4D039BACB511 21 other data
    B065A2C4-C4C2-4375-93C8-4DDEF719A278 67 other data
    21C7F7A4-5120-4690-A5B8-5022BDE15233 7 other data
    56ACA620-B7C0-47D1-8821-53D3F2FBDA71 22 other data
    F3D55D7F-F089-424C-AFC8-53E5A76F2B23 31 other data
    2DF49ED6-3113-43AD-A7F4-556CC3328DD8 27 other data
    A7770D81-6818-4A9E-B3FA-57F682DAFFE9 63 other data
    02A840B7-B761-477D-9EB3-598A07A2401F 13 other data
    483B2183-5E03-4618-A39E-5BEF31470CFF 48 other data
    0C702D0F-E9D2-4E63-A5FB-5F66395CA97B 85 other data
    84A84DC9-4F78-4F98-A676-61B89B045A06 76 other data
    03CF97FA-0232-4107-A4FF-61E6FC01CEEA 28 other data
    B1BFB116-44B2-4D2E-BAF6-629854E236EC 64 other data
    95D0F80C-D07D-48B5-93EE-63C1CF67E1DF 95 other data
    BAA630CA-1A98-4DDA-B69B-650FF989B623 55 other data
    9FB18286-7BE6-4BBF-A75D-663961D11F30 71 other data
    D7D4335E-33B7-4FBB-995F-66E15D770284 41 other data
    EBF8C763-6425-4078-9BEF-6A7ADB618F49 58 other data
    2DF2516A-076C-458E-A61C-6A8D379A0F8A 78 other data
    85745D8E-122C-458A-A25C-6B203EEDDDD0 6 other data
    3B53D6A3-5E3D-4672-8FF5-6B725DD50094 77 other data
    F2A31C7E-F409-4FA6-A5AB-6F86F5F0CD21 42 other data
    A4318A22-9055-4FB1-B8BE-71497C1B203B 43 other data
    73923CE8-1F21-471D-BE58-7173C47B8633 68 other data
    76A8FFB8-5D92-4E55-9910-71D773935753 70 other data
    2EB0B127-FF41-478D-A8EC-727FCD0420AB 4 other data
    846E931D-A3E8-4885-BF15-73DCDD8BF06E 49 other data
    5426A25C-B419-4BD0-A8AE-76DEF2BD22B8 91 other data
    3925CE47-DDD1-4689-A4D6-7A8EB3004D63 86 other data
    8176A903-4C10-4DDC-97B4-7C0889442F12 23 other data
    0F436108-401D-40A7-8136-7ED7D91A6A2E 39 other data
    FF52B83A-8FF2-4706-80C3-7F00DE29E0B3 54 other data
    3E29EB19-E709-45F1-9CDD-7FDBBB489DE1 36 other data
    379DEFC6-7757-4A85-B82F-839F88C30AE8 96 other data
    AC7C56BF-3C5E-4EFA-B7A2-83D5E6D18D9A 15 other data
    7543C2EA-EC03-486B-9AB4-84483430E610 74 other data
    83AAC12E-F4C3-48A8-9BF9-8869E6390A4B 8 other data
    4F502BD4-5C99-4DC3-8352-88DA9591B27A 18 other data
    20C8395B-2E91-43C7-8FE1-8B4298BCD4B9 16 other data
    45989F49-574A-4438-A20F-8B609DBF7586 88 other data
    4EB6040C-AC60-42A0-94C9-8BB9542EF541 100 other data
    6476E0AB-7121-4DA2-8888-8CCD5C40CA87 12 other data
    3AA1154A-F650-44DC-B88E-91837A65E60D 19 other data
    228578BB-ABAF-4706-8CE7-93DE05FC3DF2 45 other data
    5A1689CC-7BDA-40A5-82FC-9650D58739AE 30 other data
    058F97B1-7AF7-4F32-801F-9AE3B284A9BB 80 other data
    07E106C4-0307-4ABE-AF02-9C734506BAC8 92 other data
    636CD13F-212B-4EAF-86C2-9EC3A8733DF6 40 other data
    23C8B3AE-77F2-45DC-BCE3-A0442EC0F8B3 52 other data
    E35160E1-FAD0-4CB3-9FCF-A2B2278EC4AA 61 other data
    758B3131-4329-46B2-A6D8-A2DC2A56ED8B 29 other data
    DA36FFC6-ADBD-4A38-B236-A3DFA3B7828B 94 other data
    98E8BC0F-4ABF-4CB5-BF9A-A4616CB51C7A 34 other data
    1A95A814-4D04-4C76-B378-A7E56CE4C685 82 other data
    D78D5EB7-9516-44E2-A196-A94205B35A8A 3 other data
    2E8B0887-CEB2-4487-B6F0-AE614E587488 59 other data
    67CA7AF8-18D4-45A3-9D53-AEB9A0EF97C8 99 other data
    A35AB10B-3F47-4184-A6FA-B8E38495A8C0 5 other data
    FFBCDA30-3134-49AB-8CD3-B9C512E50B0B 56 other data
    567EAD09-BF58-4027-9ABC-BCB62767DFE4 93 other data
    F0BAA52F-E5CA-42C0-A6D3-BFB81531CAFA 62 other data
    972F5A38-4F74-43E4-BCE6-C357E7D953D5 65 other data
    3258AB31-3A35-46BA-A053-C7686F3281BD 37 other data
    D3C00476-8582-4D61-B198-C7B5711BEF00 81 other data
    F521BBF6-770A-41D0-8FA5-C8E0684FD7D5 26 other data
    0BAE24EF-E7FE-42DD-BEA1-CA02A4024D5B 69 other data
    AA6BD0D8-EC8C-4B21-B21B-D429331C56B8 11 other data
    998FDC1B-7CAE-40DF-9AFA-DA3FD8CDCADB 89 other data
    4352A25D-A50B-42C3-A241-DC19BD7207D3 60 other data
    3F393EF3-3A26-4D36-BD00-DE9B9C63097E 38 other data
    B8A37EFF-5121-499E-AA22-E0ABE9C6293D 9 other data
    20B7C188-751F-4D90-8B9E-E1D4889A3795 1 other data
    CB2765CC-0FDA-423F-A5FE-E420A37BF2D5 72 other data
    2CE206A3-5481-4DC8-A725-E497611DD8BF 14 other data
    B1F04446-A9FB-4A64-8462-E5D02E4B5C71 17 other data
    89E50E9D-D1F9-488F-ADF0-EB13BD8B9D3F 83 other data
    3189B3B3-3892-4307-916F-EBC3E77573EE 44 other data
    3427F572-F535-4BF4-ADCC-EDC7E4C98E39 46 other data
    C93EDF08-35B4-430F-80B3-EE2605439DEE 2 other data
    15E0FBF2-18EF-41E4-938F-EFE7F0CA76F5 53 other data
    F665CAF8-C9BC-45D3-AD2B-F3E630FD8C5F 10 other data
    17C1CB7F-6C98-4A3D-9CD5-F883312B47C8 50 other data
    0191B624-C765-4C41-8FE3-FC0C07B82609 35 other data
    006D1851-23AC-4325-AD48-FE76F73F31BC 32 other data

    Αν προσεκτικά παρατηρήσουμε το αποτέλεσμα θα δούμε ότι τα rows έχουν ταξινομηθεί με βάση το id αλλά δεν έχουν μπει όμως το ένα κάτω από το άλλο καθώς αυτό φαίνεται από το πεδίο aa το οποίο μας δείχνει την σειρά εισαγωγής του row στο πίνακα.


    Αν μείνουμε μέχρι εδώ βγαίνει το συμπέρασμα ότι η χρήση του sql server data type σαν primary key σε ένα πίνακα είναι κακή, κάκιστη θα έλεγα επιλογή. Όμως δεν είναι έτσι τα πράγματα. Για αυτό ας κάνουμε ακόμα ένα πείραμα.


    Ας φτιάξουμε ένα ακόμα πίνακα όπως παρακάτω


    USE test
    GO

    CREATE TABLE t2
    (
    id UNIQUEIDENTIFIER NOT NULL PRIMARY KEY DEFAULT(NEWSEQUENTIALID()),
    aa INT IDENTITY NOT NULL,
    otherdata NCHAR(2000) DEFAULT (N'other data')
    )

    Αυτός είναι ακριβώς ίδιος με το προηγούμενο αλλά υπάρχει μια σημαντική διαφορά αντί να χρησιμοποιώ την NEWID() χρησιμοποιώ την NEWSEQUENTIALID() (είναι διαθέσιμη από την έκδοση του SQL Server 2005!!!).


    Aς επιβεβαιώσουμε ότι έχουμε τον index μας


    sp_helpindex t2
    GO

    index_name index_description index_keys
    ------------------------ ------------------------------------------------- ----------
    PK__t2__3213E83F0519C6AF clustered, unique, primary key located on PRIMARY id

    Και ας δούμε την δομή του index μας


    SELECT index_id, avg_fragmentation_in_percent, page_count
    FROM sys.dm_db_index_physical_stats(DB_ID('test'),OBJECT_ID('t2'),1,NULL,'LIMITED')
    GO

    index_id avg_fragmentation_in_percent page_count
    ----------- ---------------------------- --------------------
    1 0 0

    Ας βάλουμε τις δύο πρώτες γραμμές στο πίνακα μας


    INSERT INTO t2 DEFAULT VALUES
    INSERT INTO t2 DEFAULT VALUES
    GO

    Και ας δούμε την δομή του index μας


    SELECT index_id, avg_fragmentation_in_percent, page_count
    FROM sys.dm_db_index_physical_stats(DB_ID('test'),OBJECT_ID('t2'),1,NULL,'LIMITED')
    GO

    index_id avg_fragmentation_in_percent page_count
    ----------- ---------------------------- --------------------
    1 0 1

    Όλα όπως βλέπουμε είναι φυσιολογικά. Ας βάλουμε τις επόμενες δύο γραμμές στον πίνακα μας


    INSERT INTO t2 DEFAULT VALUES
    INSERT INTO t2 DEFAULT VALUES
    GO

    Και να δούμε τη δομή του index


    SELECT index_id, avg_fragmentation_in_percent, page_count
    FROM sys.dm_db_index_physical_stats(DB_ID('test'),OBJECT_ID('t2'),1,NULL,'LIMITED')
    GO

    index_id avg_fragmentation_in_percent page_count
    ----------- ---------------------------- --------------------
    1 50 2

    Όλα πάλι είναι φυσιλογικά έχω page_count ίσο με 2 που είναι και το φυσιλογικό μιας και έχω τέσσερεις γραμμές. Το avg_fragmentation_in_percent είναι υψηλό (50%) αλλά για να δούμε τι θα γίνει με αυτό αν βάλουμε στο πίνακα μας ακόμα 96 εγγραφές ώστε στο σύνολο να γίνουν 100?


    INSERT INTO t2 DEFAULT VALUES
    GO 96

    SELECT index_id, avg_fragmentation_in_percent, page_count
    FROM sys.dm_db_index_physical_stats(DB_ID('test'),OBJECT_ID('t2'),1,NULL,'LIMITED')
    GO

    index_id avg_fragmentation_in_percent page_count
    ----------- ---------------------------- --------------------
    1 4 50

    Όπως βλέπουμε αυτό έχει μειωθεί σημαντικά αλλά το περισσότερο σημαντικό είναι ότι ο αριθμός των σελίδων (page_count) έχει παραμείνει σωστός (50 σελίδες) δηλαδή 2 rows/page.


    Για να δούμε και τα δεδομένα μας στο πίνακα


    SELECT * FROM t2
    GO

    id aa otherdata
    ------------------------------------ ----------- ----------
    A6D5C824-7922-E011-A143-00155D020606 1 other data
    A7D5C824-7922-E011-A143-00155D020606 2 other data
    D439AA51-7922-E011-A143-00155D020606 3 other data
    D539AA51-7922-E011-A143-00155D020606 4 other data
    2C2FEF8C-7922-E011-A143-00155D020606 5 other data
    2D2FEF8C-7922-E011-A143-00155D020606 6 other data
    2E2FEF8C-7922-E011-A143-00155D020606 7 other data
    2F2FEF8C-7922-E011-A143-00155D020606 8 other data
    302FEF8C-7922-E011-A143-00155D020606 9 other data
    312FEF8C-7922-E011-A143-00155D020606 10 other data
    322FEF8C-7922-E011-A143-00155D020606 11 other data
    332FEF8C-7922-E011-A143-00155D020606 12 other data
    342FEF8C-7922-E011-A143-00155D020606 13 other data
    352FEF8C-7922-E011-A143-00155D020606 14 other data
    362FEF8C-7922-E011-A143-00155D020606 15 other data
    372FEF8C-7922-E011-A143-00155D020606 16 other data
    382FEF8C-7922-E011-A143-00155D020606 17 other data
    392FEF8C-7922-E011-A143-00155D020606 18 other data
    3A2FEF8C-7922-E011-A143-00155D020606 19 other data
    3B2FEF8C-7922-E011-A143-00155D020606 20 other data
    3C2FEF8C-7922-E011-A143-00155D020606 21 other data
    3D2FEF8C-7922-E011-A143-00155D020606 22 other data
    3E2FEF8C-7922-E011-A143-00155D020606 23 other data
    3F2FEF8C-7922-E011-A143-00155D020606 24 other data
    402FEF8C-7922-E011-A143-00155D020606 25 other data
    412FEF8C-7922-E011-A143-00155D020606 26 other data
    422FEF8C-7922-E011-A143-00155D020606 27 other data
    432FEF8C-7922-E011-A143-00155D020606 28 other data
    442FEF8C-7922-E011-A143-00155D020606 29 other data
    452FEF8C-7922-E011-A143-00155D020606 30 other data
    462FEF8C-7922-E011-A143-00155D020606 31 other data
    472FEF8C-7922-E011-A143-00155D020606 32 other data
    482FEF8C-7922-E011-A143-00155D020606 33 other data
    492FEF8C-7922-E011-A143-00155D020606 34 other data
    4A2FEF8C-7922-E011-A143-00155D020606 35 other data
    4B2FEF8C-7922-E011-A143-00155D020606 36 other data
    4C2FEF8C-7922-E011-A143-00155D020606 37 other data
    4D2FEF8C-7922-E011-A143-00155D020606 38 other data
    4E2FEF8C-7922-E011-A143-00155D020606 39 other data
    4F2FEF8C-7922-E011-A143-00155D020606 40 other data
    502FEF8C-7922-E011-A143-00155D020606 41 other data
    512FEF8C-7922-E011-A143-00155D020606 42 other data
    522FEF8C-7922-E011-A143-00155D020606 43 other data
    532FEF8C-7922-E011-A143-00155D020606 44 other data
    542FEF8C-7922-E011-A143-00155D020606 45 other data
    552FEF8C-7922-E011-A143-00155D020606 46 other data
    562FEF8C-7922-E011-A143-00155D020606 47 other data
    572FEF8C-7922-E011-A143-00155D020606 48 other data
    582FEF8C-7922-E011-A143-00155D020606 49 other data
    592FEF8C-7922-E011-A143-00155D020606 50 other data
    5A2FEF8C-7922-E011-A143-00155D020606 51 other data
    5B2FEF8C-7922-E011-A143-00155D020606 52 other data
    5C2FEF8C-7922-E011-A143-00155D020606 53 other data
    5D2FEF8C-7922-E011-A143-00155D020606 54 other data
    5E2FEF8C-7922-E011-A143-00155D020606 55 other data
    5F2FEF8C-7922-E011-A143-00155D020606 56 other data
    602FEF8C-7922-E011-A143-00155D020606 57 other data
    612FEF8C-7922-E011-A143-00155D020606 58 other data
    622FEF8C-7922-E011-A143-00155D020606 59 other data
    632FEF8C-7922-E011-A143-00155D020606 60 other data
    642FEF8C-7922-E011-A143-00155D020606 61 other data
    652FEF8C-7922-E011-A143-00155D020606 62 other data
    662FEF8C-7922-E011-A143-00155D020606 63 other data
    672FEF8C-7922-E011-A143-00155D020606 64 other data
    682FEF8C-7922-E011-A143-00155D020606 65 other data
    692FEF8C-7922-E011-A143-00155D020606 66 other data
    6A2FEF8C-7922-E011-A143-00155D020606 67 other data
    6B2FEF8C-7922-E011-A143-00155D020606 68 other data
    6C2FEF8C-7922-E011-A143-00155D020606 69 other data
    6D2FEF8C-7922-E011-A143-00155D020606 70 other data
    6E2FEF8C-7922-E011-A143-00155D020606 71 other data
    6F2FEF8C-7922-E011-A143-00155D020606 72 other data
    702FEF8C-7922-E011-A143-00155D020606 73 other data
    712FEF8C-7922-E011-A143-00155D020606 74 other data
    722FEF8C-7922-E011-A143-00155D020606 75 other data
    732FEF8C-7922-E011-A143-00155D020606 76 other data
    742FEF8C-7922-E011-A143-00155D020606 77 other data
    752FEF8C-7922-E011-A143-00155D020606 78 other data
    762FEF8C-7922-E011-A143-00155D020606 79 other data
    772FEF8C-7922-E011-A143-00155D020606 80 other data
    782FEF8C-7922-E011-A143-00155D020606 81 other data
    792FEF8C-7922-E011-A143-00155D020606 82 other data
    7A2FEF8C-7922-E011-A143-00155D020606 83 other data
    7B2FEF8C-7922-E011-A143-00155D020606 84 other data
    7C2FEF8C-7922-E011-A143-00155D020606 85 other data
    7D2FEF8C-7922-E011-A143-00155D020606 86 other data
    7E2FEF8C-7922-E011-A143-00155D020606 87 other data
    7F2FEF8C-7922-E011-A143-00155D020606 88 other data
    802FEF8C-7922-E011-A143-00155D020606 89 other data
    812FEF8C-7922-E011-A143-00155D020606 90 other data
    822FEF8C-7922-E011-A143-00155D020606 91 other data
    832FEF8C-7922-E011-A143-00155D020606 92 other data
    842FEF8C-7922-E011-A143-00155D020606 93 other data
    852FEF8C-7922-E011-A143-00155D020606 94 other data
    862FEF8C-7922-E011-A143-00155D020606 95 other data
    872FEF8C-7922-E011-A143-00155D020606 96 other data
    882FEF8C-7922-E011-A143-00155D020606 97 other data
    892FEF8C-7922-E011-A143-00155D020606 98 other data
    8A2FEF8C-7922-E011-A143-00155D020606 99 other data
    8B2FEF8C-7922-E011-A143-00155D020606 100 other data

    Αν παρατηρήσουμε τα δεδομένα θα δούμε ότι αυτά είναι ταξινομημένα με το id αλλά έχουμε σωστή και την σειρά καταχωρήσης, δηλαδή το πρώτο record μπήκε πρώτο και το id στην ταξινόμηση είναι πρώτο το δεύτερο το τρίτο, …, το εκατοστό.


    Όμως αυτό δεν είναι ο λόγος που τώρα είναι όλα καλά.


    Όπως γνωρίζουμε στο leaf level του index είναι ο index ταξινομημένος. Επίσης επειδή ο index μας είναι clustered στην ουσία το leaf level και τα data pages είναι το ίδιο και το αυτό.


    Στην πρώτη περίπτωση με την χρήση της NEWID(), η οποία κάθε φορά επιστρέφει ένα τυχαίο GUID, σαν default value έχω το εξής φαινόμενο


    Με το που βάζω το πρώτο record αυτό μπαίνει στη σελίδα πχ 1 το δεύτερο μπαίνει και αυτό σε αυτή τη σελίδα, έτσι η σελίδα μας είναι ως εξής


    id aa otherdata
    ------------------------------------ ----------- ----------------------
    20B7C188-751F-4D90-8B9E-E1D4889A3795 1 other data
    C93EDF08-35B4-430F-80B3-EE2605439DEE 2 other data

    Όταν πάμε να βάλουμε το τρίτο record το οποίο είναι το παρακάτω


    D78D5EB7-9516-44E2-A196-A94205B35A8A 3 other data

    Το οποίο πρέπει να μπει πριν από το πρώτο έτσι πρέπει να γίνει page split στην πρώτη σελίδα και επειδή δεν έχουμε ορίζει κάποιο fillfactor στον index (default ίσο 0 που σημαίνει ότι αφήνει χώρο ώστε να μπορεί να μπει ακόμα μία έγγραφή) δημιουργούνται τρεις σελίδες όπου η κάθε μια έχει από ένα record


    Page 1
    id aa otherdata
    ------------------------------------ ----------- ------------
    D78D5EB7-9516-44E2-A196-A94205B35A8A 3 other data
    Page 2
    id aa otherdata
    ------------------------------------ ----------- ------------
    20B7C188-751F-4D90-8B9E-E1D4889A3795 1 other data
    Page 3
    id aa otherdata
    ------------------------------------ ----------- ------------
    C93EDF08-35B4-430F-80B3-EE2605439DEE 2 other data

    Βάζοντας και την τέταρτη εγγραφή η δομή του index είναι η παρακάτω


    Page 1
    id aa otherdata
    ------------------------------------ ----------- ---------------
    D539AA51-7922-E011-A143-00155D020606 4 other data
    D78D5EB7-9516-44E2-A196-A94205B35A8A 3 other data
    Page 2
    id aa otherdata
    ------------------------------------ ----------- ---------------
    20B7C188-751F-4D90-8B9E-E1D4889A3795 1 other data
    Page 3
    id aa otherdata
    ------------------------------------ ----------- --------------
    C93EDF08-35B4-430F-80B3-EE2605439DEE 2 other data

    Εξου και το αποτέλεσμα που πήραμε και είδαμε ότι είχαμε 3 σελίδες. Αυτό συμβαίνει σε όλες τις άλλες εγγραφές με την ίδια λογική και έτσι έχουμε το τελικό αποτέλεσμα με το μεγάλο fragmentation και τις 65 σελίδες.


    Στο δεύτερο πίνακα όμως δεν συμβαίνει κάτι τέτοιο καθώς η NEWSEQUENTIALID() δίνει σειριακά GUIDs. Έτσι δεν έχουμε ούτε ουσιαστικά μεγάλο fragmentation ούτε παραπάνω αριθμό σελίδων.


    Συμπέρασμα


    Το πεδίο uniqueidentifier δεν έχει απολύτως κανένα πρόβλημα να είναι primary key σε ένα πίνακα (κατ’ επέκταση θα έλεγα ότι δεν έχει κανένα πρόβλημα να είναι clustered key) ΑΡΚΕΙ να γεμίζει με την NEWSEQUENTIALID() function και όχι με την NEWID() function.
  8. antonch
    Όσοι έχουν στο περιβάλλον τους έστω και ένα AlwaysOn Availability Group σίγουρα θα έχουν αναρωτηθεί πως να εκτελούν τις διάφορες εργασίες που θέλουν πάνω σε μια database που είναι σε Availability Group.
     
    Υπάρχουν αρκετοί τρόποι, από τον απλούστερο μέχρι τον ποιό advanced και φυσικά αυτό που προτείνω για αυτό το θέμα.
     
    Αντί να γράψω ένα μακροσκελές άρθρο για το συγκεκριμένο θέμα, επέλεξα να παρουσιάσω το θέμα με ένα web cast και στο post αυτό να σας δώσω το κώδικα που χρησιμοποίησα στο web cast.
     
    http://sqlschool.gr/blog/running-sql-server-agent-job-on-alwayson-availability-groups-1046.aspx

  9. antonch
    Ένα από τα services του SQL Server είναι τα Reporting Services τα οποία παρέχουν ένα εξαιρετικά ευέλικτο τρόπο να μεταδίδεται η πληροφορία στους τελικούς χρήστες. Η χρήση τους από τις εταιρίες και τους οργανισμούς έχει αυξηθεί σε υπερθετικό βαθμό και κανείς θα βρει μεγάλο αριθμό από reports να εκτελούνται καθημερινά στις υποδομές αυτών των εταιρειών.
    Είναι φυσικό κάποια στιγμή να χρειαζόμαστε
    να παρακολουθήσουμε ποια είναι αυτά που χρησιμοποιούνται,
    πόσο συχνά χρησιμοποιούνται,
    πόσο χρόνο χρειάζονται για να εκτελεστούν
    ποιοι είναι οι χρήστες και ποια reports εκτελούν.

    Οι παραπάνω λόγοι είναι μερικοί και κάνουν επιτακτική την ανάγκη να έχεις αρχικά ενεργοποιημένο το auditing στα SSRS και στην συνέχεια να έχεις μια διαδικασία που να κάνει την ζωή σου ευκολότερη.
    Πέρα όμως από αυτούς το τελευταίο διάστημα είχα αρκετές ανάγκες στον εργασιακό μου χώρο για auditing από επιθεώρηση πάνω στoυς SSRS servers που έχω αλλά και πολλές ερωτήσεις από τους μαθητές μου στα πρόσφατα BI σεμινάρια που έκανα.
     
    http://www.sqlschool.gr/blog/auditing-reports-execution-in-ssrs-1038.aspx
  10. antonch
    Όπως γνωρίζουμε ο SQL Server διαβάζει τις σελίδες από το δίσκο και τις βάζει στην buffer cache. To πόσο χρόνο (σε δευτερόλεπτα) αυτές μπορούν να μείνουν κατά μέσο όρο στην buffer cache χωρίς να ζητηθούν/χρησιμοποιηθούν μπορούμε να το δούμε από τον SQL Server\Buffer Manager\Page Life Expectancy performance counter. O μετρητής αυτός  σύμφωνα με τα recommendations πρέπει να είναι πάνω από 300 sec για να έχουμε ένα καλό PLE. Φυσικά σε σύστήματα που έχουν προβλήματα με την μνήμη αυτός θα είναι μικρότερος καθώς οι σελίδες θα κατεβαίνουν γρηγορότερα στον δίσκο και αυτό δεν μας αρέσει. Αντίθετα σε συστήματα με άπλετη μνήμη θα έχει μεγαλύτερη τιμή και αυτό μας αρέσει.
  11. antonch
    Θα επανέλθω στο συγκεκριμένο θέμα μετά από απαίτηση συναδέλφων που είδαν την παρουσίαση που έχω κάνει για το 34ο dotNetZone Event και την οποία θα βρείτε εδώ. Υπήρχαν κάποιες απορίες και ερωτήσεις που φιλοδοξώ να τις απαντήσω με το παρόν post μου. Ας πάρουμε λοιπόν τα πράγματα από την αρχή μιας και διακαείς πόθος όλων μας είναι να έχουμε την τέλεια απόδοση στα queries μας δηλαδή όλα να εκτελούνται σε χρόνο μηδέν και να επιστρέφουν δισεκατομμύρια εγγραφές (καλά πλάκα κάνω ).
    Όμως για όλα αυτά υπάρχουν κάποιες προϋποθέσεις όπως:
    1. Tuning queries κάνει αυτός που είναι database developer ΚΑΙ όχι ένας developer γενικά. Δεν σημαίνει ότι όποιος ξέρει να γράφει C++, C#, VB, Java ξέρει και τι σημαίνει query execution. Εξάλλου όλοι πλέον οι developers δεν θέλουν να γράφουν queries μέσα από τις εφαρμογές τους (συμφωνώ και εγώ με αυτό) αλλά να χρησιμοποιούν εργαλεία όπως LINQ to SQL, Entity Framework κ.α σωστά;
    2. Αυτός που θα αναλάβει να κάνει την συγκεκριμένη εργασία δεν φτάνει να ξέρει πως τα queries, οι indexes δουλεύουν. Πρέπει να ξέρει την γενικότερη αρχιτεκτονική του SQL Server καθώς επίσης και πως ΟΛΑ του τα κομμάτια παντρεύονται μεταξύ τους.
    3. Θα πρέπει να είναι βαθύς γνώστης του σχεδιασμού της βάσης στην οποία θα κάνει tuning το ή τα queries. Όπως επίσης θα πρέπει να ξέρει την ποιότητα και την ποσότητα των δεδομένων τα οποία θα χρησιμοποιήσει για να βελτιώσει την απόδοση στο query του.
    Αν εκπληρώνονται οι παραπάνω προϋποθέσεις θα πρέπει να γνωρίζει τα εξής:
    1. Τι είναι το Query Performance και πως αυτό μετριέται.
    Βασική αρχή στην βελτίωση της απόδοσης ενός query είναι να βρω το τι φταίει και για να γίνει αυτό πρέπει να έχω μια μονάδα μέτρησης για να μπορέσω να εξάγω ένα συμπέρασμα το οποίο θα μου δείξει το δρόμο που θα με οδηγήσει στην επίλυση του προβλήματος. Στον SQL Server έχουμε τρεις βασικές μονάδες μέτρησης αυτές είναι query cost, page reads, query execution time.
    Για να τις δούμε όμως λίγο καλύτερα μήπως και βγάλουμε κάποιο συμπέρασμα
    Query Cost: Συνήθως είναι η βασική μας μονάδα μέτρησης όταν θέλουμε να συγκρίνουμε την απόδοση ενός ερωτήματος. Είναι μια εσωτερική μετρική του SQL Server η οποία λαμβάνει υπόψη της το πόσο CPU και Ι/Ο χρησιμοποιεί το ερώτημα μας. 'Οσο μικρότερη είναι η τιμή του (σε %) τόσο καλύτερη είναι και η απόδοση στο ερώτημα μας. Αλλά δεν λαμβάνει υπόψη θέματα που αφορούν πχ Waiting Locks ή Resource Contention. Επίσης όταν μέσα στο query μας υπάρχουν user defined functions (UDFs) ή Common Language Runtime (CLR) ρουτίνες αυτές δεν λαμβάνονται υπόψη στον υπολογισμό του query cost και αυτός είναι και ο λόγος που ονομάζεται estimated query cost. Αυτό το βλέπουμε από το Εxecution Plan

    Page Reads: Είναι ο αριθμός των σελίδων (8KB/page) που το Storage Engine του SQL Server διαβάζει κατά την διάρκεια εκτέλεσης του query. Αυτό το βλέπουμε εάν στο session στο οποίο εκτελούμε το query μας και πριν την εκτέλεση τους εκτελέσουμε την SET STATISTICS IO ON. To αποτέλεσμα θα είναι το παρακάτω

    Από το output καταβαίνουμε ότι έχει κάνει logical reads 27 pages δηλαδή τις διάβασε από την μνήμη (buffer cache, δείτε παλαιότερο post μου). Σε αυτές συμπεριλαμβάνονται και τα data και τα index pages. Με τα physical read και read-ahead reads βλέπουμε πόσα pages διάβασε από τον δίσκο και τις ανέβασε στην buffer cache. Με το Scan count βλέπουμε το πόσες φορές έκανε scan τις σελίδες αυτές για να βγάλει το αποτέλεσμα (εάν δούμε πάνω από 1 σημαίνει ότι κάτι δεν πάει καλά δηλαδή ξαναδιαβάζει τα ίδια data). Με το lob physical reads και lob read-ahead reads βλεπουμε τον αριθμό των σελίδων που χρειάσθηκε να διαβάζει για να μας φέρει δεδομένα τύπου varchar(max), nvarchar(max), varbinary(max), text, image, xml. Αλλά δεν συμπεριλαμβάνεται το CPU Cost όπως επίσης δεν συμμετέχουν οι UDFs και CLR ρουτίνες.
    Query Execution Time: Αυτή είναι η ποιο επηρεαζόμενη μετρική, και αυτό διότι μπορεί να έχω locks, resource contention στον server κατά την διάρκεια της εκτέλεσης του query. Αυτή την μετρική την βλέπουμε εάν στο session στο οποίο εκτελούμε το query μας και πριν την εκτέλεση τους εκτελέσουμε την SET STATISTICS ΤΙΜΕ ON. To αποτέλεσμα θα είναι το παρακάτω

    Από το output βλέπουμε τους χρόνους που χρειάσθηκε για την προεργασία (parse and compile) και τον χρόνο εκτέλεσης.
    2. Να γνωρίζει πως εκτελείτε λογικά ένα query.
    Έχω ήδη γράψει για αυτό σε παλαιότερο άρθρο μου το οποίο μπορείτε να βρείτε εδώ.
    3. Να μπορεί να διαβάσει και να ερμηνεύσει το Graphical ή μη Execution Plan

    Για όσους δεν ξέρουν πώς να το κάνουν αυτό θα του πω απλά ότι διαβάζεται από δεξιά προς τα αριστερά και κάθε σύμβολο σημαίνει μια διαφορετική διαδικασία εκτέλεσης. Υπάρχουν αρκετά τέτοια σύμβολα, τα οποία και μπορείτε να τα βρείτε όλα μέσα στο SQL Server Books Online (BOL). Θα σας δώσω όμως μερικά hints.
    a. Εάν βλέπετε παχιά βελάκια τα οποία συνδέουν το ένα task με κάποιο άλλο, αυτό σημαίνει ότι μεγάλος όγκος δεδομένων μεταφέρεται από task σε task. Γενικότερα θέλουμε να βλέπουμε λεπτά βελάκια.
    b. Εάν στο query μας έχουμε join ή group by και βλέπουμε ότι αυτό υλοποιείτε με Hash Match (Inner Join) αυτό συχνά σημαίνει ότι δεν υπάρχει ο κατάλληλος index ώστε να γίνει γρηγορότερα η διαδικασία πχ. Index στο foreign key στο join ή στο πεδίο με το οποίο κάνουμε group by.
    c. Εάν βλέπουμε το σύμβολο του Sort και το ποσοστό του κόστους που έχει είναι υψηλό, τότε καλό είναι να εξετάζουμε την περίπτωση να γίνει κάποιος Index στo πεδίο αυτό ώστε να απαλοιφή η διαδικασία Sort.
    d. Τα μεγάλα excecution plans δεν σημαίνει ότι είναι κακά αλλά καλό είναι να έχω μικρά execution plans και αυτό διότι έχω λιγότερα βήματα εκτέλεσης στο query μου.
    e. Επίσης όταν στον execution plan δω σε κάποιο task Table Scan ή Clustered Index Scan σημαίνει ότι δεν υπάρχει Index τέτοιος ώστε να γίνει καλύτερη υλοποίηση εκτέλεσης. (Σημείωση: Αν ο πίνακας σας έχει δεδομένα που είναι κάτω από 8ΜΒ τότε ακόμα και αν υπάρχει index ο SQL Server επιλέγει την συγκεκριμένη υλοποίηση διότι έχει λιγότερο κόστος σε resources και χρόνο).
    Βέβαια υπάρχουν και άλλα που πρέπει να γνωρίζουμε αλλά αυτά είναι τα βασικά. Ας αρχίσουμε με αυτά και επιφυλάσσομαι στο μέλλον να γράψω και για τα άλλα
  12. antonch
    Δεν ξέρω αν σας αρέσει να ανεβαίνει η αδρεναλίνη σας ύψη αλλά προσωπικά μου αρέσει, αρκεί να είναι όπως αυτή η περίπτωση :-)
    Είμαι λοιπόν από το πρωί πάνω σε παραγωγικό server που έχει κομβικό Internet-ικο τμήμα του οργανισμού που εργάζομαι. Παλεύω να κάνω partitioning το πίνακα ημερολογίου συναλλαγών και είναι τεράστιος… Πρέπει να κάνω απλές και σίγουρες κινήσεις που δεν θα επηρεάσουν σε καμία περίπτωση την απόδοση του συστήματος.
     
    http://www.sqlschool.gr/blog/do-not-panic-when-an-error-occurred-just-learn-to-read-the-logs-1011.aspx
  13. antonch
    Τα User Defined Functions (UDF) είναι γνωστά στους περισσότερους. Η χρήση τους είναι μεγάλη αλλά αρκετές φορές χρησιμοποιούνται λάθος κυρίως λόγο της άγνοιας που υπάρχει γύρω από την εκτέλεση ενός UDF και ιδιαίτερα όταν αυτό εμπλέκεται μέσα σε ένα query.
    Οι περισσότεροι συγκρίνοντας το execution plan ενός query χωρίς UDF και με UDF βλέπουν ότι το query cost είναι μικρότερο σε αυτό που χρησιμοποιεί το UDF και αμέσως θεωρούν ότι αυτό είναι και το καλύτερο.
    Για ακόμα μια φορά θα τονίσω ότι δεν υπάρχει έτσι απλά καλό ή καλύτερο όλα είναι εξάρτηση από πολλούς παράγοντες όπως την ποσότητα και ποιότητα των δεδομένων των indexes κλπ
     
    http://www.sqlschool.gr/blog/sql-server-2016-new-features-the-sys-dm_exec_function_stats-dmv-1057.aspx
  14. antonch
    Στα μάθηματα μου συχνά αναφέρω (ιδιαίτερα όμως όταν κάνω μάθημα σχετικό με τα joins) ότι εκτός από τα γνωστά inner, full, cross, self και left / right outer joins υπάρχουν και τα semi joins και anti-joins.
    Η πρώτη αντίδραση από το ακροατήριο μου είναι αυτή που μπορεί να έχει κάποιος όταν το χτυπάει το ρεύμα. Το καταλαβαίνω αμέσως από το γεγονός ότι ξαφνικά αποκτώ το 110% της προσοχής τους.
    Αυτό συμβαίνει πάντα για αυτό και γράφτηκε το συγκεκριμένο post.
    Αρχικά θα πω ότι είμαι σίγουρος ότι όλοι έχετε χρησιμοποιήσει semi ή anti joins απλά δεν ξέρετε ότι τα λένε έτσι.
     
    http://www.sqlschool.gr/blog/what-is-semi-join-and-anti-join-1008.aspx
  15. antonch
    Πριν προχωρήσω στο αντικείμενο που θέλω να παρουσιάσω θα ήθελα να περιγράψω κάποιες γνωστές έννοιες που θεωρώ ότι είναι καλό να επαναληθούν, μιας και η επανάληψη είναι η μητέρα της μάθησης όπως έλεγαν οι πρόγονοι μας.
    Κάθε application ( και με αυτό τον όρο συμπεριλαμβάνω και τα services ) μπορεί να εκτελεσθεί πολλές φορές. Κάθε εκτέλεση του application είναι ένα instance.
    Κάθε instance που τρέχει είναι ένα process, το οποίο στην ουσία είναι ένας container από ένα ή πολλα threads και τα οποία είναι προγραμματισμένα να κάνουν χρήση του επεξεργαστή για ένα συγκεκριμένο χρονικό διάστημα (time slice) το οποίο είναι οριζόμενο από το λειτουργικό σύστημα.
    Τα threads επιτρέπουν στην εφαρμογή να χρησιμοποιεί με το καλύτερο δυνατό τρόπο τον επεξεργαστή, είτε αυτός είναι ένας, είτε περισσότεροι.
    Τα Windows είναι ένα preemptive (με προτίμηση λόγω κατοχής) multitasking (πολυεπεξεργαστικό) λειτουργικό σύστημα.
    Αυτό σημαίνει ότι σε κάθε περίπτωση που τρέχουμε ένα application το λειτουργικό σύστημα δίνει ένα χρονικό διάστημα στο thread για να τρέξει. Όταν αυτό το χρονικό διάστημα περάσει ή κάποιο άλλο thread με προτεραιότητα μεγαλύτερη (high-priority) από αυτό που τρέχει την δεδομένη χρονική στιγμή, θέλει να τρέξει, τότε το λειτουργικό σύστημα αποθηκεύει την contextual information (συναφή πληροφορία) του τρέχοντος thread, το αντικαθιστά ή το σταματά και φορτώνει την contextual information από κάποιο άλλο thread ώστε αυτό να τρέξει με την σειρά του για το χρονικό διάστημα που του αναλογεί. Αυτό είναι το γνωστό σαν context switching και όλη η διαδικασία σαν Windows preemptive scheduling.
    User Mode Scheduler (UMS)
    Από την έκδοση 6.5 ο SQL Server χρησιμοποιεί ιδανικά το Windows scheduling. Όμως αυτό είναι γενικού σκοπού και για όλα τα application και όπως είναι φυσικό περιορίζει τo scalability (κλιμάκωση) που ο SQL Server προσπαθεί να επιτύχει. Η προσέγγιση του Windows preemptive sheduling έχει σαν αποτέλεσμα το context switching το οποίο είναι ακριβό σαν διαδικασία μιας και εναλλάσετε μεταξύ του user και του kernel mode του επεξεργαστή.
    Έτσι ένα process όπως του SQL Server το οποίο δημιουργεί και χρησιμοποιεί πολλά threads η εκτεταμένη χρήση του context switching έχει αρνητική επίδραση στο γενικότερο performance αλλά και το scalability του. Αυτός είναι και ο λόγος για τον οποίο η ομάδα ανάπτυξης του SQL Server αποφάσισε ότι ο ίδιος o SQL Server πρεπει να κάνει δικό του scheduling. Έτσι ο SQL Server γνωρίζει και ορίζει καλύτερα τις δικιές του ανάγκες για scheduling από ότι το λειτουργικό. Ακόμα θα πρέπει να τονίσω ότι ο SQL Server κάνει καλύτερη υλοποίηση όσον αφορά το thread scheduling και αποφεύγει σχεδόν πάντα το context switching.
    Από την έκδοση 7.0 του SQL Server παρουσιάστηκε η έννοια του User Mode Scheduler (UMS) o οποίος είναι ένα thin layer πάνω από το λειτουργικό σύστημα με πρωταρχικό σκόπο να βελτιστοποιήσει το thread management του SQL Server, ελαχιστοποιόντας το context switching και προσπαθώντας να κρατήσει όσο το δυνατόν τα SQL Server schedules στο user mode.
    Έτσι στο binn folder της εγκατάστασης του SQL Server 7.0 θα βρείτε ένα αρχείο to ums.dll, το οποίο είναι η υλοποίηση του UMS.
    Εδώ θα πρέπει να τονίσω ότι δεν θα γινόταν και τίποτα σπουδαίο αν το UMS εκτός από το scheduling δεν αφαιρούσε από το λειτουργικό χαρακτηριστικά που είναι system dependent όπως τα fibers και asynchronous Ι/Ο.
    Αλλά για να δούμε πως το UMS καταφέρνει να επιβληθεί πάνω στο Windows scheduling;
    UMS έχει την "εμπιστοσύνη" των Windows ότι όλα τα threads εκτός από αυτό που το ίδιο θέλει δεν είναι εφαρμόσιμο ("not viable"). Εαν ένα thread βρήσκεται σε ένα infinite wait state, αυτό σημαίνει ότι εάν ένα thread καλέσει την WaitForSingleObject και περάσει INFINITE σαν timeout value, τα Windows εκλαμβάνουν το thread αυτό σαν "not viable" για scheduling και το αγνοούν. Ο μόνος τρόπος για να ξυπνήσει ένα τέτοιο thread είναι στείλω ένα σήμα στο thread's event object.
    Στο λειτουργικό σύστημα φαίνεται μόνο ένα SQL Server thread ανα επεξεργαστή να είναι ενεργό κάθε φορά. Έτσι ακόμα και εάν έχουμε εκατοντάδες worker threads σε μια δεδομένη χρονική στιγμή, μόνο ένα από αυτά για κάθε επεξεργαστή εμφανίζετε στα Windows να κάνει κάτι.
    Ο Windows scheduler είναι όπως είπαμε preemptive. Από την άλλη μεριά ο UMS προσπαθεί και επιτυχώς ακολουθεί ένα cooperative model και είναι ένας non-preemptive scheduler.
    Ο UMS βασίζεται σε αυτό που λεμε "on threads to yield voluntarily" το οποίο σε ελεύθερη μετάφραση θα το πω "στην εθελούσια παραίτηση του thread".
    O UMS χρησιμοποιεί την προσέγγιση αυτή ώστε να χρησιμοποιήσει τον Windows kernel μόνο όταν είναι πραγματικά απαραίτητος.
    Το UMS cooperative scheduling απαίτησε να γραφτεί τέτοιος κώδικας από το SQL Server development team που θα μου επιτραπεί να τον χαρακτηρίσω λίρα εκατό. Αλλά αυτό άξιζε τον κόπο μιας καταφέρνει να είναι πιο αποτελεσματικο από του λειτουργικού αφού είναι κομένο και ραμένο στα μέτρα του SQL Server.
    Έτσι όταν ένα thread υποχωρεί είτε επειδή τελειώσε το task του είτε επειδή εκτέλεσε κώδικα που αφορούσε κλήση σε μια απο τις yield functions του UMS, αυτός ελέγχει την λίστα με τα threads τα οποία είναι έτοιμα να τρέξουν και λέει στο πρώτο διαθέσιμο οτι μπορεί να τρέξει. Έτσι με αυτό τον τρόπο όλα γίνονται στο user mode και αποφεύγεται το switching στο kernel mode.
    Όταν ο SQL Server ξεκινά, για κάθε επεξεργαστή δημιουργήτε ένας UMS. Κάθε UMS συντηρεί τα ακόλουθα 5 πράγματα:
    Worker List Η λίστα αυτή είναι τα διαθέσιμα threads ή fibers. Ο αριθμός των διαθέσιμων threads ορίζεται από αυτόν που έχουμε ορίσει στο max worker threads option κάνοντας χρήση της sp_configure. Εαν για παράδειγμα έχουμε ορίσει το max worker threads σε 255 σε ένα σύστημα με 8 επεξεργαστές , κάθε επεξεργαστής και κατα συνέπεια κάθε UMS μπορεί να έχει 32 workers (UMS workers). Κάθε ένας UMS worker μπορεί να υποστηρίξει πολλαπλούς χρήστες ή SPIDs.
    Runnable List Η λίστα αυτή δείχνει τους UMS workers που είναι έτοιμοι να εκτελέσουν κάποιο υπαρκτό request. Έτσι όταν ένας UMS worker υποχωρήσει σαν αποτέλεσμα της διαδικασίας υποχώρησης που περιέγραψα πιο πάνω ελεγχετε η λίστα και δίνει σήμα στο επόμενο UMS worker να προχωρήσει με αυτό που έχει να κάνει.
    Resource Waiter List Όταν ένας UMS worker ζητήσει κάποιο resource το οποίο είναι δεσμευμένο από κάποιον άλλον UMS worker μπαίνει στην συγκεκριμένη λίστα οικιοθελώς και μπαίνει σε infinite wait state. Όταν ο UMS worker που έχει το δεσμευμένο resource είναι έτοιμος να το αποδεσμεύσει, κοιτάζει την συγκεκριμένη λίστα και βρήσκει τον πρώτο διαθέσιμο UMS worker που ενδιαφέρεται για το συγκεκριμένο resource τον μεταφέρει στην Runnable List και «ξυπνάει» τον πρώτο διαθέσιμο UMS worker της Runnable List.
    I/O List Είναι η λίστα με τα outstanding asynchronous I/O requests.
    Timer List Είναι η λίστα με τα UMS timer request η οποία στην ουσία λέει πόσο χρόνο θα περιμένω ένα resource μέχρι να γίνει time out.
    Στον SQL Server 7.0 & 2000, μπορείς να χρησιμοποιήσεις τo DBCC SQLPERF(umsstats) undocumented statement για να κάνεις monitor κάθε visible UMS στο σύστημα σου.
    Στον SQL Server 2005 & 2008, μπορείς να χρησιμοποιήσεις την sys.dm_os_schedulers dynamic management view (DMV) για να δεις statistics για τους visible αλλά και για τους hidden schedulers στο σύστημα σου. Για περισσότερες λεπτομέρειες σχετικά με την DMV δείτε στο books online του SQL Server
    Τί είναι το SQLOS;
    Όπως είδαμε το UMS thread management έδωσε την δυνατότητα στον SQL Server να είναι self-managed και εύκολα μπορεί να γίνει scale εάν ένας νέος επεξεργαστής προστεθεί στο σύστημα μας. Από την έκδοση 7.0 του SQL Server όπου για πρώτη φορά παρουσιάσθηκε ο UMS πολλά εσωτερικά engines όπως , ο memory manager, το storage engine, και το relational engine έγιναν upgrade με built-in adaptive algorithms και self-tuning capabilities. Αυτό έδωσε την δυνατότητα στον SQL Server 7.0 όταν βγήκε στην αγορά το 1998 να είναι το πρώτο enterprise database engine το οποίο ήταν δυνατόν να κάνει automatic configuration και dynamic self-tuning.
    Στον SQL Server 2005 η Microsoft πήγε το self-managing και το self-tuning σε ακόμα υψηλότερο επίπεδο. Αυτό είχε σαν αποτέλεσμα ένα νέο component το SQLOS να γεννηθεί..
    Το SQLOS είναι ένα layer το οποίο κάθεται πάνω από το λειτουργικό και είναι υπεύθυνο για την διαχείριση των πόρων του λειτουργικού συστήματος που είναι όμως για τον SQL Server.
    To SQLOS δίνει στον SQL Server την δυνατότητα να εξυπηρετεί τις εσωτερικές του ανάγκες για πόρους καλύτερα και σε μεγαλύτερο εύρος.
    Κάθε component μέσα στο SQLOS είναι έτσι φτιαχμένο ώστε να κάνει κάποιο συγκεκριμένη εργασία καθώς επίσης να συνεργάζεται ομαλά με όλα τα άλλα componets του SQLOS, ώστε να δίνεται ένα large-scale performance προσαρμοζόμενο φυσικά σε διαφορετικά hardware configurations, όπως 32-bit, 64-bit, x64, dual core chips, και large memory addressability.
    Με άλλα λόγια δεν χρειάζεται να γίνουν αλλαγές στο SQL Server configuration ώστε να γίνει το SQLOS adapt to hardware resources ενώ από την άλλη παρέχει ένα άνευ προηγουμένου scalability.
    Εδώ θα πρέπει να επισημάνω ότι στον SQL Server 2005 δεν υπάρχει το ums.dll μιας και αυτό είναι ένα από τα components του SQLOS και αναφέρεται πλέον σαν "Non Preemptive Scheduler"

    Όπως βλέπουμε στην παραπάνω είκόνα υπάρχουν δύο μεγάλα components του SQLOS το non-preemptive scheduling (το γνωστό UMS) και το memory management. Υπάρχουν όμως και άλλα components όπως το resource monitor, ο exception handler, και τα hosting subsystems. Το SQLOS παντρεύει όλα αυτά τα system components και δίνει την δυνατότητα ένος συνεκτικού API που δίνει την δυνατότητα στο SQL Server development team εύκολα να εκμεταλλευθεί τα χαρακτηριστικά του hardware και του λειτουργικού συστήματος.
    Non-Preemptive Scheduling
    Το non-preemptive scheduling component του SQLOS χρησιμοποιήτε για να κάνει scheduling και synchronizing τα concurrent tasks χωρίς να χρειασθεί να κάνει κλήση στο Windows kernel. Είναι υπεύθυνο για το ιδανικό scheduling των Windows threads ή των fibers.
    Ο scheduler είναι υπέθυνος για την διαχείριση των scheduling process και διασφαλίζει οτι μόνο ένα thread processor είναι ενεργό σε κάθε δεδομένη χρονική στιγμή.
    Η sys.dm_os_schedulers DMV είναι αυτή που δείχνει την λίστα των schedulers.
    Ένα collection από schedulers το οποίο παρέχει ένα abstraction layer πάνω από ένα group επεξεργαστών ονομάζεται scheduling node. Με τον όρο αυτός αναφερόμαστε σε ένα unit of work το οποίο είναι scheduled από τον SQL Server.
    Ένα Transact-SQL (T-SQL) statement batch μπορεί να δείχνει σε ένα ή περισσότερα tasks.
    Για παράδειγμα ένα parallel query εκτελείται με πολλαπλά tasks, τα οποία εκτελούνται μέσω worker threads.
    Ένα worker thread αντιπροσωπεύει ένα logical thread στον SQL Server το οποίο εσωτερικά αντιστοιχίζεται (1:1) είτε σε ένα Windows thread είτα σε fiber (εαν έχω ενεργοποιήσει το lightweight pooling στον SQL Server).
    H αντιστοιχία του worker thread σε Windows thread υπάρχει μέχρι το worker thread να γινει deallocated ή επειδή υπάρχει έλειψη μνήμης ή είναι idle για μεγάλο χρονικό διάστημα. Τα Worker threads εκτελούνται και διαχειρίζονται από τα system threads.
    The max worker threads Option
    Κάθε instance του SQL Server συντηρεί ένα pool από Windows threads ή fibers με σκοπό να κάνει processing τα user queries. Αυτό το thread pool βοηθάει στο να γίνει optimize το performance όταν ένας μεγάλος αριθμός από clients είναι συνδεδεμένοι στον server. Το maximum size του pool αυτού ορίζεται από το max worker threads στα server configuration option.
    Η minimum και τα default values για το max worker threads option (το οποίο για να μην ξεχάσω είναι στα advanced options) έχει αλλάξει στον SQL Server 2005.
    Στον Server 2000 η minimum τιμή που μπορούσες να ορίσεις είναι 32, στον SQL Server 2005 είναι 128.
    Στον SQL Server 2000 η default τιμή είναι 255, στον SQL Server 2005 είναι 0. Αυτό σημαίνει ότι μόνος του ο SQL Server θα κάνει αυτόματα configure τον αριθμό των worker threads κατα το startup του service. Εάν είναι 0 τότε ο SQL Server 2005 χρησιμοποιεί την παρακάτω formula ανάλογα με το είμαι σε x32 ή x64 πλατφόρμα.
    Η formula είναι
    # of CPUs
    x32
    x64

    256
    512
    >4
    256 + ( (#CPUs - 4) * 8 )
    512 + ( (#CPUs – 4) * 8 )
    Ενδεικτικά αναφέρω

    Number of CPUs
    32-bit computer
    64-bit computer

    256
    512
    8 processors
    288
    576
    16 processors
    352
    704
    32 processors
    480
    960
    Εδώ είναι θεωρώ σημαντικό να αναφέρω ότι ο SQL Server στην πραγματικότητα δεν δημιουργεί όλα τα threads αλλα δεσμεύει την μνήμη που χρειάζεται για την δημιουργία του αριθμού των threads που έχουν ορισθεί με την max worker threads.
    Εαν τώρα ο αριθμός των χρηστών που είναι συνδεδεμένος την δεδομένη χρονική στιγμή είναι μικρότερος από τον αριθμός που έχει ορισθεί στην max worker threads τότε ένα thread κάνει handle ένα user connection (1:1). Εάν είναι μεγαλύτερος τότε ο SQL Server κάνει pooling ώστε το επόμενο διαθέσιμο worker thread να ικανοποιήσει το request.
    Routing Tasks to a Scheduler
    O SQL Server 2000 χρησιμοποιούσε για τον scheduler τον αλγόριθμο round-robin κατά την στιγμή του connection. Έτσι όλα τα batches στο ίδιο connection τρέχαν στον ίδιο scheduler, αυτό όπως γίνεται κατανοητό μπορούσε να οδηγήσει σε αστάθεια ιδιαίτερα με τα long-lived connections.
    Στον SQL Server 2005, η απόφαση για την επιλογή του scheduler βασίζεται στο πόσο απασχολημένος είναι αυτός.
    Με άλλα λόγια ένα task στον SQL Server 2000 πήγαινε πάντα σε έναν συγκεκριμένο scheduler, στον SQL Server 2005, η επιλογή για το σε ποιον θα πάει το task γίνεται με το ποσο φορτωμένος είναι.
    Κατά τη στιγμή του connection o SQL Server 2005 διαλέγει τον λιγότερο φορτωμένο scheduler. Στο επόμενο batch στο ίδιο connection χρησιμοποιεί τον ίδιο αλογόριθμο έτσι εαν ο τρεχων scheduler είναι ο ποιο φορτωμένος διαλέγει το λιγότερο φορτωμένο.
    Τα πεδία όπως το load_factor και runnable_tasks_count της sys.dm_os_schedulers μπορουν να χρησιμοποιηθούν για την εύρεση του πόσο απασχολημένος είναι ένας scheduler.
    Συνεχίζεται…
  16. antonch
    Πρόσφατα έκανα μια παρουσίαση με το παραπάνω θέμα για το dotNetZone. Επειδή το θεωρω σημαντικό και επειδή τα παιδιά εκεί την έχουν μαγνητοσκοπήσει σας την δίνω και σε εσάς.
    https://www323.livemeeting.com/cc/usergroups/view?id=dnz34
    και η παρουσίαση μου είναι στο
    http://cid-4745d5449dec653a.skydrive.live.com/self.aspx/dnzPresentations/Why%20%5E0%20How%20to%20optimize%20SQL%20Server%20for%20performance%20from%20design%20to%20query.pdf
  17. antonch
    Καθημερινά ένας DBA ή DB developer έχει να αντιμετωπίσει αρκετούς αστάθμητους τις περισσότερες φορές παράγοντες που επηρεάζουν το performance.

    Πιστέψτε με ότι αυτό είναι μια δουλεία που απαιτεί σχολαστικότητα γνώση της αρχιτεκτονικής τόσο του SQL Server όσο και της database. Για να κάνεις αυτή την δουλεία θα πρέπει να έχει μελετήσει αρκετά και να έχεις κάνει αρκετή πρακτική εξάσκηση για την απόκτηση εμπειρίας.

    Ακόμα και ένας τέτοιος άνθρωπος πάντα επιζητά τρόπους να κάνει λιγότερο δύσκολη την δουλεία του και η χαρά του είναι μεγάλη όταν ανακαλύπτει ότι το αγαπημένο του εργαλείο έκδοση με την έκδοση του δίνει περισσότερες δυνατότητες για αυτό το task.

    http://www.sqlschool.gr/blog/sql-server-2016-new-features-the-sys-dm_exec_session_wait_stats-dmv-1056.aspx
  18. antonch
    Καθώς έχουν ήδη γίνει δέκα SQL Saturday Nights θα ήθελα την γνώμη σας για την ποιότητα αυτών. Θα παρακαλούσα αν θέλετε να μου πείτε όσοι τα έχετε παρακολουθήσει είτε ζωντάνα είτε μαγνητοσκοπημένα την γνώμη σας για αυτά.
    Αυτό μπορείτε να το κάνετε μπαίνοντας στο www.sqlschool.gr και στη δημοσκόπηση που υπάρχει εκεί να επιλέξετε την άποψη σας.
    Σας ευχαριστώ εκ των προτέρων
    antonch
  19. antonch
    Μόλις έχω τελειώσει το 9ο SQL Saturday Night και μάλιστα σε χρονικό διάστημα συντομότερο από τον αρχικά προγραμματισμένο. Αίτια για αυτή την συντόμευση ήταν κάποια παλικάρια τα οποία είχαν σαν σκοπό όχι να παρακολουθήσουν την παρουσίαση μου αλλά να κάνουν πλάκα προτείνοντας θέματα σεξουαλικού περιεχομένου και άλλα όμορφα τέτοια πράγματα. Αυτό είχε σαν αποτέλεσμα πρώτον να με απασχολούν και να μου αποσπούν την προσοχή μου από την παρουσίαση μου και δεύτερο η μαγνητοσκόπηση αυτή να πάει στο καλάθι των αχρήστων. Καταρχήν θα πρέπει να ζητήσω συγνώμη σε όσους ήταν μέσα στην παρουσίαση αυτή και φάνηκε ο εκνευρισμός μου, ήταν κάτι το οποίο θεωρούσα ότι δεν πρόκειται να συμβεί ποτέ. Δυστυχώς την παρουσίαση αυτή δεν μπορώ να την ανεβάσω όπως κατεγράφη ή θα πρέπει να υποστεί μεγάλη επεξεργασία ή θα την γράψω μόνος μου από την αρχή, θα αποφασίσω για αυτό το πρωί που θα την δω. Αυτό που όμως είναι σίγουρο ότι θα γίνει είναι ότι πλέον για την αποφυγή της επανάληψης τέτοιων φαινομένων θα αλλάξω τον τρόπο με τον οποίο γίνεται η διάθεση του link της κάθε παρουσίασης και δεν θα είναι τόσο ελεύθερος όσο είναι τώρα. Λυπάμαι πολύ για αυτό αλλά πραγματικά τα SQL Saturday Nights είναι ένα δημιούργημα μου που δε θέλω να καταστραφεί.
  20. antonch
    Αρκέτες φορές έχω δεχθεί ερωτήσεις σχετικά με τα database Schemas. Οι πιο δημοφιλείς ερωτήσεις είναι;
    Τι είναι τα Schemas; Ποιά είναι η πρακτική αξία τους; Γιατί τα έβαλαν στον SQL Server τόσο αργά; η Oracle τα έχει χρόνια τώρα! Θα ξεκινήσω με την τελευταία.
    Μπορεί τα schemas να εμφανίστηκαν από τον SQL Server 2005 αλλά αυτό δεν σημαίνει ότι δεν υπήρχαν και πριν. Υπήρχαν αλλά δεν ήταν “ορατά”. Στην πραγματικότητα με το που έφτιαχνες έναν user, αυτόματα δημιουργόνταν το αντίστοιχο schema.
    Στην Oracle υπήρχαν, αλλά ο σκοπός τους ήταν καλύψουν μια ανάγκη που βασικά δεν την είχαμε, ούτε την έχουμε, στον SQL Server. Όπως θα γνωρίζεται κάθε database στην Oracle είναι ένα διαφορετικό service. Έτσι εάν θέλω να έχω μια βάση για το ERP μου, μια βάση για το Web Site μου, και μία βάση για κάτι άλλο, θα πρέπει να έχω τρία διαφορετικά services να τρέχουν. Στο SQL Server δεν το έχουμε ανάγκη αυτό, διότι με ένα service μπορώ να έχω 32767 διαφορετικές βάσεις. Με την χρήση λοιπόν των schemas στην Oracle έλυνα το παραπάνω θέμα. Έφτιαχνα μια βάση στην οποία έφτιαχνα τα αντίστοιχα schemas μέσα σε αυτή ώστε να καλύψω τις τρεις διαφορετικές δομές που είχα σε κάθε βάση.
    Η εμφάνιση των Schemas στον SQL Server είναι για να λύσουν άλλα θέματα. Αλλά ας πάρουμε τα πράγματα από την αρχή.
    Ένα Schema στον SQL Server δεν είναι τίποτα άλλο από ένα named container για τα database objects (tables,views,stored procedures κλπ).
    Βασικός λόγος που έκαναν την εμφάνιση τους τα Schemas από τον SQL Server 2005 και μετά ήταν για να λύσουν ένα βασικό administration πρόβλημα.
    Έστω λοιπόν ότι έχω τον χρήστη Nasos. Δεν είναι administrator, δεν είναι database owner, έχει όμως δικαιώμα να φτιάχνει tables. Έστω ότι αυτός έφτιαξε τους πίνακες Ν1 και Ν2. Άρα έχω dbname.Nasos.N1 και dbname.Nasos.N2.
    Εάν τώρα έσβηνα τον user Nasos είτε κατά λάθος είτε διότι έπρεπε, επειδή ο Νάσος έφυγε από την εταιρεία, στις προηγούμενες εκδόσεις τα object αυτά έμεναν ορφανά, το αποτέλεσμα ήταν ότι είτε δεν λειτουργούσαν stored procedures που πατούσαν πάνω σε αυτά, είτε δεν μπορούσα να προσπελάσω απευθείας, και έπρεπε να μπω στην διαδικασία να πάρω το ownership με ότι αυτό συνεπάγεται.
    Από τον SQL Server 2005 και μετά αυτό δεν συμβαίνει διότι πολύ απλά οι πίνακες αυτοί δεν ανήκουν στον user αλλά στο Schema.
    Αυτός ήταν και ο βασικός λόγος που σαν DBA δεν έδινα δικαιώματα στο user να φτιάχνει δικά του objects ή αν πραγματικά ήθελα να κάνω κάτι τέτοιο έβαζα όλους τους χρήστες με ένα user name.
    Κάθε user ανήκει σε κάποιο Schema. Ακόμα και αν δεν πω κατά την δημιουργία του user στην βάση σε ποιο Schema ανήκει, by default αυτός θα ανήκει στο Schema dbo.
    Επίσης όταν δημιουργώ ένα object πχ έναν πίνακα και δεν λέω κατά την στιγμή της δημιουργίας του σε ποιο Schema αυτό θα ανήκει, by default αυτό πάει στο Schema στο οποίο ανήκει ο user που το δημιούργησε.
    Κάθε Schema έρχεται και κάθεται μεταξύ του database level του object level στην ιεραρχία του SQL Server. Αυτό σημαίνει ότι μπορώ να έχω περισσότερες δυνατότητες στον ορισμό του security πάνω στα objects της βάσης μου. Κάποια στιγμή θα σας πω για το security στον SQL Server και θα σας το εξηγήσω καλύτερα.
    Κάθε Schema επίσης έχει συγκεκριμένο owner. O οποίος μπορεί να είναι είτε συγκεκριμένος user είτε database role είτε application role.
    To Schema πλέον αντικαθιστά τον owner στο multi-part object name. Αυτό σημαίνει ότι στο παράδειγμα SELECT * FROM Northwind.antonis.Customers μέχρι την έλευση του SQL Server 2005 λέγαμε ότι ο antonis είναι ο χρήστης τώρα δεν είναι ο χρήστης αλλά το schema.
    Μερικές χρήσιμες συμβουλές για τα Schemas
    Ομαδοποίηση αντικειμένων σύμφωνα με το σκοπό τους πχ schema το οποίο περιέχει όλα τα αντικείμενα που αφορούν μια συγκεκριμένη ενότητα (π.χ web site). Διαχείριση του security σε επίπεδο βάσης με την χρήση των Schemas. Δηλαδή αντί να δίνω δικαιώματα σε επίπεδο χρήστη δίνω σε επίπεδο schema και ο χρήστης κληρονομεί αυτά μιας και ανήκει στο schema αυτό. Καλό είναι να μην είναι όλος ο κόσμος Schema owner. Ένας χρήστης είναι το ιδανικό. Επισης καλό θα είναι να μην είναι ο dbo ο owner για τα schemas τα οποία έχω μέσα στην βάση μου. Αυτά τα λίγα για τα Schemas και την πρακτική τους αξία στον SQL Server. Ελπίζω να έδωσα απαντήσεις στα ερωτήματα σας.
  21. antonch
    Με μια ανακοίνωση που ακολουθεί η Microsoft παρουσίασε τον Microsoft SQL Server ODBC Driver for Linux. Διαλειτουργικότητα σε όλο της το μεγαλείο.


    Greetings Developer community:
    We heard yesterday and today at the PASS conference about the exciting new areas that we are investing in bringing the power of SQL Server to our customers. Many of our developers who rely on native connectivity to SQL Server primarily use ODBC for their connectivity needs. We have been supporting ODBC as a part of the SQL Native Access Client (SNAC) libraries. In our continued commitment to interoperability, today we also announced that we will be releasing the Microsoft SQL Server ODBC Driver for Linux. We will be releasing first community technology preview (CTP) around mid-November and will be available along with SQL Server 2012 when it is released. Please look for announcement on our SQL Connectivity home page and SQL Server blog page.
    We will be showcasing Microsoft SQL Server ODBC Driver for Linux along with our Java and PHP solutions for SQL Server and Azure at PASS conference session “[AD-211-M] Developing Multi-Platform Applications for Microsoft SQL Server and Azure” on Thursday October 13th at 5:00PM at Washington State Convention Center Room #4C4. Also, if you have any questions or feedback on our multi-platform strategy as well as the entire gamut of support we provide to the application developers, I would encourage you to attend the PASS Panel Discussion with SQL Connectivity Leadership “[AD-101-M] SQL Connectivity Leadership Unplugged” on Friday, October 14, 2011, 2:30 PM - 3:45 PM at Washington State Convention Centre Room# 612 where I will be hosting a panel along with the rest of the leadership team that drives the strategy for our application platform.
    Thanks,
    Raghu Ram
    Principal Group Program Manager
    SQL Server
  22. antonch
    Η δύναμη της συνήθειας είναι το μεγαλύτερο ναρκωτικό του μυαλού και δεν το αφήνει να σκεφτεί αποδοτικά. Αλήθεια πόσες φορές κάνουμε μηχανικά κάποια πράγματα επειδή τα έχουμε συνηθίσει στα τόσα χρόνια που κάνουμε αυτή την δουλειά ;
     
    http://www.sqlschool.gr/blog/get-all-tables-row-count-without-using-count-function-1058.aspx
  23. antonch
    Τόσα χρόνια στον χώρο της πληροφορικής έχω μάθει να προσπαθώ να καταλάβω τι γίνεται πίσω από την σκηνή με αυτό που ασχολούμαι. Ο SQL Server είναι ένα από αυτά, και στο οποίο έχω αφιερώσει αρκετές τρίχες της κεφαλής μου. Σήμερα θα σας πάρω λίγο από τον πολύτιμο χρόνο σας για να σας μεταφέρω μια γνώση που θα σας φανεί αρκετά χρήσιμη.
    Όλοι λίγο ή πολύ έχετε γράψει ένα sql query. Άλλες φορές αυτό λειτούργησε άψογα άλλες φορές όχι. Το μυστικό για να γράψεις ένα καλό sql query είναι να έχεις κατανοήσει πως αυτό λογικά εκτελείται, ιδιαίτερα στην μηχανή που χρησιμοποιείς, στην δικιά μας περίπτωση ο SQL Server.
    Ένα sql query περιέχει κάποιες εκφράσεις μέσα του, εδώ θα ασχοληθούμε με τι βασικές, Έτσι ένα sql query είναι της μορφής αυτής. Στους αριθμούς μέσα στις παρενθέσεις φαίνεται η σειρά, βήμα, φάση εκτέλεσης.
    Κάθε βήμα δημιουργεί έναν virtual table ο οποίος είναι το input για το επόμενο βήμα. Αυτά το virtual tables δεν είναι διαθέσιμα σε κανένα πλήν του SQL Server, εκτός του τελευταίου που είναι και αυτό που παίρνουμε σαν απάντηση. Εάν στο sql query μας δεν έχουμε κάποια έκφραση απλά αυτή αγνοείτε και πάει στο επόμενο βήμα.
     
    (8) SELECT (9) DISTINCT (11)
    (1) FROM
    (3) JOIN
    (2) ON
    (4) WHERE
    (5) GROUP BY
    (6) WITH {CUBE | ROLLUP}
    (7) HAVING
    (10) ORDER BY
     
    Ας δούμε τα πράγματα λίγο αναλυτικά με ένα παράδειγμα που θα δημιουργήσουμε με το παρακάτω script
    SET NOCOUNT ON;
    USE tempdb;
    GO
    IF OBJECT_ID('dbo.Orders') IS NOT NULL
    DROP TABLE dbo.Orders;
    GO
    IF OBJECT_ID('dbo.Customers') IS NOT NULL
    DROP TABLE dbo.Customers;
    GO
    CREATE TABLE dbo.Customers
    (
    customerid CHAR(5) NOT NULL PRIMARY KEY,
    city VARCHAR(10) NOT NULL
    );

    INSERT INTO dbo.Customers(customerid, city) VALUES('ANTON', 'Athens');
    INSERT INTO dbo.Customers(customerid, city) VALUES('NASOS', 'Athens');
    INSERT INTO dbo.Customers(customerid, city) VALUES('FANIS', 'Athens');
    INSERT INTO dbo.Customers(customerid, city) VALUES('CHRIS', 'Salonica');

    CREATE TABLE dbo.Orders
    (
    orderid INT NOT NULL PRIMARY KEY,
    customerid CHAR(5) NULL REFERENCES Customers(customerid)
    );

    INSERT INTO dbo.Orders(orderid, customerid) VALUES(1, 'NASOS');
    INSERT INTO dbo.Orders(orderid, customerid) VALUES(2, 'NASOS');
    INSERT INTO dbo.Orders(orderid, customerid) VALUES(3, 'FANIS');
    INSERT INTO dbo.Orders(orderid, customerid) VALUES(4, 'FANIS');
    INSERT INTO dbo.Orders(orderid, customerid) VALUES(5, 'FANIS');
    INSERT INTO dbo.Orders(orderid, customerid) VALUES(6, 'CHRIS');
    INSERT INTO dbo.Orders(orderid, customerid) VALUES(7, NULL);
    Μετά από την εκτέλεση του θα έχουμε δύο πίνακες τους Customers, Orders οι οποίοι είναι related μεταξύ τους και τα δεδομένα τους θα είναι τα εξής
    Customers Table Data
    customerid
    city
    ANTON
    Athens
    CHRIS
    Salonica
    FANIS
    Athens
    NASOS
    Athens
     
    Orders Table Data
    Orderid
    customerid
    1
    NASOS
    2
    NASOS
    3
    FANIS
    4
    FANIS
    5
    FANIS
    6
    CHRIS
    7
    NULL
     
    Ας πάρουμε σαν σενάριο ότι θέλουμε να βούμε τους πελάτες της Αθήνας που έχουν κάτω από τρεις παραγγελίες.
    Έτσι το sql query μας θα είναι σαν αυτό.
    SELECT C.customerid, COUNT(O.orderid) AS numorders
    FROM dbo.Customers AS C
    LEFT OUTER JOIN dbo.Orders AS O
    ON C.customerid = O.customerid
    WHERE C.city = 'Athens'
    GROUP BY C.customerid
    HAVING COUNT(O.orderid) 3
    ORDER BY numorders;
    Το αποτέλεσμα της εκτέλεσης του είναι το παρακάτω
    Customerid
    numorders
    ANTON
    0
    NASOS
    2
     
    Ποιά είναι όμως η λογική του εκτέλεση; Ας δούμε λοιπόν τα βήματα της εκτέλεσης.
    Βήμα 1ο - Cross Join
    FROM dbo.Customers AS C ... JOIN dbo.Orders AS O
     
    Εδώ φτιάχνει το καρτεσιανό γινόμενο των δύο πινάκων και το βάζει στο 1ο virtual table (VT1). Το περιεχόμενο του πίνακα αυτού είναι 28 γραμμές ( 4x7).
    Customerid
    City
    Orderid
    customerid
    ANTON
    Athens
    1
    NASOS
    ANTON
    Athens
    2
    NASOS
    ANTON
    Athens
    3
    FANIS
    ANTON
    Athens
    4
    FANIS
    ANTON
    Athens
    5
    FANIS
    ANTON
    Athens
    6
    CHRIS
    ANTON
    Athens
    7
    NULL
    CHRIS
    Salonica
    1
    NASOS
    CHRIS
    Salonica
    2
    NASOS
    CHRIS
    Salonica
    3
    FANIS
    CHRIS
    Salonica
    4
    FANIS
    CHRIS
    Salonica
    5
    FANIS
    CHRIS
    Salonica
    6
    CHRIS
    CHRIS
    Salonica
    7
    NULL
    FANIS
    Athens
    1
    NASOS
    FANIS
    Athens
    2
    NASOS
    FANIS
    Athens
    3
    FANIS
    FANIS
    Athens
    4
    FANIS
    FANIS
    Athens
    5
    FANIS
    FANIS
    Athens
    6
    CHRIS
    FANIS
    Athens
    7
    NULL
    NASOS
    Athens
    1
    NASOS
    NASOS
    Athens
    2
    NASOS
    NASOS
    Athens
    3
    FANIS
    NASOS
    Athens
    4
    FANIS
    NASOS
    Athens
    5
    FANIS
    NASOS
    Athens
    6
    CHRIS
    NASOS
    Athens
    7
    NULL
     
    Βήμα 2ο - Apply Join condition ON Filter
    ON C.customerid = O.customerid
    Στο βήμα αυτό εφαρμόζεται το ON που υπάρχει στο Join και μόνο τα rows εκείνα τα οποία ικανοποιούν το βήμα πηγαίνουν στον VT2 που θα είναι το αποτέλεσμα του βήματος αυτού.
    Έτσι αν πάρω τον VT1 και εφαρμόσω στο παράδειγμα μας το ON θα έχω το εξής αποτέλεσμα
    Customerid
    City
    Orderid
    customerid
    ΟΝ Filter
    ANTON
    Athens
    1
    NASOS
    FALSE
    ANTON
    Athens
    2
    NASOS
    FALSE
    ANTON
    Athens
    3
    FANIS
    FALSE
    ANTON
    Athens
    4
    FANIS
    FALSE
    ANTON
    Athens
    5
    FANIS
    FALSE
    ANTON
    Athens
    6
    CHRIS
    FALSE
    ANTON
    Athens
    7
    NULL
    UNKNOWN
    CHRIS
    Salonica
    1
    NASOS
    FALSE
    CHRIS
    Salonica
    2
    NASOS
    FALSE
    CHRIS
    Salonica
    3
    FANIS
    FALSE
    CHRIS
    Salonica
    4
    FANIS
    FALSE
    CHRIS
    Salonica
    5
    FANIS
    FALSE
    CHRIS
    Salonica
    6
    CHRIS
    TRUE
    CHRIS
    Salonica
    7
    NULL
    UNKNOWN
    FANIS
    Athens
    1
    NASOS
    FALSE
    FANIS
    Athens
    2
    NASOS
    FALSE
    FANIS
    Athens
    3
    FANIS
    TRUE
    FANIS
    Athens
    4
    FANIS
    TRUE
    FANIS
    Athens
    5
    FANIS
    TRUE
    FANIS
    Athens
    6
    CHRIS
    FALSE
    FANIS
    Athens
    7
    NULL
    UNKNOWN
    NASOS
    Athens
    1
    NASOS
    TRUE
    NASOS
    Athens
    2
    NASOS
    TRUE
    NASOS
    Athens
    3
    FANIS
    FALSE
    NASOS
    Athens
    4
    FANIS
    FALSE
    NASOS
    Athens
    5
    FANIS
    FALSE
    NASOS
    Athens
    6
    CHRIS
    FALSE
    NASOS
    Athens
    7
    NULL
    UNKNOWN
     
    Δηλαδή ο VT2 θα είναι τελικά ο παρακάτω
    Customerid
    City
    Orderid
    customerid
    CHRIS
    Salonica
    6
    CHRIS
    FANIS
    Athens
    3
    FANIS
    FANIS
    Athens
    4
    FANIS
    FANIS
    Athens
    5
    FANIS
    NASOS
    Athens
    1
    NASOS
    NASOS
    Athens
    2
    NASOS
     
    Βήμα 3ο - Apply OUTER Join
    FROM dbo.Customers AS C LEFT OUTER JOIN dbo.Orders AS O
    Στην περίπτωση μας μόνο ένας είναι ο πελάτης από την πίνακα τον πελατών που δεν υπάρχει στον VT2 o ΑΝΤΟΝ οπότε μπαίνει και αυτό στον VT3 που είναι το αποτέλεσμα του βήματος αυτού
    Customerid
    City
    Orderid
    customerid
    CHRIS
    Salonica
    6
    CHRIS
    FANIS
    Athens
    3
    FANIS
    FANIS
    Athens
    4
    FANIS
    FANIS
    Athens
    5
    FANIS
    NASOS
    Athens
    1
    NASOS
    NASOS
    Athens
    2
    NASOS
    ΑΝΤΟΝ
    Athens
    NULL
    NULL
     
    Βήμα 4ο - Apply WHERE filter
    WHERE C.city = 'Athens'
    Το αποτέλεσμα είναι ο VT4
    Customerid
    City
    Orderid
    customerid
    FANIS
    Athens
    3
    FANIS
    FANIS
    Athens
    4
    FANIS
    FANIS
    Athens
    5
    FANIS
    NASOS
    Athens
    1
    NASOS
    NASOS
    Athens
    2
    NASOS
    ΑΝΤΟΝ
    Athens
    NULL
    NULL
     
    Βήμα 5ο - Apply Grouping
    GROUP BY C.customerid
    Το αποτέλεσμα είναι ο VT5
    Customerid
    City
    Orderid
    customerid
    FANIS
    Athens
    3
    FANIS
    FANIS
    Athens
    4
    FANIS
    FANIS
    Athens
    5
    FANIS
    NASOS
    Athens
    1
    NASOS
    NASOS
    Athens
    2
    NASOS
    ΑΝΤΟΝ
    Athens
    NULL
    NULL
     
    Βήμα 6ο - Apply Cube or Rollup
    Δεν έχουμε κάτι τέτοιο στο query μας οπότε πάει στο επόμενο βήμα.
    Βήμα 7ο - Apply HAVING Filter
    HAVING COUNT(O.orderid) 3
    Το αποτέλεσμα είναι ο VT7
    Customerid
    City
    Orderid
    customerid
    NASOS
    Athens
    1
    NASOS
    NASOS
    Athens
    2
    NASOS
    ΑΝΤΟΝ
    Athens
    NULL
    NULL
     
    Βήμα 8ο - Apply SELECT List
    SELECT C.customerid, COUNT(O.orderid) AS numorders
    Το αποτέλεσμα είναι ο VT8
    Customerid
    numorders
    NASOS
    2
    ANTON
    0
    Βήμα 9ο - Apply DISTINCT
    Δεν έχουμε κάτι τέτοιο στο query μας οπότε πάει στο επόμενο βήμα.
    Βήμα 10ο - Apply ORDER BY
    SELECT C.customerid, COUNT(O.orderid) AS numorders
    Το αποτέλεσμα είναι ο VT10
    Customerid
    numorders
    ANTON
    0
    NASOS
    2
    Βήμα 11ο - Apply TOP
    Δεν έχουμε κάτι τέτοιο στο query μας οπότε καταλήγουμε στο τελικό μας αποτέλεσμα.
    ΥΓ. Επίτηδες παρέλειψα κάποια βήματα για να μην σας κουράσω. Αν υπάρχει απαίτηση από εσάς μέσω των σχολίων σας θα προχωρήσω βαθύτερα και σε αυτά
×
×
  • Create New...