{"id":8450,"date":"2017-12-18T23:56:47","date_gmt":"2017-12-19T01:56:47","guid":{"rendered":"http:\/\/www.fabriciolima.net\/?p=8450"},"modified":"2020-10-10T14:36:00","modified_gmt":"2020-10-10T17:36:00","slug":"melhorando-a-performance-de-consultas-no-totvs-protheus-parte-2","status":"publish","type":"post","link":"https:\/\/fabriciolima.net\/blog\/2017\/12\/18\/melhorando-a-performance-de-consultas-no-totvs-protheus-parte-2\/","title":{"rendered":"Melhorando a Performance de Consultas no Totvs Protheus &#8211; Parte 2"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" class=\" wp-image-8549 aligncenter\" src=\"https:\/\/www.fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/NetFlix2.png\" alt=\"\" width=\"627\" height=\"380\" srcset=\"https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/NetFlix2.png 1146w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/NetFlix2-300x182.png 300w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/NetFlix2-1024x620.png 1024w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/NetFlix2-768x465.png 768w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/NetFlix2-700x424.png 700w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/NetFlix2-410x248.png 410w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/NetFlix2-100x61.png 100w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/NetFlix2-275x167.png 275w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/NetFlix2-20x12.png 20w\" sizes=\"auto, (max-width: 627px) 100vw, 627px\" \/><\/p>\n<p>Fala pessoal,<\/p>\n<p>Estamos aqui para o segundo post da nossa s\u00e9rie que tem um epis\u00f3dio novo a cada semana.<\/p>\n<p>Antes de lerem esse post, sugiro verem o anterior para que sigam a sequ\u00eancia correta da s\u00e9rie:<\/p>\n<ul>\n<li><a href=\"https:\/\/www.fabriciolima.net\/blog\/2017\/12\/11\/melhorando-a-performance-de-consultas-no-totvs-protheus-parte-1\/\" target=\"_blank\" rel=\"noopener noreferrer\">Melhorando a Performance de Consultas no Totvs Protheus \u2013 Parte 1<\/a><\/li>\n<\/ul>\n<p>Agora que j\u00e1 sabem do que se trata essa s\u00e9rie de posts, vamos para a an\u00e1lise da nossa pr\u00f3xima query.<\/p>\n<p>Novamente olhando o Trace que monitora as queries mais demoradas do ambiente, essa query abaixo apareceu v\u00e1rias vezes consumindo muitos recursos e <strong>demorando mais de 1 minuto<\/strong>:<\/p>\n<pre class=\"lang:tsql decode:true\">SELECT   R_E_C_N_O_ ,\r\n         D_E_L_E_T_ ,\r\n         CT2_FILIAL\r\nFROM     dbo.CT2040\r\nWHERE    CT2_FILIAL &gt; '01'\r\n         AND D_E_L_E_T_ = ' '\r\n         AND (   CT2_DATA &gt;= '20170101'\r\n                 AND (   ( CT2_DEBITO = '654654654565          ' )\r\n                        OR \r\n\t\t\t\t\t ( CT2_CREDIT = '98798798798797          ' )\r\n                     )\r\n             )\r\nORDER BY CT2_FILIAL ,\r\n         CT2_DATA ,\r\n         CT2_LOTE ,\r\n         CT2_SBLOTE ,\r\n         CT2_DOC ,\r\n         CT2_LINHA ,\r\n         CT2_TPSALD ,\r\n         CT2_EMPORI ,\r\n         CT2_FILORI ,\r\n         CT2_MOEDLC ,\r\n         R_E_C_N_O_;\r\n<\/pre>\n<p>Esse \u00e9 o plano da query:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-8453 aligncenter\" src=\"https:\/\/www.fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_1.png\" alt=\"\" width=\"1261\" height=\"267\" srcset=\"https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_1.png 1936w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_1-300x64.png 300w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_1-1024x217.png 1024w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_1-768x163.png 768w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_1-1536x325.png 1536w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_1-700x148.png 700w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_1-410x87.png 410w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_1-100x21.png 100w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_1-275x58.png 275w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_1-20x4.png 20w\" sizes=\"auto, (max-width: 1261px) 100vw, 1261px\" \/><\/p>\n<p>Mais uma vez, todos os \u00edndices dessa tabela come\u00e7am com\u00a0<strong>CT2_FILIAL.<\/strong><\/p>\n<p>No caso dessa query, o SQL est\u00e1 usando 4 \u00edndices para chegar no resultado.<\/p>\n<p>Fabr\u00edcio, mais uma vez o SQL Server sugeriu um \u00edndice. <strong>Ser DBA SQL Server \u00e9 muito f\u00e1cil<\/strong>. Vamos criar!!!<\/p>\n<pre class=\"lang:tsql decode:true \">CREATE NONCLUSTERED INDEX [&lt;Name of Missing Index, sysname,&gt;]\r\nON [dbo].[CT2040] ([D_E_L_E_T_],[CT2_FILIAL],[CT2_DATA])\r\nINCLUDE ([CT2_LOTE],[CT2_SBLOTE],[CT2_DOC],[CT2_LINHA],[CT2_MOEDLC],[CT2_DEBITO],[CT2_CREDIT],[CT2_EMPORI],[CT2_FILORI],[CT2_TPSALD],[R_E_C_N_O_])<\/pre>\n<p>\u00c9 aqui que come\u00e7amos a diferenciar o profissional que entende como os \u00edndices funcionam do profissional que caiu de paraquedas nessa an\u00e1lise e n\u00e3o tem interesse em aprender.<\/p>\n<p><strong>D_E_L_E_T_<\/strong> \u00e9 uma coluna que tem apenas dois valores distintos <strong>(&#8216;*&#8217; ou &#8221;)<\/strong>, ou seja, uma p\u00e9ssima coluna para \u00edndice, pois n\u00e3o \u00e9 nada seletiva.<\/p>\n<p>Al\u00e9m disso, a tabela tem quase <strong>30 milh\u00f5es<\/strong> de registros. Um \u00edndice com todas essas colunas vai ficar bem grande.<\/p>\n<p>Vamos tentar outras possibilidades menos traum\u00e1ticas.<\/p>\n<p>Vamos focar nos filtros da query:<\/p>\n<pre class=\"lang:tsql decode:true\">WHERE    CT2_FILIAL &gt; '01'\r\n         AND D_E_L_E_T_ = ' '\r\n         AND (   CT2_DATA &gt;= '20170101'\r\n                 AND (   ( CT2_DEBITO = '654654654565          ' )\r\n                        OR \r\n\t\t\t\t\t ( CT2_CREDIT = '98798798798797          ' )\r\n                     )\r\n             )<\/pre>\n<ul>\n<li><strong>CT2_FILIAL<\/strong> e <strong>D_E_L_E_T_<\/strong> n\u00e3o filtram nada, ou seja, n\u00e3o s\u00e3o colunas boas para \u00edndices.<\/li>\n<li><strong>CT2_DATA<\/strong> filtra muito pouco, pois \u00e9 bem prov\u00e1vel que teremos muitos registros em 2017.<\/li>\n<li>A solu\u00e7\u00e3o para a query est\u00e1 em\u00a0<strong>CT2_DEBITO<\/strong> e\u00a0<strong>CT2_CREDIT<\/strong>.<\/li>\n<\/ul>\n<p>Mas Fabr\u00edcio, agora deu ruim. Tem um miser\u00e1vel de um <strong>OR<\/strong> ai nessa query. J\u00e1 me falaram que <strong>OR<\/strong> mata a performance de uma query.<\/p>\n<p>Pois \u00e9&#8230; Vamos l\u00e1&#8230;<\/p>\n<p>Realmente <strong>OR<\/strong> no <strong>WHERE<\/strong> n\u00e3o \u00e9 legal. Mas \u00e9 um <strong>ERP<\/strong>, se for query padr\u00e3o n\u00e3o vamos conseguir mudar o c\u00f3digo. Temos que sobreviver com esse <strong>OR<\/strong> ai mesmo.<\/p>\n<p>Uma coisa que podemos fazer \u00e9 usar um neg\u00f3cio chamado <strong>Index Intersection<\/strong>. Vamos criar um \u00edndice em cada coluna do OR e ver se o SQL Server vai usar os dois \u00edndices separados, juntar os dois e dar o resultado da query.<\/p>\n<pre class=\"lang:tsql decode:true \">create nonclustered index CT2040W01 on CT2040 (CT2_DEBITO ) with(FILLFACTOR=90,DATA_COMPRESSION=PAGE)\r\n\r\ncreate nonclustered index CT2040W02 on CT2040 (CT2_CREDIT ) with(FILLFACTOR=90,DATA_COMPRESSION=PAGE)<\/pre>\n<p>Os dois \u00edndices ficaram com um tamanho de <strong>500 MB<\/strong>.\u00a0Imagina o \u00edndice sugerido com todas aquelas colunas.<\/p>\n<p><strong>WOW!!!!<\/strong> Ao rodar a query novamente ela passa a rodar de forma instant\u00e2nea:<\/p>\n<p>Esse \u00e9 o novo plano:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-8456 aligncenter\" src=\"https:\/\/www.fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_2.png\" alt=\"\" width=\"923\" height=\"219\" srcset=\"https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_2.png 923w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_2-300x71.png 300w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_2-768x182.png 768w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_2-700x166.png 700w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_2-410x97.png 410w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_2-100x24.png 100w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_2-275x65.png 275w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_2-20x5.png 20w\" sizes=\"auto, (max-width: 923px) 100vw, 923px\" \/><\/p>\n<p>SQL faz um seek nos meus dois \u00edndices e depois um <strong>Merge Join<\/strong>.<\/p>\n<p>Incluindo todas as colunas usadas na query em um desses \u00edndices eu poderia evitar o<strong> Key Lookup<\/strong>, mas como a query j\u00e1 passou de<strong> 1 minuto e 30 segundos<\/strong> para <strong>0 segundos<\/strong>, vale a pena fazer isso?<\/p>\n<p>Consumo da query <strong>antes<\/strong> do \u00edndice:<\/p>\n<pre class=\"lang:tsql decode:true\">Table 'CT2040'. Scan count 12, logical reads 263520\r\n\r\nSQL Server Execution Times:\r\n   CPU time = 167669 ms,  elapsed time = 89505 ms.<\/pre>\n<p>Consumo da query <strong>depois<\/strong> do \u00edndice:<\/p>\n<pre class=\"lang:tsql decode:true\">Table 'CT2040'. Scan count 2, logical reads 3336\r\n\r\nSQL Server Execution Times:\r\n   CPU time = 31 ms,  elapsed time = 197 ms.<\/pre>\n<p><span style=\"color: #ff0000;\">Diferen\u00e7a absurda!!!<\/span><\/p>\n<p>Antes o SQL Server tinha que ler <strong>263 mil<\/strong> p\u00e1ginas de 8 kb para dar o resultado dessa query. Ap\u00f3s o \u00edndice ele precisa ler apenas <strong>3 mil<\/strong> p\u00e1ginas para dar o mesmo resultado. Essas p\u00e1ginas tem que passar pela <strong>mem\u00f3ria<\/strong>, ent\u00e3o, <strong>antes de comprar mais mem\u00f3ria para um servidor, crie alguns \u00edndices que o resultado pode ser o mesmo.<\/strong><\/p>\n<p>Olhando no <strong>Trace<\/strong> de queries demoradas, a query ficou t\u00e3o r\u00e1pida que tenho que apelar e colocar um <strong>waitfor delay<\/strong> para poder visualizar e comparar:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-8455\" src=\"https:\/\/www.fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_3.png\" alt=\"\" width=\"1203\" height=\"156\" srcset=\"https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_3.png 1203w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_3-300x39.png 300w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_3-1024x133.png 1024w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_3-768x100.png 768w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_3-700x91.png 700w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_3-410x53.png 410w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_3-100x13.png 100w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_3-275x36.png 275w, https:\/\/fabriciolima.net\/blog\/wp-content\/uploads\/2017\/12\/queryprotheysparte2_3-20x3.png 20w\" sizes=\"auto, (max-width: 1203px) 100vw, 1203px\" \/><\/p>\n<p>Ela rodou em <strong>0,5 segundos<\/strong> e a diferen\u00e7a de leituras de p\u00e1ginas<strong> (263 mil para 3 mil)<\/strong> e do consumo de CPU<strong> (176 mil para 31)<\/strong> \u00e9 gigante!!!<\/p>\n<p>\u00c9 isso ai pessoal, melhoramos mais uma query no Protheus.<\/p>\n<p>At\u00e9 a pr\u00f3xima an\u00e1lise de query.<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #ff0000;\"><strong>Atualizado no dia 07\/10\/2020:<\/strong><\/span><\/p>\n<p>Publiquei um curso com 11 horas de dura\u00e7\u00e3o com toda minha experi\u00eancia de anos no assunto e de dezenas de clientes Protheus atendidos:<\/p>\n<p>Curso:\u00a0<a class=\"external-link wpel-icon-left\" href=\"https:\/\/cursos.powertuning.com.br\/course?courseid=melhorando-a-performance-de-consultas-no-totvs-protheus\" target=\"_blank\" rel=\"external noopener noreferrer nofollow\" data-wpel-link=\"external\">Melhorando a Performance de Consultas no Totvs Protheus<\/a><\/p>\n<p>Gravei uma aula gr\u00e1tis com 60 minutos de dura\u00e7\u00e3o sobre o que voc\u00ea deve aprender para melhorar a performance no Protheus:<\/p>\n<div class=\"fluid-width-video-wrapper\"><a class=\"external-link wpel-icon-left\" href=\"https:\/\/www.youtube.com\/watch?v=BKmUa3aZn6s\" target=\"_blank\" rel=\"noopener noreferrer nofollow external\" data-wpel-link=\"external\">https:\/\/www.youtube.com\/watch?v=BKmUa3aZn6s<\/a><\/div>\n<div><\/div>\n<div><\/div>\n<div><\/div>\n<p><b>Gostou desse Post?<\/b><\/p>\n<p>Curta, comente, compartilhe com os coleguinhas\u2026<\/p>\n<p>Assine meu canal no\u00a0<a class=\"external-link wpel-icon-left\" href=\"https:\/\/www.youtube.com\/channel\/UCeBRAO_LLrUdSrOXIywjzRA\" target=\"_blank\" rel=\"external noopener noreferrer nofollow\" data-wpel-link=\"external\">Youtube<\/a>\u00a0e curta minha\u00a0<a class=\"external-link wpel-icon-left\" href=\"https:\/\/www.facebook.com\/FabricioLimaSolucoesemBancodeDados\/\" target=\"_blank\" rel=\"external noopener noreferrer nofollow\" data-wpel-link=\"external\">P\u00e1gina no Facebook<\/a>\u00a0para receber Dicas de Leituras e Eventos sobre SQL Server.<\/p>\n<p>Abra\u00e7os,<\/p>\n<p>Fabr\u00edcio Lima<\/p>\n<p>Microsoft Data Platform MVP<\/p>\n<p>Consultor e Instrutor SQL Server<\/p>\n<p>Trabalha com SQL Server desde 2006<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Fala pessoal, Estamos aqui para o segundo post da nossa s\u00e9rie que tem um epis\u00f3dio novo a cada semana. Antes de lerem esse post, sugiro verem o anterior para que sigam a sequ\u00eancia correta da s\u00e9rie: Melhorando a Performance de Consultas no Totvs Protheus \u2013 Parte 1 Agora que j\u00e1 sabem do que se trata [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_exactmetrics_skip_tracking":false,"_exactmetrics_sitenote_active":false,"_exactmetrics_sitenote_note":"","_exactmetrics_sitenote_category":0,"footnotes":""},"categories":[3,942,991,1248,33,280],"tags":[15,1031,1032,1101,1099,1100,1097,1098,1096,1048,1044,1103,942,1102,1595,1054,23,1029,1030,1088,1089,1025,1026,1049,1045,110,1034,1033,1597,1016,1040,1019,1598,1035,1042,1023,1024,1596,1046,1021,1020,1039,1053,1022,1018,1059,1047,1041,1036,1052,1060,1038,1051,33,1057,1017,1015,1055,1050,1043],"class_list":["post-8450","post","type-post","status-publish","format-standard","hentry","category-administracao-de-bd","category-consultoria-sql-server","category-dba-remoto","category-queries-do-dia-a-dia","category-sql-server","category-virtual-pass-br","tag-banco-de-dados","tag-banco-protheus","tag-banco-totvs","tag-consultor-crm","tag-consultor-datasul","tag-consultor-microsiga","tag-consultor-protheus","tag-consultor-rm","tag-consultor-totvs","tag-consultoria-microsiga","tag-consultoria-protheus","tag-consultoria-rm","tag-consultoria-sql-server","tag-consultoria-totvs","tag-crm-datasul-datasul-erp","tag-datasul-totvs","tag-dba","tag-dba-protheus","tag-dba-totvs","tag-dica-banco-de-dados-protheus","tag-dicas-banco-de-dados-protheus","tag-erp","tag-erp-com-sql-server","tag-erp-microsiga","tag-erp-protheus","tag-indice","tag-lentidao-banco-de-dados","tag-lentidao-protheus","tag-lentidao-protheus-12","tag-microsiga","tag-microsiga-protheus","tag-microsiga-sql-server","tag-migracao-protheus-12","tag-performance-protheus","tag-protheus","tag-protheus-10","tag-protheus-11","tag-protheus-12","tag-protheus-microsiga","tag-protheus-microsoft-sql-server","tag-protheus-sql-server","tag-protheus-totvs","tag-rm-sistemas","tag-rm-sql-server","tag-siga","tag-sistema-crm","tag-sistema-microsiga","tag-sistema-protheus","tag-sistema-rm","tag-sistema-rm-totvs","tag-sistema-wms","tag-sistemas-de-gestao","tag-sistemas-erp","tag-sql-server","tag-suporte-totvs","tag-top-connect","tag-totvs","tag-totvs-datasul","tag-totvs-microsiga","tag-totvs-protheus"],"post_mailing_queue_ids":[],"_links":{"self":[{"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/posts\/8450","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/comments?post=8450"}],"version-history":[{"count":12,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/posts\/8450\/revisions"}],"predecessor-version":[{"id":13845,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/posts\/8450\/revisions\/13845"}],"wp:attachment":[{"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/media?parent=8450"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/categories?post=8450"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fabriciolima.net\/blog\/wp-json\/wp\/v2\/tags?post=8450"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}