Continuando o tema de organizar o Zero Touch PROD no RDS novamente. Os futuros DBAs não poderão se conectar aos servidores PROD diretamente, mas poderão usar os trabalhos
Jenkins para um conjunto limitado de operações. O DBA inicia o trabalho e, após algum tempo, recebe uma carta com um relatório sobre a conclusão desta operação. Vejamos maneiras de apresentar esses resultados ao usuário.

Texto simples
Vamos começar com o mais trivial. O primeiro método é tão simples que não há o que falar em geral (o autor a seguir usa os trabalhos do FreeStyle):
O sqlcmd faz alguma coisa e nós a apresentamos ao usuário. Ideal para, por exemplo, tarefas de backup:

A propósito, não esquecemos que, no RDS, o backup / restauração é assíncrono; portanto, devemos esperar por isso:
declare @rds table (id int, task_type varchar(128), database_name sysname, pct int, duration int, lifecycle varchar(128), taskinfo varchar(max) null, upd datetime, cre datetime, s3 varchar(256), ovr int, KMS varchar(256) null) waitfor delay '00:00:20' insert into @rds exec msdb.dbo.rds_task_status @db_name='{db}' select @xid=max(id) from @rds again: waitfor delay '00:00:02' delete from @rds insert into @rds exec msdb.dbo.rds_task_status @db_name='{db}'
A segunda maneira, CSV
Tudo aqui também é muito simples:

No entanto, esse método funciona apenas se os dados retornados ao CSV forem "simples". Se você tentar retornar, por exemplo, a lista de consultas intensivas da CPU N TOP, o CSV "corroerá" devido ao fato de o texto da consulta poder conter caracteres - vírgulas, aspas e até quebras de linha. Portanto, precisamos de algo mais complicado.
Lindas tags HTML
Vou lhe dar um trecho de código imediatamente
$Header = @" <style> TABLE {border-width: 1px; border-style: solid; border-color: black; border-collapse: collapse;} TH {border-width: 1px; padding: 3px; border-style: solid; border-color: black; background-color: #6495ED;} TD {border-width: 1px; padding: 3px; border-style: solid; border-color: black;} </style> "@ $Result = invoke-Sqlcmd -ConnectionString $jstr -Query "select * from DbInv" ` | Select-Object -Property * -ExcludeProperty "ItemArray", "RowError", "RowState", "Table", "HasErrors" if ($Result -eq $null) { $cnt = 0; } elseif ($Result.getType().FullName -eq "System.Management.Automation.PSCustomObject") { $cnt = 1; } else { $cnt = $Result.Rows.Count; } if ($cnt -gt 0) { $body = "<h2>My table</h2>" $Result | ConvertTo-HTML -Title "Rows" -Head $header -body $body ` | Out-File "res.log" -Append -Encoding UTF8 } else { "<h3>No data</h3>" | Out-File "res.log" -Append -Encoding UTF8 }
A propósito, preste atenção à linha com System.Management.Automation.PSCustomObject, é mágico, se houver exatamente uma linha na grade, ocorreram alguns problemas. A decisão foi tomada na Internet sem muita compreensão. Como resultado, você obtém a saída, escrita algo como isto:

Desenhar gráficos
Atenção: código pervertido abaixo!
Há uma pergunta engraçada ao servidor SQL que exibe a CPU nos últimos N minutos - acontece que o camarada Major se lembra de tudo! Tente este:
DECLARE @ts_now bigint = (SELECT cpu_ticks/(cpu_ticks/ms_ticks) FROM sys.dm_os_sys_info WITH (NOLOCK)); SELECT TOP(256) DATEADD(ms, -1 * (@ts_now - [timestamp]), GETDATE()) AS [EventTime], SQLProcessUtilization AS [SQLCPU], 100 - SystemIdle - SQLProcessUtilization AS [OtherCPU] FROM (SELECT record.value('(./Record/@id)[1]', 'int') AS record_id, record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') AS [SystemIdle], record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]', 'int') AS [SQLProcessUtilization], [timestamp] FROM (SELECT [timestamp], CONVERT(xml, record) AS [record] FROM sys.dm_os_ring_buffers WITH (NOLOCK) WHERE ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR' AND record LIKE N'%<SystemHealth>%') AS x) AS y ORDER BY 1 DESC OPTION (RECOMPILE);
Agora, usando esta formatação (variável $ Fragment)
<table style="width: 100%"><tbody><tr style="background-color: white; height: 2pt;"> <td style="width: SQLCPU%; background-color: green;"></td> <td style="width: OtherCPU%; background-color: blue;"></td> <td style="width: REST%; background-color: #C0C0C0;"></td></tr></tbody> </table>
Podemos formar o corpo da carta:
$Result = invoke-Sqlcmd -ConnectionString $connstr -Query $Query ` | Select-Object -Property * -ExcludeProperty ` "ItemArray", "RowError", "RowState", "Table", "HasErrors" if ($Result.HasRows) { foreach($item in $Result) { $time = $itemEventTime $sqlcpu = $item.SQLCPU $other = $itemOtherCPU $rest = 100 - $sqlcpu - $other $f = $fragment -replace "SQLCPU", $sqlcpu $f = $f -replace "OtherCPU", $other $f = $f -replace "REST", $rest $f | Out-File "res.log" -Append -Encoding UTF8 }
O que ficaria assim:

Sim, Monsieur sabe muito sobre perversões! Curiosamente, esse código contém: PowerShell (escrito nele), SQL, Xquery, HTML. É uma pena que não possamos adicionar Javascript ao HTML (já que isso é para escrever), mas é dever de todos criar um código Python (que pode ser usado no SQL)!
Saída de Rastreio do Profiler SQL
É claro que o rastreio não se encaixará no CSV devido ao campo TextData. Mas também é estranho exibir a grade em uma carta, tanto por causa do tamanho quanto porque esses dados costumam ser usados para análises posteriores. Portanto, fazemos o seguinte:
invocamos através de
invoke-SqlCmd um determinado script, nas entranhas das quais é feito
select SPID,EventClass,TextData, Duration,Reads,Writes,CPU, StartTime,EndTime,DatabaseName,HostName, ApplicationName,LoginName from ::fn_trace_gettable ( @filename , default )
Além disso, em
outro servidor acessível pelo DBA, há uma base Traces com um espaço em branco vazio, uma placa Modelo, pronta para aceitar todas as colunas especificadas. Copiamos esse modelo para uma nova tabela com um nome exclusivo:
$dt = Get-Date -format "yyyyMMdd" $tm = Get-Date -format "hhmmss" $tableName = $srv + "_" + $dt + "_" + $tm $copytab = "select * into " + $tableName + " from Model" invoke-SqlCmd -ConnectionString $tstr -Query $copytab
E agora podemos escrever nosso rastreamento usando
Data.SqlClient.SqlBulkCopy - eu já dei um exemplo disso acima. Sim, seria bom mascarar constantes no TextData:
Substituímos números por mais de um caractere com um comprimento de 999 e substituímos cadeias de caracteres maiores que um caractere por 'str'. Os números de 0 a 9 são frequentemente usados como sinalizadores, e não os tocamos, nem como cadeias de caracteres vazias e de caractere único - entre eles, freqüentemente são encontrados 'Y', 'N' etc.
Adicione cores às nossas vidas (com mais de 18 anos)
Nos comprimidos, você geralmente deseja destacar as células que requerem atenção. Por exemplo, FAILS, um alto nível de fragmentação etc. Obviamente, isso também pode ser feito em SQL simples, formando HTML usando PRINT, e no Jenkins você pode definir o tipo de arquivo HTML:
declare @body varchar(max), @chunk varchar(max) set @body='<font face="Lucida Console" size="3">' set @body=@body+'<b>Server name: '+@@servername+'</b><br>' set @body=@body+'<br><br>' set @body=@body+'<table><tr><th>Job</th><th>Last Run</th><th>Avg Duration, sec</th><th>Last Run, Sec</th><th>Last Status</th></tr>' print @body DECLARE tab CURSOR FOR SELECT '<tr><td>'+name+'</td><td>'+ LastRun+'</td><td>'+ convert(varchar,AvgDuration)+'</td><td>'+ convert(varchar,LastDuration)+'</td><td>'+ case when LastStatus<>'Succeeded' then '<font color="red">' else '' end+ LastStatus+ case when LastStatus<>'Succeeded' then '</font>' else '' end+ +'</td><td>' from
Por que eu escrevi esse código?

Mas há uma solução mais bonita.
ConvertTo-HTML não nos permite colorir as células, mas podemos fazê-lo após o fato. Por exemplo, queremos selecionar células com um nível de fragmentação superior a 80 e superior a 90. Adicione estilos:
<style> .SQLmarkup-red { color: red; background-color: yellow; } .SQLmarkup-yellow { color: black; background-color: #FFFFE0; } .SQLmarkup-default { color: black; background-color: white; } </style>
No próprio qi, adicionaremos uma coluna fictícia
diretamente à coluna que queremos colorir. A coluna deve ser chamada
SQLmarkup- something:
case when ps.avg_fragmentation_in_percent>=90.0 then 'SQLmarkup-red' when ps.avg_fragmentation_in_percent>=80.0 then 'SQLmarkup-yellow' else 'SQLmarkup-default' end as [SQLmarkup-1], ps.avg_fragmentation_in_percent,
Agora, tendo recebido o HTML criado pelo Powershell, removeremos a coluna dummy do cabeçalho e transferiremos o valor da coluna para o estilo no corpo dos dados. Isso é feito em apenas duas permutações:
$html = $html ` -replace "<th>SQLmarkup[^<]*</th>", "" ` -replace "<td>SQLmarkup-(.+?)</td><td>",'<td class="SQLmarkup-$1">'
Resultado:

Não é elegante? Embora não, algo que esta coloração me lembra
