Hey guys!
Nesse artigo eu quero trazer pra vocês uma solução que criei ontem para incluir em um check-list que estou desenvolvendo, que me permite identificar a última versão do Service Pack (SP) e Cumulative Update (CU) para a versão do SQL Server em que estou executando esse código.
Para identificar os últimos updates para cada versão, vou utilizar o site https://sqlserverbuilds.blogspot.com/, que está sempre atualizado com as últimas atualizações do SQL Server (também poderia ser o https://buildnumbers.wordpress.com/sqlserver/).
Para realizar a extração dos dados da página para o SQL Server, vou utilizar o recurso OLE Automation, que já publiquei vários artigos explicando como realizar WebScraping de páginas WEB e API’s utilizando esse recurso.
Após recuperar o código-fonte HTML da página, preciso identificar a tabela que contém os dados de acordo com a versão que eu estou buscando e também preciso realizar alguns tratamentos no código retornado para corrigir algumas inconsistências:
- Conversão de entidades HTML (ex:   )
- Atributos de tags sem aspas (ex: class=sp ao invés de class=”sp” = HTML inválido)
- Tags abertas e não fechadas (ex: <td>Coluna1<td>Coluna2 ao invés de <td>Coluna1</td><td>Coluna2</td>)
Essa acaba sendo a parte mais trabalhosa desse script, porque se essas inconsistências no código não forem sanadas, o SQL Server me retorna erro ao tentar converter o XHTML para XML.
Após realizar todos esses tratamentos e converter o código XHTML para XML, aplico comandos XQuery para identificar a primeira linha da tabela da respectiva versão do SQL Server e retorno na SP
Código-fonte
Após um breve resumo do que precisei fazer para conseguir realizar essa tarefa, vou disponibilizar abaixo o código-fonte da solução criada, que pode ser utilizada com um simples “F5”.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
IF (OBJECT_ID('dbo.stpRetorna_Ultima_Versao_SQL') IS NULL) EXEC('CREATE PROCEDURE dbo.stpRetorna_Ultima_Versao_SQL AS SELECT 1') GO ALTER PROCEDURE dbo.stpRetorna_Ultima_Versao_SQL ( @Versao VARCHAR(20) = NULL ) AS BEGIN -------------------------------------------------------------------------------- -- Habilitando o OLE Automation (Se não estiver ativado) -------------------------------------------------------------------------------- DECLARE @Fl_Ole_Automation_Ativado BIT = (SELECT (CASE WHEN CAST([value] AS VARCHAR(MAX)) = '1' THEN 1 ELSE 0 END) FROM sys.configurations WHERE [name] = 'Ole Automation Procedures') IF (@Fl_Ole_Automation_Ativado = 0) BEGIN EXECUTE sp_configure 'show advanced options', 1 RECONFIGURE WITH OVERRIDE EXEC sp_configure 'Ole Automation Procedures', 1 RECONFIGURE WITH OVERRIDE END DECLARE @obj INT, @Url VARCHAR(8000), @xml VARCHAR(MAX), @resposta VARCHAR(MAX) SET @Url = 'http://sqlserverbuilds.blogspot.com/' EXEC sys.sp_OACreate 'MSXML2.ServerXMLHTTP', @obj OUT EXEC sys.sp_OAMethod @obj, 'open', NULL, 'GET', @Url, false EXEC sys.sp_OAMethod @obj, 'send' DECLARE @xml_versao_sql TABLE ( Ds_Dados VARCHAR(MAX) ) INSERT INTO @xml_versao_sql(Ds_Dados) EXEC sys.sp_OAGetProperty @obj, 'responseText' --, @resposta OUT EXEC sys.sp_OADestroy @obj -------------------------------------------------------------------------------- -- Desativando o OLE Automation (Se não estava habilitado antes) -------------------------------------------------------------------------------- IF (@Fl_Ole_Automation_Ativado = 0) BEGIN EXEC sp_configure 'Ole Automation Procedures', 0 RECONFIGURE WITH OVERRIDE EXECUTE sp_configure 'show advanced options', 0 RECONFIGURE WITH OVERRIDE END DECLARE @Versao_SQL_Build VARCHAR(20) IF (@Versao IS NOT NULL) SET @Versao_SQL_Build = @Versao ELSE BEGIN SET @Versao_SQL_Build = (CASE LEFT(CONVERT(VARCHAR, SERVERPROPERTY('ProductVersion')), 2) WHEN '8.' THEN '2000' WHEN '9.' THEN '2005' WHEN '10' THEN ( CASE WHEN LEFT(CONVERT(VARCHAR, SERVERPROPERTY('ProductVersion')), 4) = '10.5' THEN '2008 R2' WHEN LEFT(CONVERT(VARCHAR, SERVERPROPERTY('ProductVersion')), 4) = '10.0' THEN '2008' END) WHEN '11' THEN '2012' WHEN '12' THEN '2014' WHEN '13' THEN '2016' WHEN '14' THEN '2017' WHEN '15' THEN '2019' ELSE '2019' END) END SELECT TOP 1 @resposta = Ds_Dados FROM @xml_versao_sql SET @xml = @resposta COLLATE SQL_Latin1_General_CP1251_CS_AS DECLARE @PosicaoInicialVersao INT, @PosicaoFinalVersao INT, @ExpressaoBuscar VARCHAR(100) = 'Microsoft SQL Server ' + @Versao_SQL_Build + ' Builds', @RetornoTabela VARCHAR(MAX), @dadosXML XML SET @PosicaoInicialVersao = CHARINDEX(@ExpressaoBuscar, @xml) + LEN(@ExpressaoBuscar) + 6 SET @PosicaoFinalVersao = CHARINDEX('</table>', @xml, @PosicaoInicialVersao) SET @RetornoTabela = SUBSTRING(@xml, @PosicaoInicialVersao, @PosicaoFinalVersao - @PosicaoInicialVersao + 8) -- Corrigindo classes sem aspas duplas ("") SET @RetornoTabela = REPLACE(@RetornoTabela, ' border=1 cellpadding=4 cellspacing=0 bordercolor="#CCCCCC" style="border-collapse:collapse"', '') SET @RetornoTabela = REPLACE(@RetornoTabela, ' target=_blank rel=nofollow', ' target="_blank" rel="nofollow"') SET @RetornoTabela = REPLACE(@RetornoTabela, ' class=h', '') SET @RetornoTabela = REPLACE(@RetornoTabela, ' class=lsp', '') SET @RetornoTabela = REPLACE(@RetornoTabela, ' class=cu', '') SET @RetornoTabela = REPLACE(@RetornoTabela, ' class=sp', '') SET @RetornoTabela = REPLACE(@RetornoTabela, ' class=rtm', '') SET @RetornoTabela = REPLACE(@RetornoTabela, ' width=580', '') SET @RetornoTabela = REPLACE(@RetornoTabela, ' width=125', '') SET @RetornoTabela = REPLACE(@RetornoTabela, ' class=lcu', '') SET @RetornoTabela = REPLACE(@RetornoTabela, ' class=cve', '') SET @RetornoTabela = REPLACE(@RetornoTabela, ' class=lrtm', '') SET @RetornoTabela = REPLACE(@RetornoTabela, ' class=beta', '') -- Corrigindo elementos não fechados corretamente SET @RetornoTabela = REPLACE(@RetornoTabela, '<th>', '</th><th>') SET @RetornoTabela = REPLACE(@RetornoTabela, '<tr></th>', '<tr>') SET @RetornoTabela = REPLACE(@RetornoTabela, '<th>Build<th ', '<th>Build</th><th ') SET @RetornoTabela = REPLACE(@RetornoTabela, '<th>Release Date</tr>', '<th>Release Date</th></tr>') SET @RetornoTabela = REPLACE(@RetornoTabela, '<td>', '</td><td>') SET @RetornoTabela = REPLACE(@RetornoTabela, '<tr></td>', '<tr>') SET @RetornoTabela = REPLACE(@RetornoTabela, '</tr>', '</td></tr>') SET @RetornoTabela = REPLACE(@RetornoTabela, '</th></td>', '</th>') SET @RetornoTabela = REPLACE(@RetornoTabela, '</td></td>', '</td>') -- Removendo elementos de entidades HTML SET @RetornoTabela = REPLACE(@RetornoTabela, ' ', ' ') SET @RetornoTabela = REPLACE(@RetornoTabela, '&kbln', '&kbln') SET @RetornoTabela = REPLACE(@RetornoTabela, '<br>', '<br/>') SET @dadosXML = CONVERT(XML, @RetornoTabela) DECLARE @Atualizacoes_SQL_Server TABLE ( [Ultimo_Build] VARCHAR(100), [Ultimo_Build_SQLSERVR.EXE] VARCHAR(100), [Versao_Arquivo] VARCHAR(100), [Q] VARCHAR(100), [KB] VARCHAR(100), [Descricao_KB] VARCHAR(500), [Lancamento_KB] VARCHAR(100), [Download_Ultimo_Build] VARCHAR(100) ) INSERT INTO @Atualizacoes_SQL_Server SELECT @dadosXML.value('(//table/tr/td[1])[1]','varchar(100)') AS Ultimo_Build, @dadosXML.value('(//table/tr/td[2])[1]','varchar(100)') AS [Ultimo_Build_SQLSERVR.EXE], @dadosXML.value('(//table/tr/td[3])[1]','varchar(100)') AS Versao_Arquivo, @dadosXML.value('(//table/tr/td[4])[1]','varchar(100)') AS [Q], @dadosXML.value('(//table/tr/td[5])[1]','varchar(100)') AS KB, @dadosXML.value('(//table/tr/td[6]/a)[1]','varchar(500)') AS Descricao_KB, @dadosXML.value('(//table/tr/td[7])[1]','varchar(100)') AS Lancamento_KB, @dadosXML.value('(//table/tr/td[6]/a/@href)[1]','varchar(100)') AS Download_Ultimo_Build DECLARE @Url_Ultima_Versao_SQL VARCHAR(500) = (SELECT TOP(1) Download_Ultimo_Build FROM @Atualizacoes_SQL_Server), @Ultimo_Build VARCHAR(100) = (SELECT TOP(1) Ultimo_Build FROM @Atualizacoes_SQL_Server), @Descricao_KB VARCHAR(500) = (SELECT TOP(1) Descricao_KB FROM @Atualizacoes_SQL_Server) IF (@Versao IS NOT NULL) BEGIN SELECT * FROM @Atualizacoes_SQL_Server END ELSE BEGIN IF (CONVERT(VARCHAR(100), SERVERPROPERTY('ProductVersion')) >= @Ultimo_Build) SELECT 'SQL Server ATUALIZADO!' AS SQL_Server_Atualizado ELSE BEGIN SELECT 'SQL Server DESATUALIZADO!!' AS SQL_Server_Desatualizado UNION ALL SELECT '----------------------------------------' UNION ALL SELECT 'Versão mais atual: ' + @Ultimo_Build + ' (' + @Descricao_KB + ')' UNION ALL SELECT 'Download da versão mais atual: ' UNION ALL SELECT @Url_Ultima_Versao_SQL UNION ALL SELECT '----------------------------------------' UNION ALL SELECT 'Sua versão: ' + CONVERT(VARCHAR(100), SERVERPROPERTY('ProductVersion')) + ' (' + CONVERT(VARCHAR(100), SERVERPROPERTY('ProductLevel')) + ')' SELECT * FROM @Atualizacoes_SQL_Server END END END |
Exemplos de uso
Agora vou demonstrar alguns exemplos de uso de como essa Stored Procedure funciona na prática.
Recuperando a última atualização a partir de um SQL Server 2017 atualizado
1 2 3 4 |
SELECT @@VERSION -- Retorna a última versão do SQL Server instalado na instância atual EXEC dbo.stpRetorna_Ultima_Versao_SQL |
Recuperando a última atualização a partir de um SQL Server 2017
1 2 3 4 |
SELECT @@VERSION -- Retorna a última versão do SQL Server instalado na instância atual EXEC dbo.stpRetorna_Ultima_Versao_SQL |
Recuperando a última atualização a partir de um SQL Server 2008 R2
1 2 3 4 |
SELECT @@VERSION -- Retorna a última versão do SQL Server instalado na instância atual EXEC dbo.stpRetorna_Ultima_Versao_SQL |
Recuperando a última atualização a partir de um SQL Server 2019
1 2 3 4 |
SELECT @@VERSION -- Retorna a última versão do SQL Server instalado na instância atual EXEC dbo.stpRetorna_Ultima_Versao_SQL |
Preciso mostrar TODAS as atualizações de uma versão do SQL Server
Caso você queira mostrar todas as atualizações da versão desejada do SQL Server e não só a última, pode utilizar o código abaixo (após a linha 150):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
SELECT Versoes.linha.value('(td[1])[1]','varchar(100)') AS Ultimo_Build, Versoes.linha.value('(td[2])[1]','varchar(100)') AS [Ultimo_Build_SQLSERVR.EXE], Versoes.linha.value('(td[3])[1]','varchar(100)') AS Versao_Arquivo, Versoes.linha.value('(td[4])[1]','varchar(100)') AS [Q], Versoes.linha.value('(td[5])[1]','varchar(100)') AS KB, (CASE WHEN Versoes.linha.value('(td[6]/a)[1]','varchar(500)') IS NOT NULL THEN Versoes.linha.value('(td[6]/a)[1]','varchar(500)') ELSE Versoes.linha.value('(td[6])[1]','varchar(500)') END) AS Descricao_KB, Versoes.linha.value('(td[7])[1]','varchar(100)') AS Lancamento_KB, Versoes.linha.value('(td[6]/a/@href)[1]','varchar(100)') AS Download_Ultimo_Build FROM @dadosXML.nodes('//table/tr') Versoes(linha) WHERE Versoes.linha.value('(td[1])[1]','varchar(100)') IS NOT NULL |
Conclusão
Bom pessoal, espero que vocês tenham gostado desse artigo. Estou começando a utilizá-lo em ambientes para fazer um check-up rápido e ele acabou sendo bem prático. Caso você queira criar um monitoramento para isso em seu ambiente, analisando todas as instâncias, pode criar essa Stored Procedure em todas as instâncias e utilizar os dados retornados para enviar e-mails automáticos conforme a sua necessidade.
Nesse cenário de monitoramento, uma outra solução muito interessante é a do Reginaldo Silva, que fez o post Get-SQLBuildVersion uma forma diferente para se manter atualizado, onde ele compartilhou uma solução escrita em PowerShell, que ele desenvolveu, onde ele passa o nome da instância como parâmetro e o script já faz essa validação e parâmetros para enviar o resultado por e-mail. Muito prático para usar em várias instâncias.
Assim como o Reginaldo, vou destacar o artigo Cumulative update, aplicar ou não, eis a questão! do Luciano Moreira (Luti), onde ele expõe suas opiniões sobre a aplicação constante de atualizações do SQL Server (concordo com ele), visto que muitos DBA’s não tem o hábito de manter o SQL Server atualizado com receio de introduzir um bug novo após a aplicação de alguma atualização.
Na minha visão, isso não faz muito sentido nos dias de hoje, pois esses acontecimentos são bem raros e, ao aplicar sempre as atualizações, seu ambiente fica sempre atualizado com novos recursos, correções de bugs e melhorias em segurança, além de ter um processo de aplicação de updates mais maduro e comum no dia a dia da empresa.
Ah, vale lembrar que a partir do SQL Server 2017, não teremos mais Service Packs, apenas Cumulative Updates, conforme foi documentado nesse artigo aqui.
É isso aí, pessoal.
Espero que tenham gostado desse artigo e até mais!
Olá Dirceu, parabéns por compartilhar o conteúdo, será de grande valia. Eu executei o script de criação da procedure e ao executar, retornou o seguinte erro:
(1 row affected)
Msg 9436, Level 16, State 1, Procedure dbo.sp_Retorna_Ultima_Versao_SQL, Line 147 [Batch Start Line 30]
XML parsing: line 3, character 202, end tag does not match start tag
Verifiquei no código e na referida linha o código está da seguinte forma:
SET @RetornoTabela = REPLACE(@RetornoTabela, ”, ”)
Coloquei a “/” antes do “br”, mas mesmo assim o erro permanece. Pode me ajudar?
Antecipadamente Agradeço.
Sobre atualização do software SQL Server, cabe destacar que no momento temos dois modelos de manutenção ativos: o Incremental Servicing Model (ISM) e o Modern Servicing Model (MSM), onde o MSM se aplica somente para a versão 2017 (e posteriores) do SQL Server ao passo que o ISM se aplica para versões até 2016, inclusive.
Cabe destacar que no modelo ISM continuam sendo liberados novos service packs (além dos cumulative updates, obviamente).
O lançamento do MSM foi explicado no post “Announcing the Modern Servicing Model for SQL Server”, publicado no blog oficial “SQL Server Release Updates”, que serviu de partida para a publicação do artigo “Atualização de software SQL Server” [ https://social.technet.microsoft.com/wiki/pt-br/contents/articles/51093.atualizacao-de-software-sql-server.aspx ], onde estão descritas as características dos modelos ISM e MSM bem como glossário com a sopa de siglas (COD, CU, RTM, GDR etc).
Como curiosidade, no Gallery do Microsoft TechNet há o script “Determining the version and edition of SQL Server and its components” [ https://gallery.technet.microsoft.com/Determining-the-version-dbc1c53a ] que utiliza técnica diferente para determinar qual atualização aplicar, em versões T-SQL e PS.
José Diz (Porto SQL)
Obrigado pelos comentários, José! Grande abraço