Postgres – Criar estatísticas

O Postgres é “tão evoluído” e blá blá blá, mas não consegue ter uma rotina de criação automática de estatísticas. Parece que voltamos para antes dos anos 2000.

Bom, não vou explicar a importância da criação de estatísticas, isso você já deveria saber, se não sabe, pergunta para sua IA favorita.

A código abaixo vai ler a tabela pg_stat_statements e, a partir dela, eu tento fazer um monte de regex para separar das querys a parte do SARG para criar as estatísticas.

É preciso adicionar a extensão pg_stat_statements:

create extension pg_stat_statements;

Dependendo do seu workload logo em seguida você já vai ter acesso a alguma coisa, mas para ter dados melhores, é melhor deixar o tempo passar para acumular mais informações.

Depois de algum tempo, quando você rodar o script abaixo, deve ter um resultado mais interessante para criar as estatísticas:

WITH normalized AS (SELECT queryid
                         , query
                         -- Remove DO $ ... $ e normaliza espaços
                         , lower(regexp_replace(
            regexp_replace(query, '^do \$\$|\\$\$;$', '', 'gi'),
            '\s+', ' ', 'g'
                                 )) AS norm_query
                         , total_exec_time
                         , calls
                    FROM pg_stat_statements
                    WHERE query ILIKE 'select%'
                      AND calls > 10
                      AND query ~* ' where ')
   , tables AS (SELECT n.queryid
                     , n.query
                     , n.norm_query
                     , n.total_exec_time
                     , n.calls
                     -- Captura tabela e alias
                     , (regexp_match(n.norm_query,
                                     '(?:from|join)\s+([a-z0-9_\.]+)(?:\s+as\s+|\s+)?([a-z0-9_]+)?'))[1] AS table_obj_full
                     , (regexp_match(n.norm_query,
                                     '(?:from|join)\s+([a-z0-9_\.]+)(?:\s+as\s+|\s+)?([a-z0-9_]+)?'))[2] AS table_alias
                     , COALESCE(
            (regexp_match(n.norm_query, '(?:from|join)\s+([a-z0-9_\.]+)(?:\s+as\s+|\s+)?([a-z0-9_]+)'))[2],
            split_part((regexp_match(n.norm_query, '(?:from|join)\s+([a-z0-9_\.]+)'))[1], '.', 2)
                       )                                                                                 AS main_identifier_raw
                     , split_part(
            lower(
                    regexp_replace(
                            (regexp_match(n.norm_query, '(?:from|join)\s+([a-z0-9_\.]+)'))[1],
                            '[^a-z0-9_\.]',
                            '',
                            'g'
                    )
            ),
            '.',
            CASE WHEN (regexp_match(n.norm_query, '(?:from|join)\s+([a-z0-9_\.]+)'))[1] LIKE '%.%' THEN 2 ELSE 1 END
                       )                                                                                 AS table_name
                FROM normalized n
                WHERE (regexp_match(n.norm_query, '(?:from|join)\s+([a-z0-9_\.]+)'))[1] IS NOT NULL)
   , where_clauses AS (SELECT t.queryid
                            , t.table_name
                            , lower(t.main_identifier_raw) AS main_identifier
                            , (regexp_match(
            t.norm_query,
            'where\s+(.*?)(?:\sgroup by|\sorder by|\slimit|;|$)'
                               ))[1]                       AS where_block
                            , t.total_exec_time
                            , t.calls
                       FROM tables t)
   , where_columns AS (SELECT wc.table_name
                            , wc.main_identifier
                            , CASE
                                  WHEN rm.m[1] LIKE '%.%' THEN lower(split_part(rm.m[1], '.', 1))
                                  ELSE NULL END                                 AS column_prefix
                            , rm.m[1]                                           AS full_column_name
                            , regexp_replace(rm.m[1], '^[a-z0-9_]+\.', '', 'i') AS column_name
                            , wc.total_exec_time
                       FROM where_clauses wc
                                CROSS JOIN LATERAL regexp_matches(
                               wc.where_block,
                               '([a-z_][a-z0-9_\.]*)\s*(=|>|<|>=|<=|<>|in\b|like\b)',
                               'gi'
                                                   ) AS rm(m))
   , distinct_columns AS (SELECT table_name
                               , column_name
                               , SUM(total_exec_time) AS total_cost
                          FROM where_columns
                          WHERE column_name IS NOT NULL
                            AND (
                              column_prefix IS NULL
                                  OR column_prefix = main_identifier
                                  OR column_prefix = table_name
                              )
                          GROUP BY table_name, column_name)
   , validated_columns AS (SELECT dc.table_name
                                , dc.column_name
                                , dc.total_cost
                           FROM distinct_columns dc
                                    JOIN information_schema.columns isc
                                         ON lower(isc.table_name) = lower(dc.table_name)
                                             AND lower(isc.column_name) = lower(dc.column_name)
                           WHERE dc.table_name NOT LIKE 'pg_%')
   , family AS (SELECT vc.table_name
                     , (SELECT array_agg(c)
                        FROM (SELECT column_name AS c
                              FROM validated_columns sub
                              WHERE sub.table_name = vc.table_name
                              ORDER BY total_cost DESC
                              LIMIT 8) sub2)       AS columns
                     , COUNT(DISTINCT column_name) AS occurrences
                     , SUM(total_cost)             AS total_table_cost
                FROM validated_columns vc
                GROUP BY vc.table_name
                HAVING COUNT(DISTINCT column_name) > 1)
   , correlation_estimate AS (SELECT f.*
                                   , (array_length(f.columns, 1) ^ 1.3) * ln(total_table_cost + 5) AS score
                              FROM family f)
   , existing_ext_stats AS (SELECT cls.relname                                         AS table_name
                                 , st.stxname                                          AS stat_name
                                 , array_agg(att.attname ORDER BY att.attname)::text[] AS stat_columns
                            FROM pg_statistic_ext st
                                     JOIN pg_class cls ON cls.oid = st.stxrelid
                                     JOIN pg_attribute att
                                          ON att.attrelid = cls.oid
                                              AND att.attnum = ANY (st.stxkeys)
                            GROUP BY cls.relname, st.stxname)
   , dedup AS (SELECT ce.*
                    , 's_' || regexp_replace(ce.table_name, '[^a-z0-9_]', '', 'g') ||
                      '_' || substr(md5(array_to_string(ce.columns, ',')), 1, 8) AS stat_name
                    , EXISTS (SELECT 1
                              FROM existing_ext_stats es
                              WHERE es.table_name = ce.table_name
                                AND es.stat_columns = ce.columns::text[])        AS already_exists
               FROM correlation_estimate ce)

SELECT table_name
     , columns
     , occurrences
     , round(score::numeric, 2)                                AS score
     , stat_name
     , 'CREATE STATISTICS IF NOT EXISTS ' ||
       quote_ident(stat_name) ||
       ' ON ' ||
       array_to_string(
               ARRAY(SELECT quote_ident(c) FROM unnest(columns) c), ', '
       ) ||
       ' FROM ' ||
       quote_ident(table_name) ||
       ';'                                                     AS suggested_cmd
     , 'Filtros mais custosos sobre (' ||
       array_to_string(columns, ', ') ||
       ') em ' || table_name ||
       '. Ocorrências: ' || occurrences ||
       '. Custo total estimado: ' || total_table_cost || 'ms.' AS justification
FROM dedup
WHERE NOT already_exists
  AND table_name NOT LIKE 'pg_%'
  AND table_name NOT IN ('table_constraints', 'columns', 'tables')
  AND array_length(columns, 1) > 1
ORDER BY score DESC, total_table_cost DESC, occurrences DESC;

GCP – CloudSQL – Painel de Governança e updates

Eu precisava de um lugar fácil para ver todos os bancos de dados na nuvem do Google com informações de versões e atualizações disponíveis e as janelas dessas atualizações.

Usando como base o python do post [GCP – Executar script em várias instâncias pg], agora é possível listar todas as instâncias em todos os projetos com a versão do banco, qual a atualização disponível e para quando está agendada, bem como a janela de manutenção para a instância.

Também é possível baixar a informação para um arquivo CSV e ordenar colunas.

Não é possível aplicar as atualizações através dessa interface justamente para evitar um efeito “ooopppssss”.

O Script com o código pode ser encontrado em:
https://github.com/bigleka/gcp/blob/main/dashboard_updates.py

Atualização 20251231

Criei uma versão powershell, pra focar na linha de comando e que acaba sendo mais prática já que não precisa de tanta dependência pra rodar.

https://github.com/bigleka/gcp/blob/main/Get-CloudSQLUpdates.ps1

é mais rápido, simples, bom,,, é linha de comando, não tem nada melhor.

SQL na Caixinha

Que o SQL pode rodar em container já não é novidade tem um tempinho.

A facilidade que isso nos trás para testar recursos, novidades, configurações, bugs, etc. ajudou demais.

Só o trabalho de subir um SO, configurar todo o SO, atualizações do SO, baixar a instalação do SQL, todo o processo de instalação, atualização, configuração já cansa só de lembrar.

Tá certo que com a vantagem na nuvem podemos subir qualquer configuração a qualquer momento, só dependendo do limite do cartão de crédito, mas com o Docker, da pra fazer basicamente a mesma coisa sem precisar de uma conta em alguma nuvem, sem ter que ficar instalando um monte de binário com um monte de biblioteca, não se preocupando se está no patch certo do SO, etc.

Basicamente com duas linhas de comando você consegue “rodar” qualquer SQL Server do 17 até o 22 em qualquer cumulative update que houve nesse meio tempo.

Para começar, instale o Docker Desktop (https://www.docker.com/get-started/);

Após alguns restarts e atualizações você deve ter ele pronto no seu PC.

Agora, no Terminal, PowerShell, CMD digite o comando abaixo:

docker pull mcr.microsoft.com/mssql/server

Espere ele carregar algumas configurações

Em seguida vem a mágica com esse segundo comando:

docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=yourStrong(!)Password" -p 1433:1433 -d mcr.microsoft.com/mssql/server:2022-latest

E pronto, basicamente só isso e você vai ter um SQL Server Developer Edition 2022 rodando no seu PC

Claro que tem seus detalhes, nessa configuração simples tudo o que acontece no docker fica dentro do docker, se você apagar o container todas as bases que você criou, registros, etc. serão apagadas, o backup também conta.

Por padrão ele não integra com autenticação windows.

Se você procurar nos serviços ele não aparece listado.

Basicamente para conectar é seu hostname e a porta 1433 com usuário SA e a senha digitada ali em cima.

Caso precise da lista de todas as releases que você pode subir com o Docker a lista encontra-se aqui (https://hub.docker.com/_/microsoft-mssql-server).

AWS – EC2 com SQL

Caso você contrate uma AMI com SQL e precise da mídia de instalação do SQL para qualquer atividade, na unidade C:\ existe um diretório chamado “SQLServerSetup” com os binários para a instalação do SQL Server.

Isso ajuda caso precise trocar o Collation da instância, adicionar feature, reinstalar usando uma instância, adicionar uma instância, etc..

A instalação padrão vem na instância default, collation SQL_Latin1_General_CP1_CI_AS, tempdb nas configurações NNF, sem IFI, basicamente uma instalação NNF.

Aí vem outra pergunta, por que pegar uma imagem da AWS com SQL? por que não usar um RDS?

Bom, a resposta disso é mais com você do que comigo, porque tudo vai depender da necessidade.

AMI – EC2 com SQL Instalado

  • As imagens da AWS com SQL instalado vem em diversos sabores, você escolhe o tamanho da máquina e o tipo de licenciamento STD ou ENT, eles tem developer mas se optar por esse developer você vai pagar um custo pela licença de uma aplicação que pode ser baixada gratuitamente, e ai o preço desse licenciamento do STD ou ENT vai depender do tamanho da máquina que você escolher, a vantagem fica justamente na questão de licenciamento, quem recolhe e paga para a Microsoft é a AWS, você é apenas uma empresa que está usando uma imagem já pré-instalada, então sem stress quando a licenciamento;
  • Toda a administração do ambiente e com você, eles só deixam o SQL instalado e o resto é o trabalho de casa, desde restaurar o banco até todas as rotinas de manutenção.

RDS

  • Basicamente o SQL como serviço
  • você não loga na máquina, não tem nenhum acesso a estrutura onde o SQL está instalado
  • você não é SA nem faz parte da role de Sysadmin
  • você é owner dos seus bancos
  • todas as rotinas de manutenção do SO e algumas do SQL são geridas pela AWS.
  • é uma administração meio a meio

Vou tratar da comparação entre uma AMI e um RDS em outro post.

AWS – Redshift – Usuário para leituras

O Redshift tem umas vantagens bem interessantes, baixo custo, RDS, baixa necessidade de manutenção.

No fundo ele é um PostgreSQL modificado para prover volume de dados e não ficar trabalhando como OLTP, ele é ótimo como estrutura para DW.

Imagine o seguinte cenário, você vende uma solução mas precisa prover um acesso do seu cliente para que ele consiga acessar uma parte dos dados diretamente na sua estrutura de banco, para ele “ter a liberdade” de cruzar esses dados, montar estruturas de relatórios, etc. da forma que ele achar mais interessante, ou até mesmo exportar esses dados para uma estrutura dele e usar da forma que achar melhor.

Certo, temos várias formas de fazer isso, todas tem seus prós e contras, mas nesse caso vou usar como exemplo justamente o título do post, vamos colocar os dados no Redshift.

Então, você tem alguma forma de extração de dados incrementais (SSIS, Pentaho, Informática, estagiário, etc.) que leva os dados do seu OLTP para o Redshift e isso funciona bem.

Agora você precisa criar a estrutura de permissões para liberar o acesso do seu cliente para essa estrutura de dados.

Uma coisa muito importante : Todos os usuários do Redshift são exclusivos do banco de dados e não da instância, Então caso o cliente tenha mais de um banco ou você queira dar permissão para mais de um banco, siga o processo quantas vezes for necessário.

— Normalmente quando os objetos são criados no Redshift ele ficam armazenados no schema public.
— Isso não é um problema, o problema começa quando é criado um schema para armazenar um outro conjunto de objetos
— para um setor da empresa, ou um outro departamento…
— Quando isso acontece, o usuário owner da carga dos objetos tem acesso a essa estrutura de dados sem problema, mas novos usuários,
— ou usuários permissonalizados não tem a permissão para os objetos ou para novos objetos nesse schema.
— O script abaixo tente a sanar um cenário em que você quer liberar o acesso de select para os objetos e novo objetos em um schema pulic
— ou personalizado sem ter que ficar dando grant toda a vez que novos objetos são criados.
— Outra opção de uso é caso você tenha um Redshift na sua empresa e venda como serviço ele como datalake para algum cliente.
— dessa forma você consegue liberar um usuário para que o cliente acesse a estrutura de dados e consiga baixar os dados.

-- Normalmente quando os objetos são criados no Redshift ele ficam armazenados no schema public.
-- Isso não é um problema, o problema começa quando é criado um schema para armazenar um outro conjunto de objetos
-- para um setor da empresa, ou um outro departamento...
-- Quando isso acontece, o usuário owner da carga dos objetos tem acesso a essa estrutura de dados sem problema, mas novos usuários,
-- ou usuários permissonalizados não tem a permissão para os objetos ou para novos objetos nesse schema.
-- O script abaixo tente a sanar um cenário em que você quer liberar o acesso de select para os objetos e novo objetos em um schema pulic
-- ou personalizado sem ter que ficar dando grant toda a vez que novos objetos são criados.
-- Outra opção de uso é caso você tenha um Redshift na sua empresa e venda como serviço ele como datalake para algum cliente.
-- dessa forma você consegue liberar um usuário para que o cliente acesse a estrutura de dados e consiga baixar os dados.

-- criar um usuário
create user <username> with password ‘<password>’;

-- cria um grupo para receber as permissões
create group data_viewers;

-- adiciona o usuário ao grupo
alter group data_viewers add user <username>;

-- nesse caso remove a opção de criar objetos para os usuários do grupo
revoke create on schema public from group data_viewers;

-- atribui acesso no schema public ao grupo
grant usage on schema public to group data_viewers;

-- atribui select em todas as tabelas do schema public para o grupo
grant select on all tables in schema public to group data_viewers;

-- atribui acesso a futuros objetos do schema public para o grupo
alter default privileges in schema public grant select on tables to group data_viewers

novos códigos serão criados também em outro repositório:

https://github.com/bigleka

Telegram, Bot e PowerShell, uma máquina de alerta

Como DBA´s estamos sujeitos a jobs, alertas, sistemas, coisas do além, gerentes, desenvolvedores, intervenções místicas, etc. executando ações no banco de dados.

Não da pra ficar olhando de perto o ambiente a todo o momento analisando cada ação, monitorando tudo o que ocorre, se preparando para o pior.

Sendo um bom DBA, você deve ter um monte de Jobs, Alertas, Operadores, Sistemas de Monitoração, aquele estagiário sendo escalpelado, entre outras formas de monitorar seus bancos, a ideia aqui é trazer mais uma opção para atazanar sua vida monótona.

Vamos a receita do desastre,,, Para isso vamos precisar:

  1. Que você tenha uma conta no Telegram
  2. Instale o aplicativo do Telegram no seu Windows
  3. Crie um Bot
  4. Configure o Bot
  5. Crie um canal e adicione esse Bot como administrador a um canal
  6. Mande uma mensagem para esse canal para criar um ID
  7. Acesse uma URL do Telegram com a chave do BOT para pegar o ID do canal
  8. Crie uma função no Powershell
  9. Declare a variável com o nome do Bot e a chave
  10. Mande uma mensagem

Vamos começar,,,

Conta do Telegram

  • PQ está lendo essa parte? vai para a próxima,,, se não sabe fazer isso nem adianta continuar…

Instale o aplicativo do Telegram no seu Windows

  • Esse ponto é interessante, acesse o cliente web do Telegram e baixe a versão para seu SO, as configurações do Bot serão feitas através do cliente e não pelo celular ou Web

Crie um Bot

  • Com sua contra criada e app instalado, acesse essa URL https://t.me/BotFather . Ele é o Bot que cria os Bot´s
  • Para criar digite /start
  • Depois digite /newbot
  • De um nome para seu Bot
  • Agora crie um username para seu bot, ele tem que terminar com bot
  • Você vai receber uma mensagem de resposta com um textão e a parte que interessa que é o TOKEN. ANOTA ISSO!
  • Aqui vale uma dica, o Bot vai aparecer em pesquisa para qualquer pessoa que use o Telegram, mas só quem tem o token vai poder realmente usar ele.

Configure o Bot

  • Adicione uma descrição para ele, uma hora você ou alguém vai fazer manutenção e vai ter que lembrar para que ele serve
  • Usando /setdescription adicione uma descrição

Crie um canal e adicione esse Bot como administrador a um canal

  • Agora usando o cliente do SO, Web ou o app do celular, crie um canal no Telegram e deixe como privado.
  • Adicione um novo membro para esse canal, nesse caso o seu Bot, vai aparecer um alerta de que o Bot precisa ser administrador do canal e bla bla bla

Mande uma mensagem para esse canal para criar um ID

  • Só o fato de criar um canal não efetiva cria o canal, até esse momento ele não tem um ID
  • Mande uma mensagem qualquer para o canal para ele criar a estrutura com o ID

Acesse uma URL do Telegram com a chave do BOT para pegar o ID do canal

  • Após mandar a mensagem acesse a URL com o token do seu Bot
  • a URL é alguma coisa assim: https://api.telegram.org/bot<aquele token do seu Bot>/getUpdates
  • deixa escrito o nome bot, remove o < e o > e cola o token
  • deve abrir uma página com alguma coisa parecida com:
{"ok":true,"result":[{"update_id":129494597,
"message":{"message_id":3,"from":{"id":XXXXXX,"is_bot":false,"first_name":"Ricardo","last_name":"Leka"},"chat":{"id":XXXXXX,"first_name":"Ricardo","last_name":"Leka","type":"private"},"date":1614187816,"text":"/start","entities":[{"offset":0,"length":6,"type":"bot_command"}]}},{"update_id":129494598,
"message":{"message_id":4,"from":{"id":XXXXXX,"is_bot":false,"first_name":"Ricardo","last_name":"Leka","language_code":"pt-br"},"chat":{"id":XXXXXX,"first_name":"Ricardo","last_name":"Leka","type":"private"},"date":1614187832,"text":"/getchatid","entities":[{"offset":0,"length":10,"type":"bot_command"}]}},{"update_id":129494599,
"channel_post":{"message_id":2,"sender_chat":{"id":-XXXX,"title":"MC1_Notify","type":"channel"} ,"chat":{"id":-XXXX,"title":"Nome_Do_Canal","type":"channel"} ,"date":1614187878,"text":"teste"}}]}

O que interessa para nós é um desses últimos “id” que estão com um símbolo de menos na frente “id”:-XXXX

Crie uma função no Powershell

  • Agora a parte mágica, vamos usar um powershell para criar a função que vai conectar na API do Telegram e mandar a mensagem…
<#
.Synopsis
    Sends Telegram text message via Bot API
.DESCRIPTION
    Uses Telegram Bot API to send text message to specified Telegram chat. Several options can be specified to adjust message parameters.
.EXAMPLE
    $bot = "#########:xxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxx"
    $chat = "-#########"
    Send-TelegramTextMessage -BotToken $bot -ChatID $chat -Message "Hello"
.EXAMPLE
    $bot = "#########:xxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxx"
    $chat = "-#########"

    Send-TelegramTextMessage `
        -BotToken $bot `
        -ChatID $chat `
        -Message "Hello *chat* _channel_, check out this link: [TechThoughts](http://techthoughts.info/)" `
        -ParseMode Markdown `
        -Preview $false `
        -Notification $false `
        -Verbose
.PARAMETER BotToken
    Use this token to access the HTTP API
.PARAMETER ChatID
    Unique identifier for the target chat
.PARAMETER Message
    Text of the message to be sent
.PARAMETER ParseMode
    Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message. Default is Markdown.
.PARAMETER Preview
    Disables link previews for links in this message. Default is $false
.PARAMETER Notification
    Sends the message silently. Users will receive a notification with no sound. Default is $false
.OUTPUTS
    System.Boolean
.NOTES
    Author: Jake Morrison - @jakemorrison - http://techthoughts.info/
    This works with PowerShell Versions 5.1, 6.0, 6.1
    For a description of the Bot API, see this page: https://core.telegram.org/bots/api
    How do I get my channel ID? Use the getidsbot https://telegram.me/getidsbot
    How do I set up a bot and get a token? Use the BotFather https://t.me/BotFather
.COMPONENT
   PoshGram - https://github.com/techthoughts2/PoshGram
.FUNCTIONALITY
    https://core.telegram.org/bots/api#sendmessage
    Parameters 					Type 				Required 	Description
    chat_id 				    Integer or String 	Yes 		Unique identifier for the target chat or username of the target channel (in the format @channelusername)
    text 						String 				Yes 		Text of the message to be sent
    parse_mode 					String 				Optional 	Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message.
    disable_web_page_preview 	Boolean 			Optional 	Disables link previews for links in this message
    disable_notification 		Boolean 			Optional 	Sends the message silently. Users will receive a notification with no sound.
    reply_to_message_id 	    Integer 			Optional 	If the message is a reply, ID of the original message
#>
function Send-TelegramTextMessage {
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $true,
            HelpMessage = '#########:xxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxx')]
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        [string]$BotToken, #you could set a token right here if you wanted
        [Parameter(Mandatory = $true,
            HelpMessage = '-#########')]
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        [string]$ChatID, #you could set a Chat ID right here if you wanted
        [Parameter(Mandatory = $true,
            HelpMessage = 'Text of the message to be sent')]
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        [string]$Message,
        [Parameter(Mandatory = $false,
            HelpMessage = 'HTML vs Markdown for message formatting')]
        [ValidateSet("Markdown", "HTML")]
        [string]$ParseMode = "Markdown", #set to Markdown by default
        [Parameter(Mandatory = $false,
            HelpMessage = 'Disables link previews')]
        [bool]$Preview = $false, #set to false by default
        [Parameter(Mandatory = $false,
            HelpMessage = 'Sends the message silently')]
        [bool]$Notification = $false #set to false by default
    )
    #------------------------------------------------------------------------
    $results = $true #assume the best
    #------------------------------------------------------------------------
    $payload = @{
        "chat_id"                   = $ChatID;
        "text"                      = $Message
        "parse_mode"                = $ParseMode;
        "disable_web_page_preview"  = $Preview;
        "disable_notification"      = $Notification
    }#payload
    #------------------------------------------------------------------------
    try {
        Write-Verbose -Message "Sending message..."
        $eval = Invoke-RestMethod `
            -Uri ("https://api.telegram.org/bot{0}/sendMessage" -f $BotToken) `
            -Method Post `
            -ContentType "application/json" `
            -Body (ConvertTo-Json -Compress -InputObject $payload) `
            -ErrorAction Stop
        if (!($eval.ok -eq "True")) {
            Write-Warning -Message "Message did not send successfully"
            $results = $false
        }#if_StatusDescription
    }#try_messageSend
    catch {
        Write-Warning "An error was encountered sending the Telegram message:"
        Write-Error $_
        $results = $false
    }#catch_messageSend
    return $results
    #------------------------------------------------------------------------
}#function_Send-TelegramTextMessage

Declare a variável com o nome do Bot e a chave

  • Sendo simplista você vai precisar agora criar duas variáveis a com o nome do Token e o ID do chat
$bot = "token"
$chat = "-ID_do_chat"

Mande uma mensagem

  • Agora é só chamar a função e mandar a mensagem…
  • AAHH a máquina precisa ter acesso a internet.
  • Qualquer mensagem de erro a função vai tentar trazer a mensagem para te ajudar no troubleshooting.
$bot = "token"
$chat = "-ID_do_chat"
Send-TelegramTextMessage -BotToken $bot -ChatID $chat -Message "CASA CAIU"

Com isso agora temos um powershell que acessa uma API do Telegram e manda mensagem, junta isso em um JOB que é acionado por um alerta e vc tem o SQL ou outro sistema de monitoração mandando mensagens para seu celular.

Liberar toda a memória do servidor

Todos sabemos que o SQL é um consumidor de memória frenético, quanto mais memória disponível mais memória ele vai reservar para ele.

O que é um desenho “by default”, ele sempre fará isso afinal de contas ele precisa alocar as páginas de dados do seu banco em algum lugar.

Para resolver todos os seus problemas, existe uma forma de liberar toda a memória disponível de uma só vez do seu servidor e não é parando o serviço do SQL.

Para isso, você vai precisar o Visual Studio instalado, vamos criar um novo projeto dele…

Importante! Abra o Visual Studio como administrador !

Novo projeto de linha de comando

Escreva o nome que quiser para o app

Copie e cole o código abaixo no projeto:

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

public class CriticalProcess
{
    [DllImport("ntdll.dll", SetLastError = true)]
    private static extern int NtSetInformationProcess(IntPtr hProcess, int processInformationClass, ref int processInformation, int processInformationLength);

    static void Main(string[] args)
    {
        int isCritical = 1;  // queremos que ele seja um processo crítico
        int BreakOnTermination = 0x1D;  // valor para BreakOnTermination (flag)

        Process.EnterDebugMode();  //acquire Debug Privileges

        // configurando o BreakOnTermination = 1 para o processo ativo
        NtSetInformationProcess(Process.GetCurrentProcess().Handle, BreakOnTermination, ref isCritical, sizeof(int));
    }
}

Se tudo ocorrer como esperado, dependendo da quantidade de memória do seu servidor isso pode demorar de alguns segundos a algumas horas.

Por mais que tenhamos criado uma aplicação de linha de comando a primeira parte do processo é bem gráfica e todos já tiveram o grande prazer de conhecer:

Ele vai gerar um DUMP de toda a memória para o arquivo de paginação e depois que a maquina reiniciar ele vai copiar esse arquivo de paginação para um arquivo chamado memory.dump

É só isso,,, execução e queda,,,

Agora falando sério: NUNCA !!!! JAMAIS !!!!! Simplesmente pegue o código de qualquer coisa que você encontra na internet e saia executando sem antes entender o que ele faz.

Esse exemplo é bem ridículo, mas imagina um script que você leu o por alto achando que vai resolver todos os seus problemas de backup, ou de fragmentação de índice e descobre que no meio tem um sp_msforeach_table com um sp_msforeach_db que trunca as tabelas, ou pior, alguém cria uma chave de criptografia e habilita TDE nas suas bases e depois força a remoção da chave,,,, a culpa é tão e somente sua! Você é o DBA é sua responsabilidade preservar os dados.

Tenha discernimento com o que você copia da internet e de onde copia essas informações.

SEMPRE LEIA e NUNCA EXECUTE DIRETAMENTE EM PRODUÇÃO !!!

Bah! – SQLSaturday #744 – Caxias do Sul Tchê !

Bah! Tchê! Vamos ver se eles realmente fazem um churrasco melhor que o paulista ou é só intriga da oposição.

No dia 23/06/2018 Haverá um novo evento do SQL Saturday, desta vez em Caxias do Sul/RS.

É com um prazer inenarrável em estar presente para mais um evento da comunidade e ainda mais como palestrante.

Só tem uma coisa mais legal que a minha palestra,,, o café,,, esqueça as outras, mantenha o foco,,,

Vamos conversar sobre formas “erradas” de como fazer as coisas no SQL Server.