{"id":83649,"date":"2020-06-02T07:42:21","date_gmt":"2020-06-02T05:42:21","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/database-as-sode-experience"},"modified":"2020-06-02T07:42:21","modified_gmt":"2020-06-02T05:42:21","slug":"database-as-sode-experience","status":"publish","type":"post","link":"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/database-as-sode-experience","title":{"rendered":"Experi\u00eancia \u00abDatabase as Code\u00bb","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"Experi\u00eancia \u00abDatabase as Code\u00bb\" src=\"\/wp-content\/uploads\/2020\/06\/a1ac23deeded97559fbe787ed09184a5.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>SQL, o que pode ser mais simples? Qualquer um de n\u00f3s pode escrever uma consulta simples \u2014 digitamos <strong><em>select<\/em><\/strong>, listamos as colunas necess\u00e1rias, em seguida <strong><em>Harvard University<\/em><\/strong>, o nome da tabela, um pouco de condi\u00e7\u00f5es em <strong><em>onde<\/em><\/strong> e tudo \u2014 os dados \u00fateis est\u00e3o a nosso alcance, quase independentemente do tipo de SGBD que est\u00e1 funcionando por tr\u00e1s (ou pode ser que <noindex><a rel=\"nofollow\" href=\"https:\/\/osquery.io\/\">n\u00e3o seja um SGBD de fato<\/a><\/noindex>). Como resultado, \u00e9 poss\u00edvel tratar praticamente qualquer fonte de dados (relacional ou n\u00e3o) sob a perspectiva de um c\u00f3digo comum (com todas as implica\u00e7\u00f5es \u2014 controle de vers\u00e3o, revis\u00e3o de c\u00f3digo, an\u00e1lise est\u00e1tica, testes automatizados e tudo mais). Isso se aplica n\u00e3o apenas aos dados, esquemas e migra\u00e7\u00f5es, mas a toda a opera\u00e7\u00e3o do reposit\u00f3rio. Neste artigo, vamos discutir as tarefas do dia a dia e os problemas que surgem ao trabalhar com diferentes bancos de dados sob a \u00f3tica do 'database as code'.<\/p>\n<p><\/p>\n<p>E vamos come\u00e7ar diretamente com <noindex><a rel=\"nofollow\" href=\"https:\/\/www.yegor256.com\/2014\/12\/01\/orm-offensive-anti-pattern.html\">ORM<\/a><\/noindex>. Os primeiros embates do tipo 'SQL vs ORM' foram notados ainda em <noindex><a rel=\"nofollow\" href=\"https:\/\/www.sql.ru\/forum\/904343\/orm-vs-sql\">R\u00fassia pr\u00e9-petrina<\/a><\/noindex>.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h5 id=\"obektno-relyacionnyy-maping\">Mapeamento objeto-relacional<\/h5>\n<p><\/p>\n<p>Os defensores do ORM tradicionalmente valorizam a velocidade e a simplicidade de desenvolvimento, a independ\u00eancia do SGBD e a limpeza do c\u00f3digo. Para muitos de n\u00f3s, o c\u00f3digo que interage com o banco de dados (e muitas vezes o pr\u00f3prio banco de dados)<\/p>\n<p><\/p>\n<p>                        <b class=\"spoiler_title\">geralmente se apresenta mais ou menos assim\u2026<\/b><\/p>\n<pre><code class=\"java\">@Entity\n@Table(name = \"stock\", catalog = \"maindb\", uniqueConstraints = {\n        @UniqueConstraint(columnNames = \"STOCK_NAME\"),\n        @UniqueConstraint(columnNames = \"STOCK_CODE\") })\npublic class Stock implements java.io.Serializable {\n\n    @Id\n    @GeneratedValue(strategy = IDENTITY)\n    @Column(name = \"STOCK_ID\", unique = true, nullable = false)\n    public Integer getStockId() {\n        return this.stockId;\n    }\n  ...<\/code><\/pre>\n<p><\/p>\n<p>O modelo est\u00e1 repleto de anota\u00e7\u00f5es inteligentes, enquanto nos bastidores o valoroso ORM gera e executa toneladas de algum c\u00f3digo SQL. A prop\u00f3sito, os desenvolvedores se esfor\u00e7am para se proteger de seu banco de dados com quil\u00f4metros de abstra\u00e7\u00f5es, o que sugere um certo <noindex><a rel=\"nofollow\" href=\"https:\/\/www.sql.ru\/forum\/1303231\/prichiny-nenavisti-k-yazyku-sql\">\"SQL do \u00f3dio\"<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Do outro lado da barricada, os defensores do 'handmade' SQL apontam a possibilidade de extrair todo o valor do seu SGBD sem intermedi\u00e1rios e abstra\u00e7\u00f5es adicionais. Como resultado, surgem projetos 'centrados em dados', onde a gest\u00e3o do banco \u00e9 feita por profissionais especificamente treinados (os mesmos que chamamos de 'banc\u00e1rios', 'especialistas em banco', etc.), enquanto os desenvolvedores apenas precisam 'chamar' as views e procedures prontas, sem se aprofundar nos detalhes.<\/p>\n<p><\/p>\n<p>E se peg\u00e1ssemos o melhor dos dois mundos? Como \u00e9 feito na ferramenta maravilhosa com um nome que afirma a vida <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/krisajenkins\/yesql\">Yesql<\/a><\/noindex>. Vou citar algumas linhas da ideia geral na minha tradu\u00e7\u00e3o livre, e mais detalhes podem ser conhecidos <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/krisajenkins\/yesql#rationale\">aqui<\/a><\/noindex>.<\/p>\n<p><\/p>\n<blockquote><p>Clojure \u00e9 uma linguagem incr\u00edvel para criar DSLs, mas SQL por si s\u00f3 j\u00e1 \u00e9 um excelente DSL, e n\u00e3o precisamos de mais um. As express\u00f5es S s\u00e3o maravilhosas, mas aqui n\u00e3o adicionam nada novo. No final, temos par\u00eanteses por causa dos par\u00eanteses. N\u00e3o concorda? Ent\u00e3o aguarde o momento em que a abstra\u00e7\u00e3o sobre o banco come\u00e7ar a vazar, e voc\u00ea come\u00e7ar\u00e1 a lutar com a funcionalidade. <em>(raw-sql)<\/em><\/p>\n<p>E o que fazer? Vamos deixar o SQL como SQL comum \u2014 um arquivo por consulta:<\/p><\/blockquote>\n<p><\/p>\n<pre><code class=\"sql\">-- nome: users-by-country\nselect *\n  from users\n where country_code = :country_code<\/code><\/pre>\n<p><\/p>\n<blockquote><p>\u2026 e ent\u00e3o leia este arquivo, transformando-o em uma fun\u00e7\u00e3o Clojure comum:<\/p><\/blockquote>\n<p><\/p>\n<pre><code class=\"lisp\">(defqueries \"some\/where\/users_by_country.sql\"\n   {:connection db-spec})\n\n;;; Uma fun\u00e7\u00e3o com o nome `users-by-country` foi criada.\n;;; Vamos us\u00e1-la:\n(users-by-country {:country_code \"GB\"})\n;=&gt; ({:name \"Kris\" :country_code \"GB\" ...} ...)<\/code><\/pre>\n<p><\/p>\n<blockquote><p>Seguindo o princ\u00edpio 'SQL separado, Clojure separado', voc\u00ea obt\u00e9m:<\/p>\n<ul>\n<li>Nensurpresas sint\u00e1ticas. Seu banco de dados (como qualquer outro) n\u00e3o est\u00e1 100% em conformidade com o padr\u00e3o SQL \u2014 mas para Yesql isso n\u00e3o importa. Voc\u00ea nunca gastar\u00e1 tempo procurando por fun\u00e7\u00f5es com a sintaxe equivalente ao SQL. Voc\u00ea nunca ter\u00e1 que voltar \u00e0 fun\u00e7\u00e3o <em>(raw-sql \"some ('funky' :: SYNTAX)\")<\/em>.<\/li>\n<li>Melhor suporte ao editor. Seu editor j\u00e1 tem um \u00f3timo suporte para SQL. Mantendo o SQL como SQL, voc\u00ea pode simplesmente utiliz\u00e1-lo.<\/li>\n<li>Compatibilidade em equipe. Seus DBAs podem ler e escrever o SQL que voc\u00ea usa em seu projeto Clojure.<\/li>\n<li>Configura\u00e7\u00e3o de desempenho mais simples. Precisa criar um plano para uma consulta problem\u00e1tica? Isso n\u00e3o \u00e9 um problema quando sua consulta \u00e9 SQL comum.<\/li>\n<li>Reutiliza\u00e7\u00e3o de consultas. Desloque esses mesmos arquivos SQL para outros projetos, porque \u00e9 apenas um velho e bom SQL \u2014 basta compartilh\u00e1-lo.<\/li>\n<\/ul>\n<p>\n<\/p><\/blockquote>\n<p>Na minha opini\u00e3o, a ideia \u00e9 muito boa e ao mesmo tempo muito simples, o que fez o projeto ganhar muito <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/krisajenkins\/yesql#other-languages\">seguidores.<\/a><\/noindex> em diversos idiomas. E vamos tentar aplicar uma filosofia semelhante de separa\u00e7\u00e3o do c\u00f3digo SQL de todo o resto, muito al\u00e9m do ORM.<\/p>\n<p><\/p>\n<h5 id=\"ide--db-menedzhery\">IDE &amp; DB-gestores<\/h5>\n<p><\/p>\n<p>Vamos come\u00e7ar com uma tarefa cotidiana simples. Muitas vezes, precisamos buscar certos objetos em um banco de dados, como encontrar uma tabela em um esquema e examinar sua estrutura (quais colunas, chaves, \u00edndices, restri\u00e7\u00f5es etc. s\u00e3o utilizadas). E de qualquer IDE gr\u00e1fica ou de um gerenciador de banco de dados mais b\u00e1sico, esperamos, em primeiro lugar, essas capacidades. Para que seja r\u00e1pido e n\u00e3o precisemos esperar meia hora at\u00e9 que a janela com a informa\u00e7\u00e3o necess\u00e1ria apare\u00e7a (especialmente em uma conex\u00e3o lenta com um banco de dados remoto), e ao mesmo tempo, que a informa\u00e7\u00e3o obtida seja fresca e atual, e n\u00e3o um velho cache. Quanto mais complexos e maiores forem os bancos de dados e maior o n\u00famero deles, mais dif\u00edcil se torna essa tarefa.<\/p>\n<p><\/p>\n<p>Mas normalmente eu jogo o mouse para longe e simplesmente escrevo c\u00f3digo. Suponha que seja necess\u00e1rio descobrir quais tabelas (e com quais propriedades) est\u00e3o contidas no esquema \"HR\". Na maioria dos SGBDs, o resultado desejado pode ser alcan\u00e7ado com a seguinte consulta simples no information_schema:<\/p>\n<p><\/p>\n<pre><code class=\"sql\">select table_name\n     , ...\n  from information_schema.tables\n where schema = 'HR'<\/code><\/pre>\n<p><\/p>\n<p>De banco para banco, o conte\u00fado dessas tabelas de refer\u00eancia varia de acordo com as capacidades de cada SGBD. E, por exemplo, para MySQL, a partir desse mesmo dicion\u00e1rio, \u00e9 poss\u00edvel obter par\u00e2metros espec\u00edficos desse SGBD para a tabela:<\/p>\n<p><\/p>\n<pre><code class=\"sql\">select table_name\n     , storage_engine -- O \"motor\" utilizado (\"MyISAM\", \"InnoDB\" etc)\n     , row_format     -- Formato da linha (\"Fixed\", \"Dynamic\" etc)\n     , ...\n  from information_schema.tables\n where schema = 'HR'<\/code><\/pre>\n<p><\/p>\n<p>O Oracle n\u00e3o possui information_schema, mas ele possui <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Oracle_metadata\">metadados do Oracle<\/a><\/noindex>, e n\u00e3o surgem grandes problemas:<\/p>\n<p><\/p>\n<pre><code class=\"sql\">select table_name\n     , pct_free       -- M\u00ednimo de espa\u00e7o livre no bloco de dados (%)\n     , pct_used       -- M\u00ednimo de espa\u00e7o utilizado no bloco de dados (%)\n     , last_analyzed  -- Data da \u00faltima coleta de estat\u00edsticas\n     , ...\n  from all_tables\n where owner = 'HR'<\/code><\/pre>\n<p><\/p>\n<p>ClickHouse n\u00e3o \u00e9 uma exce\u00e7\u00e3o:<\/p>\n<p><\/p>\n<pre><code class=\"sql\">select name\n     , engine -- O \"motor\" utilizado (\"MergeTree\", \"Dictionary\" etc)\n     , ...\n  from system.tables\n where database = 'HR'<\/code><\/pre>\n<p><\/p>\n<p>Algo semelhante pode ser feito no Cassandra (onde existem column families em vez de tabelas e keyspaces em vez de esquemas):<\/p>\n<p><\/p>\n<pre><code class=\"sql\">select columnfamily_name\n     , compaction_strategy_class  -- Estrat\u00e9gia de compacta\u00e7\u00e3o\n     , gc_grace_seconds           -- Tempo de vida do lixo\n     , ...\n  from system.schema_columnfamilies\n where keyspace_name = 'HR'<\/code><\/pre>\n<p><\/p>\n<p>Para a maioria dos outros bancos de dados, tamb\u00e9m \u00e9 poss\u00edvel pensar em consultas semelhantes (at\u00e9 no Mongo h\u00e1 <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.mongodb.com\/manual\/reference\/system-collections\/#%3Cdatabase%3E.system.namespaces\">uma cole\u00e7\u00e3o sist\u00eamica especial<\/a><\/noindex>, que cont\u00e9m informa\u00e7\u00f5es sobre todas as cole\u00e7\u00f5es no sistema).<\/p>\n<p><\/p>\n<p>Claro, dessa forma, \u00e9 poss\u00edvel obter informa\u00e7\u00f5es n\u00e3o apenas sobre tabelas, mas sobre qualquer objeto. Periodicamente, pessoas generosas compartilham esse tipo de c\u00f3digo para diferentes bancos de dados, como, por exemplo, na s\u00e9rie de artigos no Habr \"Fun\u00e7\u00f5es para documentar bancos de dados PostgreSQL\" (<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/post\/415575\">aib<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/post\/415897\">ben<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/post\/418597\">gim<\/a><\/noindex>). Claro, manter toda essa montanha de consultas na cabe\u00e7a e digit\u00e1-las constantemente n\u00e3o \u00e9 uma \"grande\" divers\u00e3o, por isso, na minha IDE\/editor favorita, tenho um conjunto pr\u00e9-definido de snippets para consultas que uso frequentemente, e s\u00f3 preciso digitar os nomes dos objetos no template.<\/p>\n<p><\/p>\n<p>No final, essa forma de navega\u00e7\u00e3o e busca de objetos \u00e9 muito mais flex\u00edvel, economiza muito tempo, permite obter exatamente a informa\u00e7\u00e3o e na forma que \u00e9 necess\u00e1ria no momento (como descrito no post <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/company\/JetBrains\/blog\/342094\">\"Exportar dados de um banco de dados em qualquer formato: o que as IDEs na plataforma IntelliJ podem fazer\"<\/a><\/noindex>).<\/p>\n<p><\/p>\n<h5 id=\"operacii-s-obektami\">Opera\u00e7\u00f5es com objetos<\/h5>\n<p><\/p>\n<p>Ap\u00f3s encontrarmos e estudarmos os objetos necess\u00e1rios, \u00e9 hora de fazer algo \u00fatil com eles. Evidentemente, sem tirar os dedos do teclado.<\/p>\n<p><\/p>\n<p>N\u00e3o \u00e9 segredo que a simples exclus\u00e3o de uma tabela se parece quase igual em todos os bancos de dados:<\/p>\n<p><\/p>\n<pre><code class=\"sql\">drop table hr.persons<\/code><\/pre>\n<p><\/p>\n<p>A cria\u00e7\u00e3o de tabelas \u00e9 um assunto interessante. Praticamente qualquer SGBD (incluindo muitos NoSQL) tem alguma forma de \"create table\", e a maior parte do seu conte\u00fado ser\u00e1 similar (nome, lista de colunas, tipos de dados), mas outros detalhes podem variar bastante e dependem da estrutura interna e das capacidades do SGBD espec\u00edfico. Meu exemplo favorito \u2014 na documenta\u00e7\u00e3o do Oracle, apenas as \"BNFs\" b\u00e1sicas para a sintaxe de \"create table\". <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.oracle.com\/en\/database\/oracle\/oracle-database\/19\/sqlrf\/sql-language-reference.pdf\">ocupam 31 p\u00e1ginas<\/a><\/noindex>. Outros SGBDs possuem capacidades mais modestas, mas cada um tamb\u00e9m possui v\u00e1rias caracter\u00edsticas interessantes e \u00fanicas para a cria\u00e7\u00e3o de tabelas (<noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/static\/sql-createtable.html\">postgres<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.0\/en\/create-table.html\">mysql<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/www.cockroachlabs.com\/docs\/stable\/create-table.html#expanded\">cockroach<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.datastax.com\/en\/cql\/3.3\/cql\/cql_reference\/cqlCreateTable.html\">cassandra<\/a><\/noindex>). \u00c9 duvidoso que algum \"assistente\" gr\u00e1fico de uma IDE (especialmente uma universal) consiga abranger completamente todas essas funcionalidades, e se conseguir, ser\u00e1 uma vis\u00e3o nada f\u00e1cil de se lidar. Ao mesmo tempo, um comando escrito corretamente e no momento certo... <strong><em>create table<\/em><\/strong> permitir\u00e1 explorar todas elas facilmente, tornando o armazenamento e o acesso aos seus dados confi\u00e1veis, otimizados e o mais confort\u00e1veis poss\u00edvel.<\/p>\n<p><\/p>\n<p>Al\u00e9m disso, muitos SGBDs possuem tipos de objetos espec\u00edficos que n\u00e3o est\u00e3o presentes em outros SGBDs. E podemos realizar opera\u00e7\u00f5es n\u00e3o apenas sobre os objetos do banco de dados, mas tamb\u00e9m sobre o pr\u00f3prio SGBD, como \"finalizar\" um processo, liberar uma \u00e1rea de mem\u00f3ria, ativar a rastreabilidade, mudar para o modo \"somente leitura\" e muito mais.<\/p>\n<p><\/p>\n<h5 id=\"a-teper-nemnogo-porisuem\">Agora vamos desenhar um pouco<\/h5>\n<p><\/p>\n<p>Uma das tarefas mais comuns \u00e9 construir um diagrama com objetos do banco de dados, ver os objetos e suas rela\u00e7\u00f5es em uma imagem bonita. Isso \u00e9 uma funcionalidade comum em praticamente qualquer IDE gr\u00e1fica, utilit\u00e1rios espec\u00edficos de linha de comando, ferramentas gr\u00e1ficas especializadas e modeladores. Eles v\u00e3o desenhar algo \"como conseguem\", e a influ\u00eancia nesse processo pode ser feita apenas com alguns par\u00e2metros em um arquivo de configura\u00e7\u00e3o ou op\u00e7\u00f5es na interface.<\/p>\n<p><\/p>\n<p>Mas esse problema pode ser resolvido de forma muito mais simples, flex\u00edvel e elegante, e claro, com a ajuda de c\u00f3digo. Para construir diagramas de qualquer complexidade, temos v\u00e1rias linguagens de marca\u00e7\u00e3o especializadas (DOT, GraphML etc.), e a elas est\u00e3o associados uma gama de aplicativos (GraphViz, PlantUML, Mermaid), que sabem ler tais instru\u00e7\u00f5es e visualizar em diversos formatos. J\u00e1 sabemos como obter informa\u00e7\u00f5es sobre os objetos e as rela\u00e7\u00f5es entre eles.<\/p>\n<p><\/p>\n<p>Vamos dar um pequeno exemplo de como isso poderia parecer, usando PlantUML e <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/postgrespro\/blog\/316428\/\">um banco de dados de demonstra\u00e7\u00e3o para PostgreSQL<\/a><\/noindex> (\u00e0 esquerda, uma consulta SQL que gerar\u00e1 a instru\u00e7\u00e3o necess\u00e1ria para PlantUML, e \u00e0 direita, o resultado):<\/p>\n<p>\n<img decoding=\"async\" alt=\"Experi\u00eancia \u00abDatabase as Code\u00bb\" src=\"\/wp-content\/uploads\/2020\/06\/c5cb2a138df1527abe47cbf8d695cb36.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<pre><code class=\"sql\">select '@startuml'||chr(10)||'hide methods'||chr(10)||'hide stereotypes' union all\nselect distinct ccu.table_name || ' --|&gt; ' ||\n       tc.table_name as val\n  from table_constraints as tc\n  join key_column_usage as kcu\n    on tc.constraint_name = kcu.constraint_name\n  join constraint_column_usage as ccu\n    on ccu.constraint_name = tc.constraint_name\n where tc.constraint_type = 'FOREIGN KEY'\n   and tc.table_name ~ '.*' union all\nselect '@enduml'<\/code><\/pre>\n<p><\/p>\n<p>E se prestarmos um pouco mais de aten\u00e7\u00e3o, com base no <noindex><a rel=\"nofollow\" href=\"https:\/\/gist.github.com\/QuantumGhost\/0955a45383a0b6c0bc24f9654b3cb561\">modelo ER para PlantUML<\/a><\/noindex> podemos obter algo que se assemelha bastante a um verdadeiro diagrama ER:<\/p>\n<p><\/p>\n<p>                        <b class=\"spoiler_title\">A consulta SQL \u00e9 um pouquinho mais complexa<\/b><\/p>\n<pre><code class=\"sql\">-- Cabe&ccedil;alho\nselect &#039;@startuml\n        !define Table(name,desc) class name as &quot;desc&quot; &lt;&lt; (T,#FFAAAA) &gt;&amp;gt;\n        !define primary_key(x) &lt;b&gt;x&lt;\/b&gt;\n        !define unique(x) &lt;color:green&gt;x&lt;\/color&gt;\n        !define not_null(x) &lt;u&gt;x&lt;\/u&gt;\n        hide methods\n        hide stereotypes&#039;\n union all\n-- Tabelas\nselect format(&#039;Table(%s, &quot;%s n informa&ccedil;&otilde;es sobre %s&quot;) {&#039;||chr(10), table_name, table_name, table_name) ||\n       (select string_agg(column_name || &#039; &#039; || upper(udt_name), chr(10))\n          from information_schema.columns\n         where table_schema = &#039;public&#039;\n           and table_name = t.table_name) || chr(10) || &#039;}&#039;\n  from information_schema.tables t\n where table_schema = &#039;public&#039;\n union all\n-- Rela&ccedil;&otilde;es entre tabelas\nselect distinct ccu.table_name || &#039; &quot;1&quot; --&amp;gt; &quot;0..N&quot; &#039; || tc.table_name || format(&#039; : &quot;Uma %s pode ter v&aacute;rias %s&quot;&#039;, ccu.table_name, tc.table_name)\n  from information_schema.table_constraints as tc\n  join information_schema.key_column_usage as kcu on tc.constraint_name = kcu.constraint_name\n  join information_schema.constraint_column_usage as ccu on ccu.constraint_name = tc.constraint_name\n where tc.constraint_type = &#039;FOREIGN KEY&#039;\n   and ccu.constraint_schema = &#039;public&#039;\n   and tc.table_name ~ &#039;.*&#039;\n union all\n-- Rodap&eacute;\nselect &#039;@enduml&#039;<\/code><\/pre>\n<p>\n<img decoding=\"async\" alt=\"Experi\u00eancia \u00abDatabase as Code\u00bb\" src=\"\/wp-content\/uploads\/2020\/06\/bd45420a4fd1bdfcf20cbe74a0638c7b.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p>Se olharmos de perto, muitos ferramentas de visualiza\u00e7\u00e3o utilizam consultas semelhantes nos bastidores. Contudo, essas consultas geralmente est\u00e3o profundamente <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/pgmodeler\/pgmodeler\/blob\/9c615c0b0871df3cd649983ce61e802b0ff0137b\/schemas\/catalog\/table.sch\">\"embutidas\" no c\u00f3digo do pr\u00f3prio aplicativo e dif\u00edceis de entender.<\/a><\/noindex>, sem mencionar qualquer modifica\u00e7\u00e3o que possam sofrer.<\/p>\n<p><\/p>\n<h5 id=\"metriki-i-monitoring\">M\u00e9tricas e monitoramento<\/h5>\n<p><\/p>\n<p>Vamos abordar um tema que tradicionalmente \u00e9 complexo \u2014 monitoramento de performance de banco de dados. Vou lembrar uma pequena hist\u00f3ria verdadeira que me foi contada \"por um amigo meu\". Em um projeto, havia um DBA poderoso, e poucos desenvolvedores o conheciam pessoalmente ou tinham visto seu rosto (apesar de que, segundo rumores, ele trabalhava em um pr\u00e9dio pr\u00f3ximo). Na hora \"X\", quando o sistema de produ\u00e7\u00e3o de um grande varejista come\u00e7ava a \"apresentar problemas\", ele silenciosamente enviava capturas de tela dos gr\u00e1ficos do Oracle Enterprise Manager, onde ressaltava cuidadosamente os pontos cr\u00edticos com um marcador vermelho para uma \"melhor compreens\u00e3o\" (isso, para dizer o m\u00ednimo, ajudava pouco). E assim, a partir dessa \"foto\", era preciso encontrar solu\u00e7\u00f5es. Ningu\u00e9m tinha acesso ao precioso (em ambos os sentidos da palavra) Enterprise Manager, pois o sistema era complexo e caro, e havia o temor de que os desenvolvedores \"fizessem algo errado e tudo quebrasse\". Portanto, os desenvolvedores identificavam o local e a causa das lentid\u00f5es por meio de um m\u00e9todo \"emp\u00edrico\" e lan\u00e7avam um patch. Se uma carta amea\u00e7adora do DBA n\u00e3o chegasse novamente em breve, todos respiravam aliviados e voltavam \u00e0s suas tarefas atuais (at\u00e9 a nova Carta).<\/p>\n<p><\/p>\n<p>Mas o processo de monitoramento pode ser muito mais divertido e amig\u00e1vel, e o mais importante \u2014 acess\u00edvel e transparente para todos. Pelo menos a parte b\u00e1sica disso, como um complemento aos principais sistemas de monitoramento (que s\u00e3o indiscutivelmente \u00fateis e, em muitos casos, indispens\u00e1veis). Qualquer SGBD est\u00e1 prontamente disposto a compartilhar informa\u00e7\u00f5es sobre seu estado e performance atuais, sem custo algum. Na mesma \"sanguin\u00e1ria\" Oracle DB, praticamente qualquer informa\u00e7\u00e3o sobre performance pode ser obtida a partir das vis\u00f5es do sistema, desde processos e sess\u00f5es at\u00e9 o estado do cache de buffer (por exemplo, <noindex><a rel=\"nofollow\" href=\"https:\/\/oracle-base.com\/dba\/scripts\">Scripts de DBA<\/a><\/noindex>, se\u00e7\u00e3o \"Monitoring\"). No PostgreSQL tamb\u00e9m h\u00e1 uma grande variedade de vis\u00f5es do sistema para <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/static\/monitoring-stats.html\">monitoramento do funcionamento do banco de dados<\/a><\/noindex>, em particular, algumas que s\u00e3o indispens\u00e1veis na vida di\u00e1ria de qualquer DBA, como <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/static\/monitoring-stats.html#PG-STAT-ACTIVITY-VIEW\">pg_stat_activity<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/static\/monitoring-stats.html#PG-STAT-DATABASE-VIEW\">pg_stat_database<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/static\/monitoring-stats.html#PG-STAT-BGWRITER-VIEW\">pg_stat_bgwriter<\/a><\/noindex>. No MySQL, h\u00e1 at\u00e9 um esquema separado para isso, <noindex><a rel=\"nofollow\" href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.0\/en\/performance-schema-table-descriptions.html\">performance_schema<\/a><\/noindex>. E no Mongo, um <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.mongodb.com\/manual\/tutorial\/manage-the-database-profiler\/\">profiler<\/a><\/noindex> agrega dados sobre o desempenho em uma cole\u00e7\u00e3o sist\u00eamica <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.mongodb.com\/manual\/tutorial\/manage-the-database-profiler\/#view-profiler-data\">system.profile<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Assim, armados com algum coletor de m\u00e9tricas (Telegraf, Metricbeat, Collectd), que consegue executar consultas SQL personalizadas, um armazenamento dessas m\u00e9tricas (InfluxDB, Elasticsearch, Timescaledb) e um visualizador (Grafana, Kibana), \u00e9 poss\u00edvel obter um sistema de monitoramento bastante leve e flex\u00edvel, que ser\u00e1 estreitamente integrado com outras m\u00e9tricas do sistema (obtidas, por exemplo, do servidor de aplicativos, do sistema operacional, etc.). Como \u00e9 feito no pgwatch2, onde \u00e9 utilizada a combina\u00e7\u00e3o InfluxDB + Grafana e um conjunto de consultas \u00e0s vis\u00f5es do sistema, \u00e0s quais tamb\u00e9m podem ser <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/cybertec-postgresql\/pgwatch2#adding-metrics\">adicionadas consultas personalizadas<\/a><\/noindex>.<\/p>\n<p><\/p>\n<h2 id=\"itogo\">Total<\/h2>\n<p><\/p>\n<p>E essa \u00e9 apenas uma lista aproximada do que pode ser feito com nosso banco de dados atrav\u00e9s de SQL comum. Tenho certeza de que existem ainda muitas aplica\u00e7\u00f5es, escreva nos coment\u00e1rios. E sobre como (e, principalmente, por que) automatizar tudo isso e integr\u00e1-lo ao seu pipeline CI\/CD, falaremos na pr\u00f3xima vez.<\/p>\n<p>Fonte: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/426833\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>SQL, \u0447\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u0440\u043e\u0449\u0435? \u041a\u0430\u0436\u0434\u044b\u0439 \u0438\u0437 \u043d\u0430\u0441 \u043c\u043e\u0436\u0435\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u0438\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u2014 \u043d\u0430\u0431\u0438\u0440\u0430\u0435\u043c select, \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u044f\u0435\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043a\u043e\u043b\u043e\u043d\u043a\u0438, \u0437\u0430\u0442\u0435\u043c from, \u0438\u043c\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u044b, \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0443\u0441\u043b\u043e\u0432\u0438\u0439 \u0432 where \u0438 \u0432\u0441\u0435 \u2014 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0443 \u043d\u0430\u0441 \u0432 \u043a\u0430\u0440\u043c\u0430\u043d\u0435, \u043f\u0440\u0438\u0447\u0435\u043c (\u043f\u043e\u0447\u0442\u0438) \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e \u043e\u0442 \u0442\u043e\u0433\u043e \u043a\u0430\u043a\u0430\u044f \u0421\u0423\u0411\u0414 \u0432 \u044d\u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c (\u0430 \u043c\u043e\u0436\u0435\u0442 \u0438 \u043d\u0435 \u0421\u0423\u0411\u0414 \u0432\u043e\u0432\u0441\u0435). \u0412 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":83650,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-83649","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.3 - aioseo.com -->\n\t<meta name=\"description\" content=\"SQL, \u0447\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u0440\u043e\u0449\u0435?\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/database-as-sode-experience\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"pt_PT\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u00abDatabase as \u0421ode\u00bb Experience | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"SQL, \u0447\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u0440\u043e\u0449\u0435?\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/database-as-sode-experience\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-06-02T05:42:21+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-06-02T05:42:21+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47\u00abDatabase as Code\u00bb Experience | ProHoster","description":"SQL, o que pode ser mais simples?","canonical_url":"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/database-as-sode-experience","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"pt_PT","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u00abDatabase as \u0421ode\u00bb Experience | ProHoster","og:description":"SQL, \u0447\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u0440\u043e\u0449\u0435?","og:url":"https:\/\/prohoster.info\/pt\/blog\/administrirovanie\/database-as-sode-experience","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-06-02T05:42:21+00:00","article:modified_time":"2020-06-02T05:42:21+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"83649","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 15:13:28","updated":"2022-10-02 10:23:46","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts\/83649","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/comments?post=83649"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/posts\/83649\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/media\/83650"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/media?parent=83649"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/categories?post=83649"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pt\/wp-json\/wp\/v2\/tags?post=83649"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}