{"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\/de\/blog\/administrirovanie\/database-as-sode-experience","title":{"rendered":"\u201eDatabase as Code\u201c Experience","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"\u201eDatabase as Code\u201c Experience\" src=\"\/wp-content\/uploads\/2020\/06\/a1ac23deeded97559fbe787ed09184a5.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>SQL, was k\u00f6nnte einfacher sein? Jeder von uns kann eine einfache Abfrage schreiben \u2013 wir tippen <strong><em>select<\/em><\/strong>, listen die ben\u00f6tigten Spalten auf, dann <strong><em>from<\/em><\/strong>, der Tabellenname, ein bisschen Bedingungen in <strong><em>where<\/em><\/strong> und fertig \u2013 n\u00fctzliche Daten sind in unserer Tasche, und das (fast) unabh\u00e4ngig davon, welche DBMS gerade unter der Haube steckt (oder vielleicht <noindex><a rel=\"nofollow\" href=\"https:\/\/osquery.io\/\">gar keine DBMS)<\/a><\/noindex>). In der Folge kann die Arbeit mit praktisch jeder Datenquelle (relationale und nicht-relationale) als normaler Code betrachtet werden (mit all den Konsequenzen \u2014 Versionskontrolle, Code-Review, statische Analyse, automatisierte Tests und all diesen Dingen). Und das betrifft nicht nur die Daten selbst, Schemata und Migrationen, sondern die gesamte Lebensweise des Speichers. In diesem Artikel werden wir \u00fcber allt\u00e4gliche Aufgaben und Probleme bei der Arbeit mit verschiedenen Datenbanken im Kontext von &quot;database as code&quot; sprechen.<\/p>\n<p><\/p>\n<p>Und wir fangen gleich mit <noindex><a rel=\"nofollow\" href=\"https:\/\/www.yegor256.com\/2014\/12\/01\/orm-offensive-anti-pattern.html\">ORM<\/a><\/noindex>. Die ersten K\u00e4mpfe der Art &quot;SQL vs ORM&quot; wurden bereits beobachtet in <noindex><a rel=\"nofollow\" href=\"https:\/\/www.sql.ru\/forum\/904343\/orm-vs-sql\">vor-petrinischen Russland<\/a><\/noindex>.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h5 id=\"obektno-relyacionnyy-maping\">bemerkenswert.<\/h5>\n<p><\/p>\n<p>Objekt-relationale Abbildung<\/p>\n<p><\/p>\n<p>                        <b class=\"spoiler_title\">sieht normalerweise etwa so aus\u2026<\/b><\/p>\n<pre><code class=\"java\">@Entity\n@Table(name = &quot;stock&quot;, catalog = &quot;maindb&quot;, uniqueConstraints = {\n        @UniqueConstraint(columnNames = &quot;STOCK_NAME&quot;),\n        @UniqueConstraint(columnNames = &quot;STOCK_CODE&quot;) })\npublic class Stock implements java.io.Serializable {\n\n    @Id\n    @GeneratedValue(strategy = IDENTITY)\n    @Column(name = &quot;STOCK_ID&quot;, unique = true, nullable = false)\n    public Integer getStockId() {\n        return this.stockId;\n    }\n  ...<\/code><\/pre>\n<p><\/p>\n<p>@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  ... <noindex><a rel=\"nofollow\" href=\"https:\/\/www.sql.ru\/forum\/1303231\/prichiny-nenavisti-k-yazyku-sql\">&quot;SQL der Abneigung&quot;<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Auf der anderen Seite des Barrikades weisen die Bef\u00fcrworter reinen &quot;handmade&quot;-SQL darauf hin, dass man alle M\u00f6glichkeiten seiner DBMS ohne zus\u00e4tzliche Schichten und Abstraktionen aussch\u00f6pfen kann. Infolgedessen entstehen &quot;data-centric&quot; Projekte, in denen eigens geschulte Personen (sie sind auch die &quot;DB-Menschen&quot;, auch die &quot;Datenbankleute&quot;, auch die &quot;DB-Fachleute&quot; usw.) sich um die Daten k\u00fcmmern, w\u00e4hrend die Entwickler nur bereitstehen, um vorbereitete Views und gespeicherte Prozeduren abzurufen, ohne sich in die Details zu vertiefen.<\/p>\n<p><\/p>\n<p>Hinzu kommt, dass auf der anderen Seite der Barrikaden die Bef\u00fcrworter von reinem \"handmade\"-SQL die M\u00f6glichkeit hervorheben, aus ihrer DBMS alles herauszuholen, ohne zus\u00e4tzliche Schichten und Abstraktionen. Infolgedessen entstehen \"data-centric\"-Projekte, in denen speziell ausgebildete Personen sich um die Datenbank k\u00fcmmern (sie sind auch 'Basi\u015bci', 'Baziwiks', 'Basiendik', usw.), und den Entwicklern bleibt nur, die fertigen Views und gespeicherten Prozeduren zu 'ziehen', ohne ins Detail zu gehen. <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/krisajenkins\/yesql\">Und was, wenn wir das Beste aus beiden Welten nehmen? So wie es im bemerkenswerten Tool mit dem lebensbejahenden Namen<\/a><\/noindex>Yesql gemacht wird. Ich werde ein paar Zeilen aus dem Gesamtkonzept in meiner freien \u00dcbersetzung anf\u00fchren, und anhand dieser kann man sich n\u00e4her damit besch\u00e4ftigen. <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/krisajenkins\/yesql#rationale\">hier<\/a><\/noindex>.<\/p>\n<p><\/p>\n<blockquote><p>Clojure ist eine gro\u00dfartige Sprache zur Erstellung von DSLs, aber SQL ist bereits von Natur aus eine tolle DSL, und wir brauchen keine weitere. S-Ausdr\u00fccke sind wunderbar, aber hier bringen sie nichts Neues. Am Ende bekommen wir Klammern, nur um der Klammer willen. Nicht einverstanden? Dann warten Sie, bis die Abstraktion \u00fcber der Datenbank undicht wird und Sie anfangen, mit der Funktion zu k\u00e4mpfen. <em>(raw-sql)<\/em><\/p>\n<p>Und was tun? Lassen Sie uns SQL einfach SQL lassen \u2014 eine Datei f\u00fcr eine Abfrage:<\/p><\/blockquote>\n<p><\/p>\n<pre><code class=\"sql\">-- name: users-by-country\nselect *\n  from users\n where country_code = :country_code<\/code><\/pre>\n<p><\/p>\n<blockquote><p>\u2026 und lesen Sie dann diese Datei, um sie in eine gew\u00f6hnliche Clojure-Funktion zu verwandeln:<\/p><\/blockquote>\n<p><\/p>\n<pre><code class=\"lisp\">(defqueries &quot;some\\\/where\\\/users_by_country.sql&quot;\n   {:connection db-spec})\n\n;;; Eine Funktion mit dem Namen `users-by-country` wurde erstellt.\n;;; Lassen Sie uns sie verwenden:\n(users-by-country {:country_code &quot;GB&quot;})\n;=&gt; ({:name &quot;Kris&quot; :country_code &quot;GB&quot; ...} ...)<\/code><\/pre>\n<p><\/p>\n<blockquote><p>Indem Sie sich an das Prinzip &quot;SQL getrennt, Clojure getrennt&quot; halten, erhalten Sie:<\/p>\n<ul>\n<li>Keine syntaktischen \u00dcberraschungen. Ihre Datenbank (wie jede andere auch) entspricht nicht zu 100 % dem SQL-Standard \u2013 aber das ist f\u00fcr Yesql nicht wichtig. Sie werden niemals Zeit damit verschwenden, nach Funktionen mit einer SQL-\u00e4quivalenten Syntax zu suchen. Sie m\u00fcssen niemals zur Funktion zur\u00fcckkehren. <em>(raw-sql &quot;some (&#8216;funky&#8217; :: SYNTAX)&quot;))<\/em>.<\/li>\n<li>Bessere Editorunterst\u00fctzung. Ihr Editor hat bereits eine gro\u00dfartige Unterst\u00fctzung f\u00fcr SQL. Wenn Sie SQL als SQL belassen, k\u00f6nnen Sie es einfach nutzen.<\/li>\n<li>Teamkompatibilit\u00e4t. Ihre DBA k\u00f6nnen SQL lesen und schreiben, das Sie in Ihrem Clojure-Projekt verwenden.<\/li>\n<li>Einfachere Leistungsoptimierung. Muss ein Plan f\u00fcr eine problematische Abfrage erstellt werden? Das ist kein Problem, wenn Ihre Abfrage normales SQL ist.<\/li>\n<li>Wiederverwendung von Abfragen. Ziehen Sie diese gleichen SQL-Dateien in andere Projekte, denn es ist einfach das gute alte SQL \u2013 teilen Sie es einfach.<\/li>\n<\/ul>\n<p>\n<\/p><\/blockquote>\n<p>Ich finde die Idee wirklich cool und gleichzeitig sehr einfach, wodurch das Projekt viel erreicht hat. <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/krisajenkins\/yesql#other-languages\">Follower<\/a><\/noindex> in einer Vielzahl von Sprachen. Und wir werden versuchen, eine \u00e4hnliche Philosophie anzuwenden, um SQL-Code von allem anderen weit \u00fcber ORM hinaus zu trennen.<\/p>\n<p><\/p>\n<h5 id=\"ide--db-menedzhery\">IDE &amp; DB-Manager<\/h5>\n<p><\/p>\n<p>Lass uns mit einer einfachen allt\u00e4glichen Aufgabe beginnen. Oft m\u00fcssen wir nach bestimmten Objekten in der Datenbank suchen, zum Beispiel nach Tabellen im Schema und ihrer Struktur (welche Spalten, Schl\u00fcssel, Indizes, Constraints usw. verwendet werden). Von jeder grafischen IDE oder jedem minimalen DB-Manager erwarten wir in erster Linie genau diese F\u00e4higkeiten. Es sollte schnell gehen, ohne dass wir eine halbe Stunde warten m\u00fcssen, bis das Fenster mit den ben\u00f6tigten Informationen angezeigt wird (insbesondere bei einer langsamen Verbindung zur remote DB), und die erhaltenen Informationen sollten aktuell und frisch sein, nicht veraltete gecachte Daten. Je komplizierter und umfangreicher die Datenbank ist und je mehr Datenbanken es gibt, desto schwieriger wird es, dies zu erreichen.<\/p>\n<p><\/p>\n<p>In der Regel lege ich die Maus aber beiseite und schreibe einfach den Code. Angenommen, ich muss herausfinden, welche Tabellen (und mit welchen Eigenschaften) im Schema \"HR\" enthalten sind. In den meisten DBMS kann man das gew\u00fcnschte Ergebnis mit einer einfachen Abfrage aus dem information_schema erzielen:<\/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>Von Datenbank zu Datenbank variiert der Inhalt solcher Nachschlagetabellen je nach den F\u00e4higkeiten jedes DBMS. For MySQL kann man beispielsweise aus diesem Nachschlagewerk spezifische Parameter f\u00fcr diese Datenbank erhalten:<\/p>\n<p><\/p>\n<pre><code class=\"sql\">select table_name\n     , storage_engine -- Verwendeter \"Engine\" (\"MyISAM\", \"InnoDB\" usw.)\n     , row_format     -- Zeilenformat (\"Fixed\", \"Dynamic\" usw.)\n     , ...\n  from information_schema.tables\n where schema = 'HR'<\/code><\/pre>\n<p><\/p>\n<p>Oracle unterst\u00fctzt kein information_schema, daf\u00fcr hat es <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Oracle_metadata\">Oracle-Metadaten<\/a><\/noindex>, und es gibt keine gr\u00f6\u00dferen Probleme:<\/p>\n<p><\/p>\n<pre><code class=\"sql\">select table_name\n     , pct_free       -- Minimum an freiem Platz im Datenblock (%)\n     , pct_used       -- Minimum an verwendetem Platz im Datenblock (%)\n     , last_analyzed  -- Datum der letzten Statistiksammlung\n     , ...\n  from all_tables\n where owner = 'HR'<\/code><\/pre>\n<p><\/p>\n<p>Auch ClickHouse bildet da keine Ausnahme:<\/p>\n<p><\/p>\n<pre><code class=\"sql\">select name\n     , engine -- Verwendeter \"Engine\" (\"MergeTree\", \"Dictionary\" usw.)\n     , ...\n  from system.tables\n where database = 'HR'<\/code><\/pre>\n<p><\/p>\n<p>Etwas \u00c4hnliches kann man auch in Cassandra machen (wo es column families anstelle von Tabellen und keyspaces anstelle von Schemata gibt):<\/p>\n<p><\/p>\n<pre><code class=\"sql\">select columnfamily_name\n     , compaction_strategy_class  -- M\u00fcllabfuhrstrategie\n     , gc_grace_seconds           -- Lebensdauer des M\u00fclls\n     , ...\n  from system.schema_columnfamilies\n where keyspace_name = 'HR'<\/code><\/pre>\n<p><\/p>\n<p>F\u00fcr die meisten anderen DBs lassen sich ebenfalls \u00e4hnliche Abfragen formulieren (sogar in Mongo gibt es <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.mongodb.com\/manual\/reference\/system-collections\/#%3Cdatabase%3E.system.namespaces\">eine spezielle Systemkollektion<\/a><\/noindex>, die Informationen \u00fcber alle Sammlungen im System enth\u00e4lt.)<\/p>\n<p><\/p>\n<p>Nat\u00fcrlich kann man auf diese Weise Informationen nicht nur \u00fcber Tabellen, sondern auch \u00fcber jedes andere Objekt erhalten. Gelegentlich teilen freundliche Leute solchen Code f\u00fcr verschiedene Datenbanken, wie zum Beispiel in der Reihe von Hub-Artikel \"Funktionen zur Dokumentation von PostgreSQL-Datenbanken\" (<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/post\/415575\">\u0430\u0439\u0431<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/post\/415897\">\u0431\u0435\u043d<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/post\/418597\">\u0433\u0438\u043c<\/a><\/noindex>). Nat\u00fcrlich ist es kein wirkliches Vergn\u00fcgen, all diese Abfragen im Kopf zu behalten und sie st\u00e4ndig neu einzugeben, deshalb habe ich in meiner bevorzugten IDE\/Editor einen vorab vorbereiteten Satz von Snippets f\u00fcr h\u00e4ufig verwendete Abfragen, und es bleibt nur noch, die Objektnamen in die Vorlage einzugeben.<\/p>\n<p><\/p>\n<p>Das Ergebnis ist, dass diese Art der Navigation und Objektsuche viel flexibler ist, viel Zeit spart und genau die Informationen in der Form liefert, in der sie jetzt ben\u00f6tigt werden (wie zum Beispiel in dem Post beschrieben, <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/company\/JetBrains\/blog\/342094\">\"Datenexport aus der DB in jedem Format: Was k\u00f6nnen IDEs auf der IntelliJ-Plattform?\"<\/a><\/noindex>).<\/p>\n<p><\/p>\n<h5 id=\"operacii-s-obektami\">Operationen mit Objekten<\/h5>\n<p><\/p>\n<p>Nachdem wir die ben\u00f6tigten Objekte gefunden und untersucht haben, ist es an der Zeit, etwas N\u00fctzliches damit zu machen. Nat\u00fcrlich, ohne die Finger von der Tastatur zu nehmen.<\/p>\n<p><\/p>\n<p>Es ist kein Geheimnis, dass das einfache L\u00f6schen einer Tabelle fast in allen Datenbanken gleich aussieht:<\/p>\n<p><\/p>\n<pre><code class=\"sql\">drop table hr.persons<\/code><\/pre>\n<p><\/p>\n<p>Die Erstellung von Tabellen ist bereits interessanter. Praktisch jede DBMS (einschlie\u00dflich vieler NoSQL-Datenbanken) kann irgendwie \"create table\" verwenden, und der Hauptteil weicht kaum ab (Name, Liste der Spalten, Datentypen), aber die weiteren Details k\u00f6nnen erheblich variieren und h\u00e4ngen von der internen Struktur und den M\u00f6glichkeiten der spezifischen DBMS ab. Mein Lieblingsbeispiel \u2014 in der Oracle-Dokumentation gibt es nur die \"nackten\" BNF f\u00fcr die Syntax von \"create table\". <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.oracle.com\/en\/database\/oracle\/oracle-database\/19\/sqlrf\/sql-language-reference.pdf\">die 31 Seiten einnehmen<\/a><\/noindex>. Andere DBMS verf\u00fcgen \u00fcber bescheidenere M\u00f6glichkeiten, aber jede von ihnen hat auch viele interessante und einzigartige Funktionen zur Erstellung von Tabellen (<noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/static\/sql-createtable.html\">in einer Umgebung, in der die Locale nicht UTF-8 ist, beispielsweise in einer leeren Umgebung (hier<\/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>). Kaum wird ein grafischer \"Wizard\" aus irgendeiner IDE (insbesondere einer universellen) all diese F\u00e4higkeiten vollst\u00e4ndig abdecken k\u00f6nnen, und wenn doch, wird es ein Anblick f\u00fcr die Nervenstarken sein. Gleichzeitig ist ein korrekt und rechtzeitig geschriebener Befehl <strong><em>create table<\/em><\/strong> es, all diese Funktionen m\u00fchelos zu nutzen und die Speicherung sowie den Zugriff auf Ihre Daten zuverl\u00e4ssig, optimal und maximal komfortabel zu gestalten.<\/p>\n<p><\/p>\n<p>In vielen DBMS gibt es auch spezifische Objektarten, die in anderen DBMS fehlen. Zudem k\u00f6nnen wir nicht nur Operationen auf DB-Objekten durchf\u00fchren, sondern auch auf dem DBMS selbst, zum Beispiel einen Prozess \"t\u00f6ten\", einen bestimmten Speicherbereich freigeben, Tracing aktivieren, in den \"read only\"-Modus wechseln und vieles mehr.<\/p>\n<p><\/p>\n<h5 id=\"a-teper-nemnogo-porisuem\">Jetzt lassen Sie uns ein wenig zeichnen.<\/h5>\n<p><\/p>\n<p>Eine der h\u00e4ufigsten Aufgaben ist es, ein Diagramm mit DB-Objekten zu erstellen, um auf einem sch\u00f6nen Bild die Objekte und die Beziehungen zwischen ihnen zu sehen. Praktisch jede grafische IDE, einzelne \"Command Line\"-Utilities, spezialisierte grafische Tools und Modellierungswerkzeuge k\u00f6nnen dies. Diese zeigen Ihnen etwas \"so gut sie k\u00f6nnen\" an, und auf diesen Prozess kann man nur durch einige Parameter in der Konfigurationsdatei oder durch H\u00e4kchen im Interface Einfluss nehmen.<\/p>\n<p><\/p>\n<p>Aber dieses Problem l\u00e4sst sich viel einfacher, flexibler und eleganter l\u00f6sen, und nat\u00fcrlich mithilfe von Code. F\u00fcr die Erstellung von Diagrammen beliebiger Komplexit\u00e4t stehen uns mehrere spezialisierte Markup-Sprachen (DOT, GraphML etc.) zur Verf\u00fcgung, und dazu eine Vielzahl von Anwendungen (GraphViz, PlantUML, Mermaid), die in der Lage sind, solche Anweisungen zu lesen und in den unterschiedlichsten Formaten zu visualisieren. Die Informationen \u00fcber Objekte und deren Beziehungen haben wir bereits und wissen, wie wir sie erhalten k\u00f6nnen.<\/p>\n<p><\/p>\n<p>Hier ist ein kleines Beispiel daf\u00fcr, wie das aussehen k\u00f6nnte, unter Verwendung von PlantUML und <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/postgrespro\/blog\/316428\/\">einer Demodatenbank f\u00fcr PostgreSQL.<\/a><\/noindex> (links der SQL-Befehl, der die ben\u00f6tigte Anweisung f\u00fcr PlantUML generiert, und rechts das Ergebnis):<\/p>\n<p>\n<img decoding=\"async\" alt=\"\u201eDatabase as Code\u201c Experience\" 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>Wenn man ein wenig mehr M\u00fche investiert, kann man auf Basis <noindex><a rel=\"nofollow\" href=\"https:\/\/gist.github.com\/QuantumGhost\/0955a45383a0b6c0bc24f9654b3cb561\">einer ER-Vorlage f\u00fcr PlantUML<\/a><\/noindex> etwas sehr \u00c4hnliches wie ein echtes ER-Diagramm erhalten:<\/p>\n<p><\/p>\n<p>                        <b class=\"spoiler_title\">Die SQL-Abfrage ist ein wenig komplexer.<\/b><\/p>\n<pre><code class=\"sql\">-- Kopf\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-- Tabellen\nselect format(&#039;Table(%s, &quot;%s n Informationen &uuml;ber %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-- Beziehungen zwischen Tabellen\nselect distinct ccu.table_name || &#039; &quot;1&quot; --&amp;gt; &quot;0..N&quot; &#039; || tc.table_name || format(&#039; : &quot;Eine %s kann viele %s haben&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-- Fu&szlig;zeile\nselect &#039;@enduml&#039;<\/code><\/pre>\n<p>\n<img decoding=\"async\" alt=\"\u201eDatabase as Code\u201c Experience\" src=\"\/wp-content\/uploads\/2020\/06\/bd45420a4fd1bdfcf20cbe74a0638c7b.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p>Wenn man genau hinsieht, verwenden viele Visualisierungstools unter der Haube genau solche \u00e4hnlichen Abfragen. Diese Abfragen sind jedoch normalerweise tief <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/pgmodeler\/pgmodeler\/blob\/9c615c0b0871df3cd649983ce61e802b0ff0137b\/schemas\/catalog\/table.sch\">\"eingebettet\" im Code der Anwendung und schwer zu verstehen.<\/a><\/noindex>, ganz zu schweigen von einer m\u00f6glichen Modifikation.<\/p>\n<p><\/p>\n<h5 id=\"metriki-i-monitoring\">Metriken und \u00dcberwachung<\/h5>\n<p><\/p>\n<p>Kommen wir zu einem traditionell komplexen Thema \u2014 der \u00dcberwachung der Datenbankleistung. Ich erinnere mich an eine kleine wahre Geschichte, die mir \"ein Freund von mir\" erz\u00e4hlt hat. In einem bestimmten Projekt lebte ein m\u00e4chtiger DBA, den nur wenige der Entwickler pers\u00f6nlich kannten oder \u00fcberhaupt je gesehen hatten (obwohl er, Ger\u00fcchten zufolge, irgendwo im benachbarten Geb\u00e4ude arbeitete). In der Stunde \"X\", wenn das Produktionssystem eines gro\u00dfen Einzelh\u00e4ndlers wieder einmal \"schlecht lief\", schickte er still und leise Screenshots von Diagrammen aus dem Oracle Enterprise Manager, auf denen er die kritischen Punkte sorgsam mit einem roten Stift zur \"Veranschaulichung\" markierte (das half, milde gesagt, kaum). Und so mussten wir anhand dieses \"Fotos\" die Probleme beheben. Allerdings hatte niemand Zugang zu dem wertvollen (in jeder Hinsicht) Enterprise Manager, da das System komplex und teuer war; nicht dass die \"Entwickler aus Versehen irgendetwas kaputt machen\". Daher fanden die Entwickler auf \"empirische\" Weise die Stelle und die Ursache der Verz\u00f6gerungen und ver\u00f6ffentlichten einen Patch. Wenn das eindringliche Schreiben vom DBA nicht bald wieder eintraf, atmeten alle erleichtert auf und kehrten zu ihren aktuellen Aufgaben zur\u00fcck (bis zum neuen Schreiben).<\/p>\n<p><\/p>\n<p>Der Prozess der \u00dcberwachung kann jedoch viel freundlicher und zug\u00e4nglicher gestaltet werden, vor allem \u2014 f\u00fcr alle transparent. Zumindest der grundlegende Teil kann als Erg\u00e4nzung zu den Haupt\u00fcberwachungssystemen (die ohne Frage n\u00fctzlich und in vielen F\u00e4llen unverzichtbar sind) dienen. Jede Datenbankmanagementsystem (DBMS) ist bereit, Informationen \u00fcber ihren aktuellen Zustand und ihre Leistung frei und kostenlos zu teilen. In der \"blutigen\" Oracle DB kann fast jede Information zur Leistung aus den Systemansichten abgerufen werden, beginnend bei Prozessen und Sitzungen bis hin zum Zustand des Puffercaches (zum Beispiel, <noindex><a rel=\"nofollow\" href=\"https:\/\/oracle-base.com\/dba\/scripts\">DBA-Skripte<\/a><\/noindex>, Abschnitt \"Monitoring\"). In PostgreSQL gibt es ebenfalls eine Vielzahl von Systemansichten f\u00fcr <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/static\/monitoring-stats.html\">DB-\u00dcberwachung<\/a><\/noindex>, darunter unabdingbare wie <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>. In MySQL gibt es sogar ein separates Schema daf\u00fcr <noindex><a rel=\"nofollow\" href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.0\/en\/performance-schema-table-descriptions.html\">performance_schema<\/a><\/noindex>. Und in MongoDB aggregiert der eingebaute <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.mongodb.com\/manual\/tutorial\/manage-the-database-profiler\/\">Profiler<\/a><\/noindex> Leistungsdaten in einer systemweiten Sammlung <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>So kann man mit einem beliebigen Metrik-Sammler (Telegraf, Metricbeat, Collectd), der benutzerdefinierte SQL-Abfragen ausf\u00fchren kann, einem Speicher dieser Metriken (InfluxDB, Elasticsearch, Timescaledb) und einem Visualisierungstool (Grafana, Kibana) ein relativ einfaches und flexibles \u00dcberwachungssystem erstellen, das eng mit anderen allgemeinen Systemmetriken (die beispielsweise vom Anwendungsserver, vom Betriebssystem usw. erhalten werden) integriert ist. Wie dies beispielsweise in pgwatch2 der Fall ist, wo eine Kombination aus InfluxDB + Grafana und einer Reihe von Abfragen an systemische Sichtweisen verwendet wird, zu denen auch benutzerdefinierte Abfragen hinzugef\u00fcgt werden k\u00f6nnen. <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/cybertec-postgresql\/pgwatch2#adding-metrics\">benutzerdefinierte Abfragen hinzuf\u00fcgen<\/a><\/noindex>.<\/p>\n<p><\/p>\n<h2 id=\"itogo\">Insgesamt<\/h2>\n<p><\/p>\n<p>Und das ist nur eine grobe \u00dcbersicht dessen, was man mit unserer Datenbank durch regul\u00e4ren SQL-Code machen kann. Ich bin mir sicher, dass es noch viele weitere Anwendungen gibt. Schreibt eure Ideen in die Kommentare. Und dar\u00fcber, wie (und vor allem warum) man das alles automatisieren und in seine CI\/CD-Pipeline integrieren kann, werden wir beim n\u00e4chsten Mal sprechen.<\/p>\n<p>Quelle: <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.1.1 - 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\/de\/blog\/administrirovanie\/database-as-sode-experience\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"de_DE\" \/>\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\/de\/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\u201eDatabase as Code\u201c Erfahrung | ProHoster","description":"SQL, was k\u00f6nnte einfacher sein?","canonical_url":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/database-as-sode-experience","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"de_DE","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\/de\/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\/de\/wp-json\/wp\/v2\/posts\/83649","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/comments?post=83649"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/83649\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media\/83650"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media?parent=83649"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/categories?post=83649"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/tags?post=83649"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}