Μέθοδοι βελτιστοποίησης ερωτημάτων LINQ στο C#.NET

Εισαγωγή

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

Είναι γνωστό ότι LINQ(Language-Integrated Query) είναι μια απλή και βολική γλώσσα για την αναζήτηση μιας πηγής δεδομένων.

А LINQ σε SQL είναι μια τεχνολογία για την πρόσβαση σε δεδομένα σε ένα DBMS. Αυτό είναι ένα ισχυρό εργαλείο για την εργασία με δεδομένα, όπου τα ερωτήματα κατασκευάζονται μέσω μιας δηλωτικής γλώσσας, η οποία στη συνέχεια θα μετατραπεί σε Ερωτήματα SQL πλατφόρμα και αποστέλλεται στον διακομιστή της βάσης δεδομένων για εκτέλεση. Στην περίπτωσή μας, με τον όρο DBMS εννοούμε MS SQL Server.

Ωστόσο, Ερωτήματα LINQ δεν μετατρέπονται σε βέλτιστα γραμμένα Ερωτήματα SQL, που θα μπορούσε να γράψει ένας έμπειρος DBA με όλες τις αποχρώσεις της βελτιστοποίησης Ερωτήματα SQL:

  1. βέλτιστες συνδέσεις (Ενώνω) και φιλτράρισμα των αποτελεσμάτων (ΠΟΥ)
  2. πολλές αποχρώσεις στη χρήση συνδέσεων και συνθηκών ομάδας
  3. πολλές παραλλαγές στις συνθήκες αντικατάστασης IN επί ΥΠΑΡΧΕΙи ΟΧΙ ΜΕΣΑ, <> σε ΥΠΑΡΧΕΙ
  4. ενδιάμεση προσωρινή αποθήκευση των αποτελεσμάτων μέσω προσωρινών πινάκων, CTE, μεταβλητών πίνακα
  5. χρήση πρότασης (ΕΠΙΛΟΓΉ) με οδηγίες και υποδείξεις πίνακα ΜΕ (...)
  6. χρησιμοποιώντας ευρετηριασμένες προβολές ως ένα από τα μέσα για να απαλλαγείτε από περιττές αναγνώσεις δεδομένων κατά τις επιλογές

Τα κύρια σημεία συμφόρησης απόδοσης του προκύπτοντος Ερωτήματα SQL κατά τη σύνταξη Ερωτήματα LINQ είναι οι εξής:

  1. ενοποίηση ολόκληρου του μηχανισμού επιλογής δεδομένων σε ένα αίτημα
  2. αντιγραφή πανομοιότυπων μπλοκ κώδικα, που τελικά οδηγεί σε πολλαπλές περιττές αναγνώσεις δεδομένων
  3. ομάδες συνθηκών πολλαπλών συστατικών (λογικά "και" και "ή") - ΚΑΙ и OR, ο συνδυασμός σε πολύπλοκες συνθήκες, οδηγεί στο γεγονός ότι ο βελτιστοποιητής, έχοντας κατάλληλα μη ομαδοποιημένα ευρετήρια για τα απαραίτητα πεδία, αρχίζει τελικά να σαρώνει έναντι του ευρετηρίου συμπλέγματος (ΣΑΡΩΣΗ ΕΥΡΕΤΗΡΙΟΥ) ανά ομάδες συνθηκών
  4. Η βαθιά ένθεση των υποερωτημάτων καθιστά την ανάλυση πολύ προβληματική Δηλώσεις SQL και ανάλυση του σχεδίου ερωτημάτων από την πλευρά των προγραμματιστών και DBA

Μέθοδοι βελτιστοποίησης

Τώρα ας περάσουμε απευθείας στις μεθόδους βελτιστοποίησης.

1) Πρόσθετη ευρετηρίαση

Είναι καλύτερο να λαμβάνετε υπόψη φίλτρα στους κύριους πίνακες επιλογής, καθώς πολύ συχνά ολόκληρο το ερώτημα βασίζεται σε έναν ή δύο κύριους πίνακες (εφαρμογές-άτομα-λειτουργίες) και με ένα τυπικό σύνολο συνθηκών (IsClosed, Canceled, Enabled, Status). Είναι σημαντικό να δημιουργηθούν κατάλληλοι δείκτες για τα προσδιορισμένα δείγματα.

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

Για παράδειγμα, έχουμε 500000 αιτήσεις. Ωστόσο, υπάρχουν μόνο 2000 ενεργές εφαρμογές. Τότε ένα σωστά επιλεγμένο ευρετήριο θα μας σώσει από ΣΑΡΩΣΗ ΕΥΡΕΤΗΡΙΟΥ σε ένα μεγάλο τραπέζι και θα σας επιτρέψει να επιλέξετε γρήγορα δεδομένα μέσω ενός ευρετηρίου χωρίς ομαδοποίηση.

Επίσης, η έλλειψη ευρετηρίων μπορεί να εντοπιστεί μέσω μηνυμάτων για ανάλυση σχεδίων ερωτημάτων ή συλλογή στατιστικών στοιχείων προβολής συστήματος MS SQL Server:

  1. sys.dm_db_missing_index_groups
  2. sys.dm_db_missing_index_group_stats
  3. sys.dm_db_missing_index_details

Όλα τα δεδομένα προβολής περιέχουν πληροφορίες σχετικά με ευρετήρια που λείπουν, με εξαίρεση τα χωρικά ευρετήρια.

Ωστόσο, τα ευρετήρια και η προσωρινή αποθήκευση είναι συχνά μέθοδοι για την καταπολέμηση των συνεπειών της κακής γραφής Ερωτήματα LINQ и Ερωτήματα SQL.

Όπως δείχνει η σκληρή πρακτική της ζωής, είναι συχνά σημαντικό για μια επιχείρηση να εφαρμόζει επιχειρηματικά χαρακτηριστικά εντός ορισμένων προθεσμιών. Και επομένως, τα βαριά αιτήματα συχνά μεταφέρονται στο παρασκήνιο με την προσωρινή αποθήκευση.

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

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

Αξίζει επίσης να θυμηθούμε ότι στη διαδικασία αναζήτησης των απαραίτητων ευρετηρίων για προσθήκη, προτάσεις MS SQL Η βελτιστοποίηση μπορεί να είναι εσφαλμένη, συμπεριλαμβανομένων των ακόλουθων συνθηκών:

  1. εάν υπάρχουν ήδη ευρετήρια με παρόμοιο σύνολο πεδίων
  2. εάν τα πεδία στον πίνακα δεν μπορούν να ευρετηριαστούν λόγω περιορισμών ευρετηρίασης (περιγράφεται με περισσότερες λεπτομέρειες εδώ).

2) Συγχώνευση χαρακτηριστικών σε ένα νέο χαρακτηριστικό

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

Αυτό ισχύει ιδιαίτερα για τα πεδία κατάστασης, τα οποία είναι συνήθως είτε bit είτε σε ακέραιο.

Παράδειγμα:

IsClosed = 0 AND Cancelled = 0 AND Enabled = 0 αντικατασταθεί από Κατάσταση = 1.

Εδώ εισάγεται το χαρακτηριστικό Integer Status για να διασφαλιστεί ότι αυτές οι καταστάσεις συμπληρώνονται στον πίνακα. Στη συνέχεια, αυτό το νέο χαρακτηριστικό ευρετηριάζεται.

Αυτή είναι μια θεμελιώδης λύση στο πρόβλημα απόδοσης, επειδή έχουμε πρόσβαση σε δεδομένα χωρίς περιττούς υπολογισμούς.

3) Υλοποίηση της άποψης

Δυστυχώς σε Ερωτήματα LINQ Οι προσωρινοί πίνακες, οι CTE και οι μεταβλητές πίνακα δεν μπορούν να χρησιμοποιηθούν απευθείας.

Ωστόσο, υπάρχει ένας άλλος τρόπος βελτιστοποίησης για αυτήν την περίπτωση - οι προβολές με ευρετήριο.

Ομάδα συνθηκών (από το παραπάνω παράδειγμα) IsClosed = 0 AND Cancelled = 0 AND Enabled = 0 (ή ένα σύνολο άλλων παρόμοιων συνθηκών) γίνεται μια καλή επιλογή για τη χρήση τους σε μια ευρετηριασμένη προβολή, αποθηκεύοντας προσωρινά ένα μικρό τμήμα δεδομένων από ένα μεγάλο σύνολο.

Ωστόσο, υπάρχουν ορισμένοι περιορισμοί κατά την υλοποίηση μιας άποψης:

  1. χρήση υποερωτημάτων, ρήτρες ΥΠΑΡΧΕΙ πρέπει να αντικατασταθεί με χρήση Ενώνω
  2. δεν μπορείς να χρησιμοποιήσεις προτάσεις ΕΝΩΣΗ, UNION ALL, ΕΞΑΙΡΕΣΗ, ΔΙΑΤΕΜΝΩ
  3. Δεν μπορείτε να χρησιμοποιήσετε υποδείξεις και προτάσεις πίνακα ΕΠΙΛΟΓΉ
  4. δεν υπάρχει δυνατότητα εργασίας με κύκλους
  5. Είναι αδύνατο να εμφανιστούν δεδομένα σε μία προβολή από διαφορετικούς πίνακες

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

Ωστόσο, όταν καλείτε μια προβολή, αυτά τα ευρετήρια ενδέχεται να μην χρησιμοποιούνται και για να τα χρησιμοποιήσετε ρητά, πρέπει να καθορίσετε ΜΕ (NOEXPAND).

Επειδή μέσα Ερωτήματα LINQ Είναι αδύνατο να ορίσετε υποδείξεις πίνακα, επομένως πρέπει να δημιουργήσετε μια άλλη αναπαράσταση - ένα "περιτύλιγμα" της ακόλουθης μορφής:

CREATE VIEW ИМЯ_представления AS SELECT * FROM MAT_VIEW WITH (NOEXPAND);

4) Χρήση συναρτήσεων πίνακα

Συχνά σε Ερωτήματα LINQ Μεγάλα μπλοκ υποερωτημάτων ή μπλοκ που χρησιμοποιούν προβολές με σύνθετη δομή σχηματίζουν ένα τελικό ερώτημα με μια πολύ περίπλοκη και μη βέλτιστη δομή εκτέλεσης.

Βασικά πλεονεκτήματα της χρήσης των λειτουργιών πίνακα στο Ερωτήματα LINQ:

  1. Η δυνατότητα, όπως στην περίπτωση των προβολών, να χρησιμοποιείται και να προσδιορίζεται ως αντικείμενο, αλλά μπορείτε να μεταβιβάσετε ένα σύνολο παραμέτρων εισόδου:
    FROM FUNCTION(@param1, @param2 ...)
    Ως αποτέλεσμα, μπορεί να επιτευχθεί ευέλικτη δειγματοληψία δεδομένων
  2. Στην περίπτωση χρήσης συνάρτησης πίνακα, δεν υπάρχουν τόσο ισχυροί περιορισμοί όπως στην περίπτωση των προβολών με ευρετήριο που περιγράφονται παραπάνω:
    1. Υποδείξεις πίνακα:
      μέσω LINQ Δεν μπορείτε να καθορίσετε ποια ευρετήρια θα χρησιμοποιηθούν και να καθορίσετε το επίπεδο απομόνωσης δεδομένων κατά την υποβολή ερωτημάτων.
      Αλλά η λειτουργία έχει αυτές τις δυνατότητες.
      Με τη συνάρτηση, μπορείτε να επιτύχετε ένα αρκετά σταθερό σχέδιο ερωτήματος εκτέλεσης, όπου ορίζονται κανόνες για την εργασία με ευρετήρια και επίπεδα απομόνωσης δεδομένων
    2. Η χρήση της συνάρτησης επιτρέπει, σε σύγκριση με προβολές με ευρετήριο, να αποκτήσετε:
      • σύνθετη λογική δειγματοληψίας δεδομένων (ακόμη και με χρήση βρόχων)
      • λήψη δεδομένων από πολλούς διαφορετικούς πίνακες
      • η χρήση του ΕΝΩΣΗ и ΥΠΑΡΧΕΙ

  3. Πρόταση ΕΠΙΛΟΓΉ πολύ χρήσιμο όταν χρειάζεται να παρέχουμε έλεγχο συγχρονισμού ΕΠΙΛΟΓΗ (MAXDOP N), τη σειρά του σχεδίου εκτέλεσης ερωτήματος. Για παράδειγμα:
    • μπορείτε να καθορίσετε μια αναγκαστική εκ νέου δημιουργία του σχεδίου ερωτήματος ΕΠΙΛΟΓΗ (ΕΠΑΝΑΜΕΤΑΓΡΑΦΗ)
    • μπορείτε να καθορίσετε εάν θα εξαναγκαστεί το σχέδιο ερωτήματος να χρησιμοποιήσει τη σειρά ένωσης που καθορίζεται στο ερώτημα ΕΠΙΛΟΓΗ (ΑΝΑΓΚΑΣΤΙΚΗ ΠΑΡΑΓΓΕΛΙΑ)

    Περισσότερες λεπτομέρειες για ΕΠΙΛΟΓΉ περιγράφεται εδώ.

  4. Χρησιμοποιώντας το στενότερο και πιο απαιτούμενο τμήμα δεδομένων:
    Δεν χρειάζεται να αποθηκεύετε μεγάλα σύνολα δεδομένων σε κρυφές μνήμες (όπως συμβαίνει με τις προβολές με ευρετήριο), από τις οποίες θα πρέπει να φιλτράρετε τα δεδομένα ανά παράμετρο.
    Για παράδειγμα, υπάρχει ένας πίνακας του οποίου το φίλτρο ΠΟΥ χρησιμοποιούνται τρία πεδία (α, β, γ).

    Συμβατικά, όλα τα αιτήματα έχουν σταθερή προϋπόθεση a = 0 και b = 0.

    Ωστόσο, το αίτημα για το γήπεδο c πιο μεταβλητή.

    Αφήστε την προϋπόθεση a = 0 και b = 0 Μας βοηθά πραγματικά να περιορίσουμε το απαιτούμενο προκύπτον σύνολο σε χιλιάδες ρεκόρ, αλλά η προϋπόθεση είναι с περιορίζει την επιλογή σε εκατό ρεκόρ.

    Εδώ η συνάρτηση πίνακα μπορεί να είναι καλύτερη επιλογή.

    Επίσης, μια συνάρτηση πίνακα είναι πιο προβλέψιμη και συνεπής στο χρόνο εκτέλεσης.

παραδείγματα

Ας δούμε ένα παράδειγμα υλοποίησης χρησιμοποιώντας τη βάση δεδομένων Questions ως παράδειγμα.

Υπάρχει αίτημα SELECT, το οποίο συνδυάζει πολλούς πίνακες και χρησιμοποιεί μία προβολή (OperativeQuestions), στην οποία η συσχέτιση ελέγχεται μέσω email (μέσω ΥΠΑΡΧΕΙ) στις «Λειτουργικές Ερωτήσεις»:

Αίτημα Νο. 1

(@p__linq__0 nvarchar(4000))SELECT
1 AS [C1],
[Extent1].[Id] AS [Id],
[Join2].[Object_Id] AS [Object_Id],
[Join2].[ObjectType_Id] AS [ObjectType_Id],
[Join2].[Name] AS [Name],
[Join2].[ExternalId] AS [ExternalId]
FROM [dbo].[Questions] AS [Extent1]
INNER JOIN (SELECT [Extent2].[Object_Id] AS [Object_Id],
[Extent2].[Question_Id] AS [Question_Id], [Extent3].[ExternalId] AS [ExternalId],
[Extent3].[ObjectType_Id] AS [ObjectType_Id], [Extent4].[Name] AS [Name]
FROM [dbo].[ObjectQuestions] AS [Extent2]
INNER JOIN [dbo].[Objects] AS [Extent3] ON [Extent2].[Object_Id] = [Extent3].[Id]
LEFT OUTER JOIN [dbo].[ObjectTypes] AS [Extent4] 
ON [Extent3].[ObjectType_Id] = [Extent4].[Id] ) AS [Join2] 
ON [Extent1].[Id] = [Join2].[Question_Id]
WHERE ([Extent1].[AnswerId] IS NULL) AND (0 = [Extent1].[Exp]) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[OperativeQuestions] AS [Extent5]
WHERE (([Extent5].[Email] = @p__linq__0) OR (([Extent5].[Email] IS NULL) 
AND (@p__linq__0 IS NULL))) AND ([Extent5].[Id] = [Extent1].[Id])
));

Η προβολή έχει μια μάλλον περίπλοκη δομή: έχει ενώσεις υποερωτημάτων και χρησιμοποιεί ταξινόμηση ΔΙΑΦΟΡΕΤΙΚΕΣ, η οποία γενικά είναι μια λειτουργία που απαιτεί αρκετά πόρους.

Ένα δείγμα από το OperativeQuestions είναι περίπου δέκα χιλιάδες εγγραφές.

Το κύριο πρόβλημα με αυτό το ερώτημα είναι ότι για τις εγγραφές από το εξωτερικό ερώτημα, εκτελείται ένα εσωτερικό υποερώτημα στην προβολή [OperativeQuestions], το οποίο για το [Email] = @p__linq__0 μας επιτρέπει να περιορίσουμε την επιλογή εξόδου (μέσω ΥΠΑΡΧΕΙ) έως και εκατοντάδες εγγραφές.

Και μπορεί να φαίνεται ότι το υποερώτημα θα πρέπει να υπολογίσει τις εγγραφές μία φορά με [Email] = @p__linq__0 και, στη συνέχεια, αυτές οι δύο εκατοντάδες εγγραφές θα πρέπει να συνδεθούν με Id με Ερωτήσεις και το ερώτημα θα είναι γρήγορο.

Στην πραγματικότητα, υπάρχει διαδοχική σύνδεση όλων των πινάκων: έλεγχος της αντιστοιχίας των Ερωτήσεων Id με το Id από το OperativeQuestions και φιλτράρισμα μέσω email.

Στην πραγματικότητα, το αίτημα λειτουργεί και με τις δεκάδες χιλιάδες εγγραφές του OperativeQuestions, αλλά χρειάζονται μόνο τα δεδομένα που σας ενδιαφέρουν μέσω email.

Κείμενο προβολής OperativeQuestions:

Αίτημα Νο. 2

 
CREATE VIEW [dbo].[OperativeQuestions]
AS
SELECT DISTINCT Q.Id, USR.email AS Email
FROM            [dbo].Questions AS Q INNER JOIN
                         [dbo].ProcessUserAccesses AS BPU ON BPU.ProcessId = CQ.Process_Id 
OUTER APPLY
                     (SELECT   1 AS HasNoObjects
                      WHERE   NOT EXISTS
                                    (SELECT   1
                                     FROM     [dbo].ObjectUserAccesses AS BOU
                                     WHERE   BOU.ProcessUserAccessId = BPU.[Id] AND BOU.[To] IS NULL)
) AS BO INNER JOIN
                         [dbo].Users AS USR ON USR.Id = BPU.UserId
WHERE        CQ.[Exp] = 0 AND CQ.AnswerId IS NULL AND BPU.[To] IS NULL 
AND (BO.HasNoObjects = 1 OR
              EXISTS (SELECT   1
                           FROM   [dbo].ObjectUserAccesses AS BOU INNER JOIN
                                      [dbo].ObjectQuestions AS QBO 
                                                  ON QBO.[Object_Id] =BOU.ObjectId
                               WHERE  BOU.ProcessUserAccessId = BPU.Id 
                               AND BOU.[To] IS NULL AND QBO.Question_Id = CQ.Id));

Αρχική αντιστοίχιση προβολής στο DbContext (EF Core 2)

public class QuestionsDbContext : DbContext
{
    //...
    public DbQuery<OperativeQuestion> OperativeQuestions { get; set; }
    //...
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Query<OperativeQuestion>().ToView("OperativeQuestions");
    }
}

Αρχικό ερώτημα LINQ

var businessObjectsData = await context
    .OperativeQuestions
    .Where(x => x.Email == Email)
    .Include(x => x.Question)
    .Select(x => x.Question)
    .SelectMany(x => x.ObjectQuestions,
                (x, bo) => new
                {
                    Id = x.Id,
                    ObjectId = bo.Object.Id,
                    ObjectTypeId = bo.Object.ObjectType.Id,
                    ObjectTypeName = bo.Object.ObjectType.Name,
                    ObjectExternalId = bo.Object.ExternalId
                })
    .ToListAsync();

Στη συγκεκριμένη περίπτωση, εξετάζουμε μια λύση σε αυτό το πρόβλημα χωρίς αλλαγές υποδομής, χωρίς να εισάγουμε έναν ξεχωριστό πίνακα με έτοιμα αποτελέσματα («Ενεργά ερωτήματα»), που θα απαιτούσε έναν μηχανισμό για την πλήρωσή του με δεδομένα και την ενημέρωση του. .

Αν και αυτή είναι μια καλή λύση, υπάρχει μια άλλη επιλογή για τη βελτιστοποίηση αυτού του προβλήματος.

Ο κύριος σκοπός είναι η προσωρινή αποθήκευση καταχωρήσεων από [Email] = @p__linq__0 από την προβολή OperativeQuestions.

Εισαγάγετε τη συνάρτηση πίνακα [dbo].[OperativeQuestionsUserMail] στη βάση δεδομένων.

Στέλνοντας Email ως παράμετρο εισαγωγής, λαμβάνουμε πίσω έναν πίνακα τιμών:

Αίτημα Νο. 3


CREATE FUNCTION [dbo].[OperativeQuestionsUserMail]
(
    @Email  nvarchar(4000)
)
RETURNS
@tbl TABLE
(
    [Id]           uniqueidentifier,
    [Email]      nvarchar(4000)
)
AS
BEGIN
        INSERT INTO @tbl ([Id], [Email])
        SELECT Id, @Email
        FROM [OperativeQuestions]  AS [x] WHERE [x].[Email] = @Email;
     
    RETURN;
END

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

Προκειμένου τα ερωτήματα στο OperativeQuestionsUserMail να είναι βέλτιστα και να έχουν βέλτιστα σχέδια ερωτημάτων, απαιτείται αυστηρή δομή και όχι ΕΠΙΣΤΡΟΦΕΣ ΤΡΑΠΕΖΙ ΩΣ ΕΠΙΣΤΡΟΦΗ...

Σε αυτήν την περίπτωση, το απαιτούμενο ερώτημα 1 μετατρέπεται σε ερώτημα 4:

Αίτημα Νο. 4

(@p__linq__0 nvarchar(4000))SELECT
1 AS [C1],
[Extent1].[Id] AS [Id],
[Join2].[Object_Id] AS [Object_Id],
[Join2].[ObjectType_Id] AS [ObjectType_Id],
[Join2].[Name] AS [Name],
[Join2].[ExternalId] AS [ExternalId]
FROM (
    SELECT Id, Email FROM [dbo].[OperativeQuestionsUserMail] (@p__linq__0)
) AS [Extent0]
INNER JOIN [dbo].[Questions] AS [Extent1] ON([Extent0].Id=[Extent1].Id)
INNER JOIN (SELECT [Extent2].[Object_Id] AS [Object_Id], [Extent2].[Question_Id] AS [Question_Id], [Extent3].[ExternalId] AS [ExternalId], [Extent3].[ObjectType_Id] AS [ObjectType_Id], [Extent4].[Name] AS [Name]
FROM [dbo].[ObjectQuestions] AS [Extent2]
INNER JOIN [dbo].[Objects] AS [Extent3] ON [Extent2].[Object_Id] = [Extent3].[Id]
LEFT OUTER JOIN [dbo].[ObjectTypes] AS [Extent4] 
ON [Extent3].[ObjectType_Id] = [Extent4].[Id] ) AS [Join2] 
ON [Extent1].[Id] = [Join2].[Question_Id]
WHERE ([Extent1].[AnswerId] IS NULL) AND (0 = [Extent1].[Exp]);

Αντιστοίχιση προβολών και λειτουργιών στο DbContext (EF Core 2)

public class QuestionsDbContext : DbContext
{
    //...
    public DbQuery<OperativeQuestion> OperativeQuestions { get; set; }
    //...
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Query<OperativeQuestion>().ToView("OperativeQuestions");
    }
}
 
public static class FromSqlQueries
{
    public static IQueryable<OperativeQuestion> GetByUserEmail(this DbQuery<OperativeQuestion> source, string Email)
        => source.FromSql($"SELECT Id, Email FROM [dbo].[OperativeQuestionsUserMail] ({Email})");
}

Τελικό ερώτημα LINQ

var businessObjectsData = await context
    .OperativeQuestions
    .GetByUserEmail(Email)
    .Include(x => x.Question)
    .Select(x => x.Question)
    .SelectMany(x => x.ObjectQuestions,
                (x, bo) => new
                {
                    Id = x.Id,
                    ObjectId = bo.Object.Id,
                    ObjectTypeId = bo.Object.ObjectType.Id,
                    ObjectTypeName = bo.Object.ObjectType.Name,
                    ObjectExternalId = bo.Object.ExternalId
                })
    .ToListAsync();

Η σειρά του χρόνου εκτέλεσης έχει πέσει από 200-800 ms, σε 2-20 ms, κ.λπ., δηλαδή δεκάδες φορές πιο γρήγορα.

Αν το πάρουμε πιο μέτρια, τότε αντί για 350 ms πήραμε 8 ms.

Από τα προφανή πλεονεκτήματα παίρνουμε επίσης:

  1. γενική μείωση του φορτίου ανάγνωσης,
  2. σημαντική μείωση της πιθανότητας αποκλεισμού
  3. μείωση του μέσου χρόνου μπλοκαρίσματος σε αποδεκτές τιμές

Παραγωγή

Βελτιστοποίηση και μικρορύθμιση κλήσεων βάσης δεδομένων MS SQL μέσω LINQ είναι ένα πρόβλημα που μπορεί να λυθεί.

Η προσοχή και η συνέπεια είναι πολύ σημαντικές σε αυτή τη δουλειά.

Στην αρχή της διαδικασίας:

  1. είναι απαραίτητο να ελέγξετε τα δεδομένα με τα οποία λειτουργεί το αίτημα (τιμές, επιλεγμένοι τύποι δεδομένων)
  2. να πραγματοποιήσει σωστή ευρετηρίαση αυτών των δεδομένων
  3. ελέγξτε την ορθότητα των συνθηκών ένωσης μεταξύ των πινάκων

Η επόμενη επανάληψη βελτιστοποίησης αποκαλύπτει:

  1. βάση του αιτήματος και ορίζει το κύριο φίλτρο αιτήματος
  2. επαναλαμβάνοντας παρόμοια μπλοκ ερωτημάτων και αναλύοντας τη διασταύρωση των συνθηκών
  3. σε SSMS ή άλλο GUI για Ο SQL Server βελτιστοποιεί τον εαυτό του Ερώτημα SQL (εκχώρηση μιας ενδιάμεσης αποθήκευσης δεδομένων, δημιουργία του ερωτήματος που προκύπτει χρησιμοποιώντας αυτήν την αποθήκευση (μπορεί να υπάρχουν πολλά))
  4. στο τελευταίο στάδιο, λαμβάνοντας ως βάση το προκύπτον Ερώτημα SQL, η δομή ανακατασκευάζεται Ερώτημα LINQ

Το αποτέλεσμα Ερώτημα LINQ θα πρέπει να είναι πανομοιότυπη στη δομή με το προσδιορισμένο βέλτιστο Ερώτημα SQL από το σημείο 3.

Ευχαριστώ

Ευχαριστώ πολύ τους συναδέλφους jobgemws и alex_ozr από την εταιρεία Fortis για βοήθεια στην προετοιμασία αυτού του υλικού.

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο