Guide de plan SQL Server et autres pratiques non recommandées

En rĂšgle gĂ©nĂ©rale, les articles sur l'optimisation des requĂȘtes vous indiquent comment faire ce qu'il faut pour aider l'optimiseur de requĂȘtes Ă  choisir le meilleur plan d'exĂ©cution: utilisez des expressions SARGable dans le WHERE, rĂ©cupĂ©rez uniquement les colonnes dont vous avez besoin, utilisez des index bien formĂ©s qui sont dĂ©fragmentĂ©s et avec des statistiques mises Ă  jour.



Aujourd'hui, je veux parler d'autre chose - de quelque chose qui n'appartient en aucun cas aux meilleures pratiques, Ă  l'aide desquelles il est trĂšs facile de se tirer une balle dans le pied et de ralentir une requĂȘte prĂ©cĂ©demment exĂ©cutĂ©e, ou de ne plus s'exĂ©cuter du tout en raison d'une erreur ... Il s'agit de conseils et de guides de planification.



Les astuces sont des astuces pour l'optimiseur de requĂȘtes, une liste complĂšte peut ĂȘtre trouvĂ©e sur MSDN . Certains d'entre eux sont vraiment des indices (par exemple, vous pouvez spĂ©cifier OPTION (MAXDOP 4)) afin que la requĂȘte puisse ĂȘtre exĂ©cutĂ©e avec un degrĂ© de parallĂ©lisme maximal = 4, mais il n'y a aucune garantie que SQL Server gĂ©nĂ©rera un plan parallĂšle avec cet indice.



L'autre partie est un guide direct pour l'action. Par exemple, si vous Ă©crivez OPTION (HASH JOIN), SQL Server crĂ©e un plan sans NESTED LOOPS et MERGE JOINs. Et vous savez ce qui se passera s'il s'avĂšre qu'il est impossible de crĂ©er un plan avec uniquement des jointures de hachage? L'optimiseur le dira - je ne peux pas crĂ©er de plan et la requĂȘte ne sera pas exĂ©cutĂ©e.



Le problĂšme est qu'on ne sait pas avec certitude (du moins pour moi) quels indices sont des indices que l'optimiseur peut percuter; et quelles astuces sont des astuces manuelles qui peuvent provoquer le blocage de la requĂȘte en cas de problĂšme. Il existe sĂ»rement dĂ©jĂ  une collection prĂȘte Ă  l'emploi oĂč cela est dĂ©crit, mais ce n'est en aucun cas une information officielle et peut changer Ă  tout moment.



Plan Guide est une chose telle (que je ne sais pas traduire correctement) qui vous permet de lier un ensemble spĂ©cifique d'indices Ă  une demande spĂ©cifique, dont vous connaissez le texte. Cela peut ĂȘtre pertinent si vous ne pouvez pas influencer directement le texte de requĂȘte gĂ©nĂ©rĂ© par l'ORM, par exemple.



Les conseils et les guides de plan ne sont en aucun cas les meilleures pratiques, il est prĂ©fĂ©rable d'omettre les conseils et ces guides, car la distribution des donnĂ©es peut changer, les types de donnĂ©es peuvent changer et un million de choses supplĂ©mentaires peuvent se produire, ce qui fait que vos requĂȘtes avec des indices fonctionneront moins bien que sans eux, et dans certains cas, elles cesseront complĂštement de fonctionner. Vous devez ĂȘtre conscient Ă  cent pour cent de ce que vous faites et pourquoi.



Maintenant, une petite explication de pourquoi je suis mĂȘme entrĂ© dans cela.



J'ai une large table avec un tas de champs nvarchar de diffĂ©rentes tailles - de 10 Ă  max. Et il y a un tas de requĂȘtes dans cette table, que CHARINDEX recherche des occurrences de sous-chaĂźnes dans une ou plusieurs de ces colonnes. Par exemple, il y a une demande qui ressemble Ă  ceci:



SELECT *
FROM table
WHERE CHARINDEX(N' ', column)>1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET x ROWS FETCH NEXT y ROWS ONLY


La table a un index clusterisĂ© sur l'ID et un index non groupĂ© non unique sur la colonne. Comme vous le comprenez vous-mĂȘme, tout cela n'a aucun sens, car dans WHERE nous utilisons CHARINDEX, ce qui n'est certainement pas SARGable. Pour Ă©viter d'Ă©ventuels problĂšmes avec le SB, je vais simuler cette situation sur la base de donnĂ©es ouverte StackOverflow2013, que vous trouverez ici .



ConsidĂ©rez la table dbo.Posts, qui n'a qu'un index clusterisĂ© par ID et une requĂȘte comme celle-ci:



SELECT *
FROM dbo.Posts
WHERE CHARINDEX (N'Data', Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY


Pour correspondre à ma base de données réelle, je crée un index sur la colonne Titre:



CREATE INDEX ix_Title ON dbo.Posts (Title);


En conséquence, bien sûr, nous obtenons un plan d'exécution absolument logique, qui consiste à balayer l'index clusterisé dans la direction opposée:











Et il est, certes, assez bien exécuté:

Tableau 'Messages'. Nombre de scans 1, lectures logiques 516, lectures physiques 0, lectures anticipées 0, lectures logiques lob 0, lectures physiques lob 0, lectures anticipées lob 0.

Temps d'exécution de SQL Server:

temps CPU = 16 ms


Mais que se passe-t-il si, au lieu du mot commun «DonnĂ©es», nous recherchons quelque chose de plus rare? Par exemple, N'Aptana '(aucune idĂ©e de ce que c'est). Le plan, bien sĂ»r, restera le mĂȘme, mais les statistiques d'exĂ©cution, hum, changeront quelque peu:

Tableau 'Messages'. Nombre de scans 1, lectures logiques 253191, lectures physiques 113, lectures anticipées 224602, lectures logiques lob 0, lectures physiques lob 0, lectures anticipées lob 0.

Temps d'exécution de SQL Server:

temps CPU = 2563 ms


Et c'est aussi logique - le mot est beaucoup moins courant et SQL Server doit analyser beaucoup plus de données pour trouver 25 lignes avec lui. Mais d'une maniÚre ou d'une autre, ce n'est pas cool, non?

Et je crĂ©ais un index non clusterisĂ©. Ce serait peut-ĂȘtre mieux si SQL Server l'utilise? Lui-mĂȘme ne l'utilisera pas, alors j'ajoute un indice:



SELECT *
FROM dbo.Posts
WHERE CHARINDEX (N'Aptana', Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY
OPTION (TABLE HINT (dbo.Posts, INDEX(ix_Title)));


Et quelque chose est en quelque sorte complÚtement triste. Statistiques d'exécution:

Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table 'Posts'. Scan count 5, logical reads 109312, physical reads 5, read-ahead reads 104946, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

SQL Server Execution Times:

CPU time = 35031 ms


et le plan:







maintenant, le plan d'exĂ©cution est parallĂšle et il a deux sortes, les deux avec des dĂ©versements dans tempdb. À propos, faites attention au premier tri, qui est effectuĂ© aprĂšs une analyse d'index non clusterisĂ©e, avant la recherche de clĂ© - il s'agit d'une optimisation SQL Server spĂ©ciale qui tente de rĂ©duire le nombre d'E / S alĂ©atoires - les recherches de clĂ©s sont effectuĂ©es dans l'ordre croissant de la clĂ© d'index cluster. Vous pouvez en savoir plus ici .



Le deuxiĂšme tri est nĂ©cessaire pour sĂ©lectionner 25 lignes dans l'ordre dĂ©croissant Id. À propos, SQL Server pourrait deviner qu'il devra trier Ă  nouveau par Id, uniquement dans l'ordre dĂ©croissant et effectuer des recherches de clĂ© dans le sens «opposé», en triant par ordre dĂ©croissant, et non croissant, de la clĂ© d'index cluster au dĂ©but.



Je ne fournis pas de statistiques sur l'exĂ©cution d'une requĂȘte avec un indice sur un index non clusterisĂ© avec une recherche par l'entrĂ©e «DonnĂ©es». Sur mon disque dur Ă  moitiĂ© mort dans un ordinateur portable, cela a pris plus de 16 minutes et je n'ai pas pensĂ© Ă  prendre une capture d'Ă©cran. DĂ©solĂ©, je ne veux plus attendre aussi longtemps.

Mais qu'en est-il de la demande? Une analyse d'index groupĂ© est-elle le rĂȘve ultime et vous ne pouvez rien faire plus rapidement?



Et si j'essayais d'éviter toutes sortes, j'ai pensé et créé un index non clusterisé, qui, en général, contredit ce qui est généralement considéré comme les meilleures pratiques pour les index non clusterisés:



CREATE INDEX ix_Id_Title ON dbo.Posts (Id DESC, Title);


Maintenant, nous utilisons l'indication pour dire Ă  SQL Server de l'utiliser:



SELECT *
FROM dbo.Posts
WHERE CHARINDEX (N'Aptana', Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY
OPTION (TABLE HINT (dbo.Posts, INDEX(ix_Id_Title)));


Oh, ça a bien fonctionné:





Tableau 'Messages'. Nombre de scans 1, lectures logiques 6259, lectures physiques 0, lectures anticipées 7816, lectures logiques lob 0, lectures physiques lob 0, lectures anticipées lob 0.

Temps d'exécution de SQL Server:

temps CPU = 1734 ms


Le gain de temps processeur n'est pas grand, mais il faut lire beaucoup moins - pas mal. Qu'en est-il des «données» fréquentes?

Tableau 'Messages'. Nombre de scans 1, lectures logiques 208, lectures physiques 0, lectures anticipées 0, lectures logiques lob 0, lectures physiques lob 0, lectures anticipées lob 0.

Temps d'exécution de SQL Server:

temps CPU = 0 ms


Wow, c'est bien aussi. Maintenant, puisque la requĂȘte provient de l'ORM et que nous ne pouvons pas modifier son texte, nous devons trouver comment «clouer» cet index Ă  la requĂȘte. Et le guide du plan vient Ă  la rescousse.



La procédure stockée sp_create_plan_guide ( MSDN ) est utilisée pour créer un guide de plan .



Considérons-le en détail:



sp_create_plan_guide [ @name = ] N'plan_guide_name'  
    , [ @stmt = ] N'statement_text'  
    , [ @type = ] N'{ OBJECT | SQL | TEMPLATE }'  
    , [ @module_or_batch = ]  
      {   
        N'[ schema_name. ] object_name'  
        | N'batch_text'  
        | NULL  
      }  
    , [ @params = ] { N'@parameter_name data_type [ ,...n ]' | NULL }   
    , [ @hints = ] { 
                 N'OPTION ( query_hint [ ,...n ] )'   
                 | N'XML_showplan'  
                 | NULL 
      }  


Nom - nom de guide de plan clair et unique

stmt- c'est la demande Ă  laquelle vous devez ajouter l'indication. Il est important de savoir ici que cette demande doit ĂȘtre Ă©crite EXACTEMENT de la mĂȘme maniĂšre que la demande qui provient de l'application. Un espace Ă©trange? Le guide du plan ne sera pas utilisĂ©. Mauvais saut de ligne? Le guide du plan ne sera pas utilisĂ©. Pour vous faciliter les choses, il y a un "life hack" sur lequel je reviendrai un peu plus tard (et que j'ai trouvĂ© ici ).



type - indique oĂč la demande spĂ©cifiĂ©e dans stmt. S'il fait partie d'une procĂ©dure stockĂ©e, il doit ĂȘtre OBJECT; si cela fait partie d'un lot de plusieurs requĂȘtes, ou d'une requĂȘte ad hoc, ou d'un lot d'une requĂȘte, il devrait y avoir SQL. Si TEMPLATE est indiquĂ© ici, il s'agit d'une histoire distincte sur le paramĂ©trage des requĂȘtes, que vous pouvez lire sur MSDN .



@module_or_batch dĂ©pend detype. Si untype= 'OBJECT', cela doit ĂȘtre le nom de la procĂ©dure stockĂ©e. Si untype= 'BATCH' - il devrait y avoir le texte du lot entier, spĂ©cifiĂ© mot Ă  mot avec ce qui provient des applications. Un espace Ă©trange? Eh bien, vous le savez dĂ©jĂ . S'il est NULL, alors nous considĂ©rons qu'il s'agit d'un lot d'une requĂȘte et qu'il correspond Ă  ce qui est spĂ©cifiĂ© dansstmt avec toutes les restrictions.



paramĂštres- tous les paramĂštres passĂ©s Ă  la demande avec les types de donnĂ©es doivent ĂȘtre rĂ©pertoriĂ©s ici.



@hints est enfin la partie la plus intĂ©ressante, ici vous devez spĂ©cifier les astuces Ă  ajouter Ă  la requĂȘte. Ici, vous pouvez insĂ©rer explicitement le plan d'exĂ©cution requis au format XML, le cas Ă©chĂ©ant. Ce paramĂštre peut Ă©galement ĂȘtre NULL, ce qui entraĂźnera le fait que SQL Server n'utilisera pas les conseils qui sont explicitement spĂ©cifiĂ©s dans la requĂȘte dansstmt.



Nous crĂ©ons donc un guide de plan pour la requĂȘte:



DECLARE @sql nvarchar(max) = N'SELECT *
FROM dbo.Posts
WHERE CHARINDEX (N''Data'', Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY';

exec sp_create_plan_guide @name = N'PG_dboPosts_Index_Id_Title'  
    ,  @stmt =  @sql
    ,  @type = N'SQL'
    ,  @module_or_batch =  NULL          
    ,  @params = NULL
    ,  @hints =  N'OPTION (TABLE HINT (dbo.Posts, INDEX (ix_Id_Title)))'


Et nous essayons d'exĂ©cuter la requĂȘte:

SELECT *
FROM dbo.Posts
WHERE CHARINDEX (N'Data', Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY


Wow, cela a fonctionné:







dans les propriétés de la derniÚre instruction SELECT, nous voyons:







Super, le plan giude a Ă©tĂ© appliquĂ©. Et si vous recherchez «Aptana» maintenant? Et tout ira mal - nous reviendrons Ă  nouveau sur l'analyse de l'index cluster avec toutes les consĂ©quences. Pourquoi? Et parce que, le guide de plan est appliquĂ© Ă  une requĂȘte SPÉCIFIQUE, dont le texte coĂŻncide un Ă  un avec celui en cours d'exĂ©cution.



Heureusement pour moi, la plupart des requĂȘtes sur mon systĂšme sont paramĂ©trĂ©es. Je n’ai pas travaillĂ© avec des requĂȘtes non paramĂ©trĂ©es et j’espĂšre que je n’ai pas Ă  le faire. Pour eux, vous pouvez utiliser des modĂšles (voir un peu plus haut sur TEMPLATE), vous pouvez activer PARAMETERISATION FORCÉE dans la base de donnĂ©es ( ne faites pas cela sans comprendre ce que vous faites !!! ) et, peut-ĂȘtre, aprĂšs cela, vous pourrez lier le Guide du plan. Mais je n'ai vraiment pas essayĂ©.



Dans mon cas, la requĂȘte est exĂ©cutĂ©e dans quelque chose comme ceci:



exec sp_executesql  
N'SELECT *
FROM dbo.Posts
WHERE CHARINDEX (@p0, Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET @p1 ROWS FETCH NEXT @p2 ROWS ONLY;'
, N'@p0 nvarchar(250), @p1 int, @p2 int'
, @p0 = N'Aptana', @p1 = 0, @p2 = 25;


Par conséquent, je crée un guide de plan correspondant:



DECLARE @sql nvarchar(max) = N'SELECT *
FROM dbo.Posts
WHERE CHARINDEX (@p0, Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET @p1 ROWS FETCH NEXT @p2 ROWS ONLY;';

exec sp_create_plan_guide @name = N'PG_paramters_dboPosts_Index_Id_Title'  
    ,  @stmt =  @sql
    ,  @type = N'SQL'
    ,  @module_or_batch =  NULL          
    ,  @params = N'@p0 nvarchar(250), @p1 int, @p2 int'
    ,  @hints =  N'OPTION (TABLE HINT (dbo.Posts, INDEX (ix_Id_Title)))'


Et, hourra, tout fonctionne comme prévu:











Ă©tant en dehors des conditions de la serre, il n'est pas toujours possible de spĂ©cifier correctement le paramĂštrestmtpour joindre un guide de plan Ă  une demande, et pour cela il y a un "hack de vie" que j'ai mentionnĂ© ci-dessus. Nous effaçons le cache du plan, supprimons les guides, exĂ©cutons Ă  nouveau la requĂȘte paramĂ©trĂ©e et rĂ©cupĂ©rons son plan d'exĂ©cution et son plan_handle du cache.



Une demande pour cela peut ĂȘtre utilisĂ©e, par exemple, comme ceci:



SELECT qs.plan_handle, st.text, qp.query_plan
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text (qs.sql_handle) st
CROSS APPLY sys.dm_exec_query_plan (qs.plan_handle) qp






Nous pouvons maintenant utiliser la procédure stockée sp_create_plan_guide_from_handle pour créer un guide de plan à partir d'un plan existant.



Il prend comme paramĂštresNom- le nom du guide crĂ©Ă©, @plan_handle - le handle du plan d'exĂ©cution existant et @statement_start_offset - qui dĂ©finit le dĂ©but de l'instruction dans le batch pour lequel le guide doit ĂȘtre crĂ©Ă©.



En essayant:



exec sp_create_plan_guide_from_handle N'PG_dboPosts_from_handle'  
    , 0x0600050018263314F048E3652102000001000000000000000000000000000000000000000000000000000000
    , NULL;


Et maintenant, dans SSMS, nous regardons ce que nous avons dans Programmabilité -> Guides de plan:







maintenant, le plan d'exécution actuel a été "cloué" à notre demande en utilisant le guide de plan 'PG_dboPosts_from_handle', mais, mieux encore, maintenant, comme presque n'importe quel objet dans SSMS, nous pouvons créer des scripts et recréer comme nous en avons besoin.



RMB, Script -> Drop AND Create et nous obtenons un script prĂȘt Ă  l'emploi dans lequel nous devons remplacer la valeur du paramĂštre @hints par celle dont nous avons besoin, donc nous obtenons:



USE [StackOverflow2013]
GO

/****** Object:  PlanGuide PG_dboPosts_from_handle    Script Date: 05.07.2020 16:25:04 ******/
EXEC sp_control_plan_guide @operation = N'DROP', @name = N'[PG_dboPosts_from_handle]'
GO
/****** Object:  PlanGuide PG_dboPosts_from_handle    Script Date: 05.07.2020 16:25:04 ******/
EXEC sp_create_plan_guide @name = N'[PG_dboPosts_from_handle]', @stmt = N'SELECT *
FROM dbo.Posts
WHERE CHARINDEX (@p0, Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET @p1 ROWS FETCH NEXT @p2 ROWS ONLY', @type = N'SQL', @module_or_batch = N'SELECT *
FROM dbo.Posts
WHERE CHARINDEX (@p0, Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET @p1 ROWS FETCH NEXT @p2 ROWS ONLY;', 
@params = N'@p0 nvarchar(250), @p1 int, @p2 int', 
@hints = N'OPTION (TABLE HINT (dbo.Posts, INDEX (ix_Id_Title)))'
GO


Nous exécutons et réexécutons la demande. Hourra, tout fonctionne:







si vous remplacez la valeur du paramĂštre, tout fonctionne de la mĂȘme maniĂšre.



Veuillez noter qu'un seul guide peut correspondre Ă  une dĂ©claration. Si vous essayez d'ajouter un autre guide Ă  la mĂȘme instruction, vous recevrez un message d'erreur.

Msg 10502, niveau 16, Ă©tat 1, ligne 1

Impossible de créer le guide de plan 'PG_dboPosts_from_handle2' car l'instruction spécifiée parstmtet @module_or_batch, ou par @plan_handle et @statement_start_offset, correspond au guide de plan existant 'PG_dboPosts_from_handle' dans la base de données. Supprimez le guide de plan existant avant de créer le nouveau guide de plan.


La derniÚre chose que je voudrais mentionner est la procédure stockée sp_control_plan_guide .



Avec son aide, vous pouvez supprimer, désactiver et activer les guides de plan - les deux un à la fois, en indiquant le nom, et tous les guides (je ne sais pas - tout du tout. Ou tout dans le contexte de la base de données dans laquelle la procédure est exécutée) - les valeurs sont utilisées pour cela @ paramÚtre d'opération - DROP ALL, DISABLE ALL, ENABLE ALL. Un exemple d'utilisation de HP pour un plan spécifique est donné juste au-dessus - un guide de plan spécifique avec le nom spécifié est supprimé.



Était-il possible de se passer d'indices et de guide de plan?



En gĂ©nĂ©ral, s'il vous semble que l'optimiseur de requĂȘtes est stupide et fait une sorte de jeu, et que vous savez comment faire, avec une probabilitĂ© de 99%, vous faites une sorte de jeu (comme dans mon cas). Cependant, dans le cas oĂč vous n'avez pas la possibilitĂ© d'influencer directement le texte de la demande, un guide de plan qui vous permet d'ajouter un indice Ă  la demande peut vous sauver la vie. Supposons que nous ayons la possibilitĂ© de rĂ©Ă©crire le texte de la requĂȘte selon nos besoins - cela peut-il changer quelque chose? SĂ»r! MĂȘme sans l'utilisation du terme "exotique" sous forme de recherche plein texte, qui, en fait, devrait ĂȘtre utilisĂ© ici. Par exemple, une telle requĂȘte a un plan et des statistiques d'exĂ©cution complĂštement normaux (pour une requĂȘte):



;WITH c AS (
    SELECT p2.id
    FROM dbo.Posts p2
    WHERE CHARINDEX (N'Aptana', Title) > 1
    ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
    OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY
)
SELECT p.* 
FROM dbo.Posts p
JOIN c ON p.id = c.id;




Tableau 'Messages'. Nombre de scans 1, lectures logiques 6250, lectures physiques 0, lectures anticipées 0, lectures logiques lob 0, lectures physiques lob 0, lectures anticipées lob 0.

Temps d'exécution de SQL Server:

temps CPU = 1500 ms


SQL Server trouve d'abord les 25 identificateurs requis par l'index «tordu» ix_Id_Title, et ensuite seulement fait une recherche dans l'index cluster pour les identificateurs sĂ©lectionnĂ©s - encore mieux qu'avec le guide! Mais que se passe-t-il si nous exĂ©cutons une requĂȘte sur 'DonnĂ©es' et affichons 25 lignes, Ă  partir de la 20 000Ăšme ligne:



;WITH c AS (
    SELECT p2.id
    FROM dbo.Posts p2 
    WHERE CHARINDEX (N'Data', Title) > 1
    ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
    OFFSET 20000 ROWS FETCH NEXT 25 ROWS ONLY
)
SELECT p.* 
FROM dbo.Posts p
JOIN c ON p.id = c.id;




Tableau 'Messages'. Nombre de scans 1, lectures logiques 5914, lectures physiques 0, lectures anticipées 0, lectures logiques lob 11, lectures physiques lob 0, lectures anticipées lob 0.

Temps d'exécution de SQL Server:

temps CPU = 1453 ms


exec sp_executesql  
N'SELECT *
FROM dbo.Posts
WHERE CHARINDEX (@p0, Title) > 1
ORDER BY ROW_NUMBER() OVER (ORDER BY Id DESC)
OFFSET @p1 ROWS FETCH NEXT @p2 ROWS ONLY;'
, N'@p0 nvarchar(250), @p1 int, @p2 int'
, @p0 = N'Data', @p1 = 20000, @p2 = 25;




Table 'Posts'. Scan count 1, logical reads 87174, physical reads 0, read-ahead reads 0, lob logical reads 11, lob physical reads 0, lob read-ahead reads 0.

SQL Server Execution Times:

CPU time = 1437 ms


Oui, le temps processeur est le mĂȘme, car il est passĂ© sur charindex, mais la requĂȘte avec le guide fait un ordre de grandeur plus de lectures, et cela peut devenir un problĂšme.



Permettez-moi de rĂ©sumer le rĂ©sultat final. Les conseils et les guides peuvent vous aider beaucoup ici et maintenant, mais ils peuvent facilement aggraver les choses. Si vous spĂ©cifiez explicitement un indice avec un index dans le texte de la demande, puis supprimez l'index, la requĂȘte ne peut tout simplement pas ĂȘtre exĂ©cutĂ©e. Sur mon SQL Server 2017, la requĂȘte avec le guide, aprĂšs la suppression de l'index, est exĂ©cutĂ©e correctement - le guide est ignorĂ©, mais je ne peux pas ĂȘtre sĂ»r qu'il en sera toujours ainsi et dans toutes les versions de SQL Server.



Il n'y a pas beaucoup d'informations sur le guide du plan en russe, j'ai donc dĂ©cidĂ© de l'Ă©crire moi-mĂȘme. Vous pouvez lire icisur les limitations dans l'utilisation des guides de plan, en particulier sur le fait que parfois une indication explicite de l'index avec un indice utilisant PG peut conduire au fait que les demandes tomberont. Je souhaite que vous ne les utilisiez jamais, et si vous devez - eh bien, bonne chance - vous savez oĂč cela peut mener.



All Articles