{"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\/sv\/blog\/administrirovanie\/database-as-sode-experience","title":{"rendered":"\u00abDatabase as Code\u00bb Erfarenhet","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"\u00abDatabase as Code\u00bb Erfarenhet\" src=\"\/wp-content\/uploads\/2020\/06\/a1ac23deeded97559fbe787ed09184a5.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>SQL, vad kan vara enklare? Vi kan alla skriva en enkel fr\u00e5ga \u2014 vi skriver <strong><em>select<\/em><\/strong>, listar de n\u00f6dv\u00e4ndiga kolumnerna, sedan <strong><em>fr\u00e5n<\/em><\/strong>, tabellnamnet, lite villkor i <strong><em>d\u00e4r<\/em><\/strong> och allt \u2014 d\u00e5 har vi anv\u00e4ndbara data i fickan, n\u00e4stan oavsett vilken databas som ligger under huven (eller kanske inte ens \u00e4r en databas) <noindex><a rel=\"nofollow\" href=\"https:\/\/osquery.io\/\">Inte en databas alls<\/a><\/noindex>). Som ett resultat kan arbetet med n\u00e4stan vilken datak\u00e4lla som helst (relationell eller inte) ses ur en vanlig kods perspektiv (med allt som f\u00f6ljer \u2014 versionshantering, kodgranskning, statisk analys, automatiserade tester och allt det d\u00e4r). Och detta g\u00e4ller inte bara sj\u00e4lva datan, scheman och migreringar, utan \u00f6verlag hela livscykeln f\u00f6r datalagret. I denna artikel kommer vi att diskutera de vardagliga uppgifterna och problemen med arbete med olika databaser under linsen av \"database as code\".<\/p>\n<p><\/p>\n<p>Och vi b\u00f6rjar direkt med <noindex><a rel=\"nofollow\" href=\"https:\/\/www.yegor256.com\/2014\/12\/01\/orm-offensive-anti-pattern.html\">ORM<\/a><\/noindex>. De f\u00f6rsta striderna av typen \"SQL vs ORM\" observerades redan i <noindex><a rel=\"nofollow\" href=\"https:\/\/www.sql.ru\/forum\/904343\/orm-vs-sql\">f\u00f6re Peter den stores tid<\/a><\/noindex>.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h5 id=\"obektno-relyacionnyy-maping\">Objekt-relationell mappning<\/h5>\n<p><\/p>\n<p>F\u00f6respr\u00e5kare av ORM v\u00e4rderar traditionellt hastighet och enkelhet i utvecklingen, oberoende av databasen och renheten i koden. F\u00f6r m\u00e5nga av oss ser koden f\u00f6r databasarbetet (och ofta sj\u00e4lva databasen)<\/p>\n<p><\/p>\n<p>                        <b class=\"spoiler_title\">ser vanligtvis ut ungef\u00e4r s\u00e5 h\u00e4r\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>Modellen \u00e4r prydd med smarta annotationer, och n\u00e5gonstans bakom kulisserna genererar och exekverar den \u00e5terh\u00e5llsamma ORM:en tonvis med SQL-kod. F\u00f6r att s\u00e4ga s\u00e5, f\u00f6rs\u00f6ker utvecklarna med alla medel att skydda sig fr\u00e5n sin databas med kilometer av abstraktioner, vilket tyder p\u00e5 en viss <noindex><a rel=\"nofollow\" href=\"https:\/\/www.sql.ru\/forum\/1303231\/prichiny-nenavisti-k-yazyku-sql\">\"SQL-hatred\"<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>P\u00e5 andra sidan barrikaderna noterar anh\u00e4ngare av ren \"handmade\"-SQL m\u00f6jligheten att pressa ut maximal prestanda fr\u00e5n sin DBMS utan ytterligare lager och abstraktioner. Detta leder till \"data-centric\" projekt d\u00e4r databasen hanteras av specialutbildade personer (de kallas ocks\u00e5 \"databasister\", \"databasfolk\", \"databaskunniga\" och s\u00e5 vidare), och utvecklarna f\u00e5r bara \"dra\" f\u00e4rdiga vyer och lagrade procedurer utan att g\u00e5 in i detaljer.<\/p>\n<p><\/p>\n<p>Vad s\u00e4gs om att ta det b\u00e4sta fr\u00e5n tv\u00e5 v\u00e4rldar? Hur det g\u00f6rs i det fantastiska verktyget med det livsbejakande namnet <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/krisajenkins\/yesql\">Yesql<\/a><\/noindex>. Jag kommer att ge ett par rader fr\u00e5n den \u00f6vergripande konceptet i min fria \u00f6vers\u00e4ttning, och mer detaljerat kan man bekanta sig med det <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/krisajenkins\/yesql#rationale\">h\u00e4r<\/a><\/noindex>.<\/p>\n<p><\/p>\n<blockquote><p>Clojure \u00e4r ett fantastiskt spr\u00e5k f\u00f6r att skapa DSL:er, men SQL i sig \u00e4r redan en kraftfull DSL, och vi beh\u00f6ver inte ytterligare en. S-expressioner \u00e4r fantastiska, men h\u00e4r tillf\u00f6r de inget nytt. I slut\u00e4ndan f\u00e5r vi parenteser f\u00f6r parentesernas skull. H\u00e5ller du inte med? D\u00e5 kan du v\u00e4nta p\u00e5 det \u00f6gonblick d\u00e5 abstraktionen \u00f6ver databasen l\u00e4cker, och du b\u00f6rjar kampen med funktionen. <em>(raw-sql)<\/em><\/p>\n<p>Och vad ska vi g\u00f6ra? L\u00e5t oss l\u00e5ta SQL f\u00f6rbli vanligt SQL \u2014 en fil f\u00f6r en f\u00f6rfr\u00e5gan:<\/p><\/blockquote>\n<p><\/p>\n<pre><code class=\"sql\">-- namn: users-by-country\nselect *\n  from users\n where country_code = :country_code<\/code><\/pre>\n<p><\/p>\n<blockquote><p>\u2026 och l\u00e4s sedan denna fil, och omvandla den till en vanlig Clojure-funktion:<\/p><\/blockquote>\n<p><\/p>\n<pre><code class=\"lisp\">(defqueries \"some\/where\/users_by_country.sql\"\n   {:connection db-spec})\n\n;;; En funktion med namnet `users-by-country` har skapats.\n;;; L\u00e5t oss anv\u00e4nda den:\n(users-by-country {:country_code \"GB\"})\n;=&gt; ({:name \"Kris\" :country_code \"GB\" ...} ...)<\/code><\/pre>\n<p><\/p>\n<blockquote><p>Genom att f\u00f6lja principen \"SQL separat, Clojure separat\" f\u00e5r du:<\/p>\n<ul>\n<li>Inga syntax\u00f6verraskningar. Er databas (precis som vilken annan som helst) f\u00f6ljer inte SQL-standarden till 100% \u2014 men f\u00f6r Yesql spelar det ingen roll. Ni kommer inte att sl\u00f6sa tid p\u00e5 att jaga efter funktioner med syntax som \u00e4r likv\u00e4rdig med SQL. Ni kommer aldrig att beh\u00f6va \u00e5terv\u00e4nda till funktionen. <em>(raw-sql \"some (\u2018funky\u2019 :: SYNTAX)\")<\/em>.<\/li>\n<li>B\u00e4sta editorst\u00f6d. Er editor har redan utm\u00e4rkt st\u00f6d f\u00f6r SQL. Genom att beh\u00e5lla SQL som SQL kan ni helt enkelt anv\u00e4nda det.<\/li>\n<li>Kompatibilitet inom teamet. Era DBA:er kan l\u00e4sa och skriva SQL som ni anv\u00e4nder i ert Clojure-projekt.<\/li>\n<li>Enklare prestandaoptimering. Beh\u00f6ver ni skapa en plan f\u00f6r en problematisk fr\u00e5ga? Inga problem n\u00e4r er fr\u00e5ga \u00e4r vanlig SQL.<\/li>\n<li>\u00c5teranv\u00e4ndning av fr\u00e5gor. Dra och sl\u00e4pp dessa samma SQL-filer till andra projekt, f\u00f6r det handlar bara om gammaldags SQL \u2014 dela dem bara.<\/li>\n<\/ul>\n<p>\n<\/p><\/blockquote>\n<p>Enligt mig \u00e4r id\u00e9n mycket cool och samtidigt mycket enkel, vilket har gett projektet m\u00e5nga f\u00f6rdelar. <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/krisajenkins\/yesql#other-languages\">f\u00f6ljare.<\/a><\/noindex> p\u00e5 olika spr\u00e5k. Och vi kommer att f\u00f6rs\u00f6ka till\u00e4mpa en liknande filosofi att separera SQL-kod fr\u00e5n allt annat l\u00e5ngt bortom ORM.<\/p>\n<p><\/p>\n<h5 id=\"ide--db-menedzhery\">IDE &amp; DB-hanterare<\/h5>\n<p><\/p>\n<p>L\u00e5t oss b\u00f6rja med en enkel, vanlig uppgift. Ofta m\u00e5ste vi s\u00f6ka efter vissa objekt i databasen, till exempel hitta tabeller i ett schema och studera deras struktur (vilka kolumner, nycklar, index, begr\u00e4nsningar, etc. som anv\u00e4nds). Och fr\u00e5n vilken grafisk IDE eller ens en enklare DB-hanterare f\u00f6rv\u00e4ntar vi oss f\u00f6rst och fr\u00e4mst just dessa f\u00f6rm\u00e5gor. F\u00f6r att det ska g\u00e5 snabbt och vi inte ska beh\u00f6va v\u00e4nta en halvtimme p\u00e5 att ett f\u00f6nster med den n\u00f6dv\u00e4ndiga informationen ska visas (s\u00e4rskilt vid en l\u00e5ngsam anslutning till en fj\u00e4rrdatabas), och dessutom att den information vi f\u00e5r \u00e4r aktuell och f\u00e4rsk, och inte gammal cachelagrad information. Ju mer komplex och st\u00f6rre databasen \u00e4r, och ju fler databaser det finns, desto sv\u00e5rare \u00e4r det att g\u00f6ra detta.<\/p>\n<p><\/p>\n<p>Men vanligtvis sl\u00e4nger jag bort musen och skriver bara kod. L\u00e5t oss s\u00e4ga att vi beh\u00f6ver veta vilka tabeller (och med vilka egenskaper) som finns i schemat \"HR\". I de flesta databaser kan vi uppn\u00e5 det \u00f6nskade resultatet med en s\u00e5 h\u00e4r enkel fr\u00e5ga fr\u00e5n 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>Inneh\u00e5llet i s\u00e5dana referenstabeller varierar fr\u00e5n databas till databas beroende p\u00e5 kapaciteterna hos varje DBMS. Och till exempel f\u00f6r MySQL kan vi f\u00e5 specifika parametrar f\u00f6r denna DBMS fr\u00e5n samma referens:<\/p>\n<p><\/p>\n<pre><code class=\"sql\">select table_name\n     , storage_engine -- Anv\u00e4nd motor (\"MyISAM\", \"InnoDB\" etc)\n     , row_format     -- Radformat (\"Fixed\", \"Dynamic\" etc)\n     , ...\n  from information_schema.tables\n where schema = 'HR'<\/code><\/pre>\n<p><\/p>\n<p>Oracle kan inte information_schema, men det har <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Oracle_metadata\">Oracle metadata<\/a><\/noindex>, och det uppst\u00e5r inga stora problem:<\/p>\n<p><\/p>\n<pre><code class=\"sql\">select table_name\n     , pct_free       -- Minimal ledig plats i datakluster (%)\n     , pct_used       -- Minimal anv\u00e4nd plats i datakluster (%)\n     , last_analyzed  -- Datum f\u00f6r senaste statistikinsamlingen\n     , ...\n  from all_tables\n where owner = 'HR'<\/code><\/pre>\n<p><\/p>\n<p>ClickHouse \u00e4r inget undantag:<\/p>\n<p><\/p>\n<pre><code class=\"sql\">select name\n     , engine -- Anv\u00e4nd motor (\"MergeTree\", \"Dictionary\" etc)\n     , ...\n  from system.tables\n where database = 'HR'<\/code><\/pre>\n<p><\/p>\n<p>N\u00e5got liknande kan g\u00f6ras i Cassandra (d\u00e4r det finns columnfamilies ist\u00e4llet f\u00f6r tabeller och keyspaces ist\u00e4llet f\u00f6r scheman):<\/p>\n<p><\/p>\n<pre><code class=\"sql\">select columnfamily_name\n     , compaction_strategy_class  -- Skr\u00e4puttagningsstrategi\n     , gc_grace_seconds           -- Skr\u00e4pets livsl\u00e4ngd\n     , ...\n  from system.schema_columnfamilies\n where keyspace_name = 'HR'<\/code><\/pre>\n<p><\/p>\n<p>F\u00f6r de flesta andra databaser kan liknande fr\u00e5gor ocks\u00e5 konstrueras (\u00e4ven i Mongo finns det <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.mongodb.com\/manual\/reference\/system-collections\/#%3Cdatabase%3E.system.namespaces\">en speciell systemkollektion<\/a><\/noindex>, som inneh\u00e5ller information om alla kollektioner i systemet).<\/p>\n<p><\/p>\n<p>Naturligtvis kan denna metod anv\u00e4ndas f\u00f6r att f\u00e5 information inte bara om tabeller, utan om vilket objekt som helst. Regelbundet delar v\u00e4nliga m\u00e4nniskor s\u00e5dana koder f\u00f6r olika databaser, som i en serie artiklar p\u00e5 Habr, \"Funktioner f\u00f6r dokumentation av PostgreSQL-databaser\" (<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\">gym<\/a><\/noindex>). Sj\u00e4lvklart \u00e4r det \"inte s\u00e5 roligt\" att h\u00e5lla alla dessa massor av fr\u00e5gor i huvudet och st\u00e4ndigt skriva in dem, s\u00e5 i min favorit IDE\/redigerare har jag ett f\u00f6rberett set av snippetar f\u00f6r de ofta anv\u00e4nda fr\u00e5gorna, och det \u00e5terst\u00e5r bara att fylla i objektnamn i mallen.<\/p>\n<p><\/p>\n<p>Som ett resultat \u00e4r ett s\u00e5dant s\u00e4tt att navigera och s\u00f6ka efter objekt mycket mer flexibelt, sparar mycket tid, och g\u00f6r det m\u00f6jligt att f\u00e5 just den informationen i det format som \u00e4r n\u00f6dv\u00e4ndigt just nu (som beskrivs i inl\u00e4gget <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/company\/JetBrains\/blog\/342094\">\"Export av data fr\u00e5n databasen i vilket format som helst: vad IDE:er p\u00e5 IntelliJ-plattformen kan\"<\/a><\/noindex>).<\/p>\n<p><\/p>\n<h5 id=\"operacii-s-obektami\">Operationer med objekt<\/h5>\n<p><\/p>\n<p>Efter att vi har hittat och studerat de n\u00f6dv\u00e4ndiga objekten \u00e4r det dags att g\u00f6ra n\u00e5got nyttigt med dem. Sj\u00e4lvklart utan att ta fingrarna fr\u00e5n tangentbordet.<\/p>\n<p><\/p>\n<p>Det \u00e4r ingen hemlighet att enkel borttagning av en tabell kommer att se likadan ut n\u00e4stan i alla databaser:<\/p>\n<p><\/p>\n<pre><code class=\"sql\">drop table hr.persons<\/code><\/pre>\n<p><\/p>\n<p>Att skapa en tabell \u00e4r en mer intressant process. N\u00e4stan alla DBMS (inklusive m\u00e5nga NoSQL) kan p\u00e5 ett eller annat s\u00e4tt utf\u00f6ra \"create table\", och den huvudsakliga delen kommer att vara ganska likartad (namn, kolumnlista, datatyper), men andra detaljer kan variera kraftigt och beror p\u00e5 den specifika DBMS:ens interna struktur och funktioner. En av mina favorit exempel \u00e4r att i Oracle-dokumentationen finns det bara \"rena\" BNF:er f\u00f6r syntaxen \"create table\". <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.oracle.com\/en\/database\/oracle\/oracle-database\/19\/sqlrf\/sql-language-reference.pdf\">31 sidor<\/a><\/noindex>. Andra DBMS har mer blygsamma m\u00f6jligheter, men var och en av dem har ocks\u00e5 m\u00e5nga intressanta och unika funktioner f\u00f6r att skapa tabeller (<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>). Det \u00e4r osannolikt att n\u00e5gon grafisk \"wizard\" fr\u00e5n en vanlig IDE (s\u00e4rskilt en universell) kan t\u00e4cka alla dessa funktioner helt, och om det kan s\u00e5 kommer det att vara en upplevelse f\u00f6r k\u00e4nsliga personer. Samtidigt \u00e4r det viktigt att skriva kommandon korrekt och i god tid. <strong><em>create table<\/em><\/strong> att m\u00f6jligg\u00f6ra att utan sv\u00e5righet utnyttja alla dessa, g\u00f6ra lagring och tillg\u00e5ng till dina data p\u00e5litlig, optimal och s\u00e5 bekv\u00e4m som m\u00f6jligt.<\/p>\n<p><\/p>\n<p>Dessutom har m\u00e5nga DBMS sina egna specifika objekttyper som saknas i andra DBMS. Vi kan dessutom utf\u00f6ra operationer inte bara p\u00e5 databasobjekten, utan \u00e4ven p\u00e5 sj\u00e4lva DBMS:en, till exempel \"d\u00f6da\" en process, frig\u00f6ra minnesomr\u00e5den, aktivera sp\u00e5rning, v\u00e4xla till \"read only\" l\u00e4ge och mycket mer.<\/p>\n<p><\/p>\n<h5 id=\"a-teper-nemnogo-porisuem\">Och nu ska vi g\u00f6ra lite teckningar<\/h5>\n<p><\/p>\n<p>En av de vanligaste uppgifterna \u00e4r att skapa ett diagram som visar databasobjekten, d\u00e4r man p\u00e5 en vacker bild kan se objekten och relationerna mellan dem. N\u00e4stan alla grafiska IDE:er, vissa \"command line\"-verktyg, specialiserade grafiska verktyg och modellerare kan g\u00f6ra detta. De ritar n\u00e5got \"efter f\u00f6rm\u00e5ga\", och man kan endast p\u00e5verka processen n\u00e5got med hj\u00e4lp av n\u00e5gra parametrar i konfigurationsfilen eller kryssrutor i gr\u00e4nssnittet.<\/p>\n<p><\/p>\n<p>Men detta problem kan l\u00f6sas p\u00e5 ett mycket enklare, mer flexibelt och elegant s\u00e4tt, naturligtvis med hj\u00e4lp av kod. F\u00f6r att skapa diagram av vilken komplexitet som helst har vi flera specialiserade m\u00e4rkspr\u00e5k (DOT, GraphML, etc.), och med dem en hel del program (GraphViz, PlantUML, Mermaid) som kan l\u00e4sa s\u00e5dana instruktioner och visualisera i olika format. Vi vet redan hur man f\u00e5r information om objekten och relationerna mellan dem.<\/p>\n<p><\/p>\n<p>L\u00e5t oss ge ett litet exempel p\u00e5 hur det skulle kunna se ut, med hj\u00e4lp av PlantUML och <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/postgrespro\/blog\/316428\/\">en demonstrationsdatabas f\u00f6r PostgreSQL<\/a><\/noindex> (till v\u00e4nster SQL-fr\u00e5gan som genererar den n\u00f6dv\u00e4ndiga instruktionen f\u00f6r PlantUML, och till h\u00f6ger resultatet):<\/p>\n<p>\n<img decoding=\"async\" alt=\"\u00abDatabase as Code\u00bb Erfarenhet\" 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>Om man anstr\u00e4nger sig lite kan man p\u00e5 basis av <noindex><a rel=\"nofollow\" href=\"https:\/\/gist.github.com\/QuantumGhost\/0955a45383a0b6c0bc24f9654b3cb561\">ER-mallen f\u00f6r PlantUML<\/a><\/noindex> f\u00e5 n\u00e5got som liknar en riktig ER-diagram:<\/p>\n<p><\/p>\n<p>                        <b class=\"spoiler_title\">SQL-fr\u00e5gan \u00e4r lite mer komplicerad<\/b><\/p>\n<pre><code class=\"sql\">-- Huvud\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-- Tabeller\nselect format(&#039;Table(%s, &quot;%s n information about %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-- Relationer mellan tabeller\nselect distinct ccu.table_name || &#039; &quot;1&quot; --&amp;gt; &quot;0..N&quot; &#039; || tc.table_name || format(&#039; : &quot;A %s may haven many %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-- Sidfot\nselect &#039;@enduml&#039;<\/code><\/pre>\n<p>\n<img decoding=\"async\" alt=\"\u00abDatabase as Code\u00bb Erfarenhet\" src=\"\/wp-content\/uploads\/2020\/06\/bd45420a4fd1bdfcf20cbe74a0638c7b.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p>Om man tittar noga s\u00e5 anv\u00e4nder de flesta visualiseringsverktyg p\u00e5 samma s\u00e4tt liknande fr\u00e5gor. Men dessa fr\u00e5gor \u00e4r vanligtvis djupt <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/pgmodeler\/pgmodeler\/blob\/9c615c0b0871df3cd649983ce61e802b0ff0137b\/schemas\/catalog\/table.sch\">\"inb\u00e4ddade\" i sj\u00e4lva applikationskoden och \u00e4r sv\u00e5ra att f\u00f6rst\u00e5.<\/a><\/noindex>, f\u00f6r att inte tala om att modifiera dem.<\/p>\n<p><\/p>\n<h5 id=\"metriki-i-monitoring\">M\u00e4tningar och \u00f6vervakning<\/h5>\n<p><\/p>\n<p>L\u00e5t oss g\u00e5 vidare till det traditionellt komplexa \u00e4mnet \u2014 \u00f6vervakning av databasprestanda. Jag minns en liten sann historia som ber\u00e4ttades f\u00f6r mig av \"en av mina v\u00e4nner\". P\u00e5 ett projekt fanns det en m\u00e4ktig DBA, och f\u00e5 av utvecklarna k\u00e4nde honom personligen eller ens hade sett honom (\u00e4ven om han, enligt rykten, arbetade n\u00e5gonstans i den intilliggande byggnaden). Vid \"X-timmen\", n\u00e4r produktionssystemet hos en stor \u00e5terf\u00f6rs\u00e4ljare \u00e5terigen b\u00f6rjade \"m\u00e5 d\u00e5ligt\", skickade han tyst sk\u00e4rmdumpar av diagram fr\u00e5n Oracle Enterprise Manager, d\u00e4r han noggrant markerade kritiska punkter med r\u00f6d tusch f\u00f6r \"tydlighet\" (vilket, f\u00f6r att uttrycka det milt, gjorde lite nytta). S\u00e5ledes fick man arbeta med den h\u00e4r \"fotokartl\u00e4ggningen\". Ingen hade tillg\u00e5ng till den dyra och v\u00e4rdefulla Enterprise Manager eftersom systemet var komplext och kostsamt, och pl\u00f6tsligt kunde \"utvecklarna snubbla \u00f6ver n\u00e5got och f\u00f6rst\u00f6ra allt\". D\u00e4rf\u00f6r hittade utvecklarna \"empiriskt\" orsaken till f\u00f6rdr\u00f6jningarna och sl\u00e4ppte en patch. Om inget skr\u00e4mmande brev fr\u00e5n DBA kom tillbaka inom en snar framtid, andades alla ut med l\u00e4ttnad och \u00e5terv\u00e4nde till sina p\u00e5g\u00e5ende uppgifter (tills n\u00e4sta brev kom).<\/p>\n<p><\/p>\n<p>Men \u00f6vervakningsprocessen kan se mer gl\u00e4djefull och v\u00e4nlig ut, och viktigast av allt \u2014 tillg\u00e4nglig och transparent f\u00f6r alla. \u00c5tminstone den grundl\u00e4ggande delen, som ett komplement till huvud\u00f6vervakningssystemen (som utan tvekan \u00e4r anv\u00e4ndbara och i m\u00e5nga fall oumb\u00e4rliga). Varje DBMS \u00e4r villig och helt gratis att dela med sig av information om sitt nuvarande tillst\u00e5nd och prestanda. I den \"blodiga\" Oracle DB kan n\u00e4stan all information om prestanda f\u00e5s fr\u00e5n systemvyer, fr\u00e5n processer och sessioner till statusen f\u00f6r buffertcachen (till exempel, <noindex><a rel=\"nofollow\" href=\"https:\/\/oracle-base.com\/dba\/scripts\">DBA-skript<\/a><\/noindex>, avsnittet \"\u00d6vervakning\"). I PostgreSQL finns det ocks\u00e5 en m\u00e4ngd systemvyer f\u00f6r <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/static\/monitoring-stats.html\">\u00f6vervakning av databash\u00e4lsa<\/a><\/noindex>, s\u00e4rskilt s\u00e5dana som \u00e4r oumb\u00e4rliga i det dagliga livet f\u00f6r varje DBA, som <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>. I MySQL \u00e4r det till och med en separat skema som \u00e4r avsedd f\u00f6r detta, <noindex><a rel=\"nofollow\" href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.0\/en\/performance-schema-table-descriptions.html\">performance_schema<\/a><\/noindex>. Och i Mongo finns det en inbyggd <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.mongodb.com\/manual\/tutorial\/manage-the-database-profiler\/\">profilerare<\/a><\/noindex> som samlar in prestandadata i en systemkollektion <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>S\u00e5ledes, med hj\u00e4lp av en metrics-aggregator (Telegraf, Metricbeat, Collectd) som kan utf\u00f6ra skr\u00e4ddarsydda SQL-fr\u00e5gor, en databas f\u00f6r dessa metrics (InfluxDB, Elasticsearch, Timescaledb) och en visualiserare (Grafana, Kibana), kan man f\u00e5 ett relativt enkelt och flexibelt \u00f6vervakningssystem som kommer att vara n\u00e4ra integrerat med andra systemmetrics (h\u00e4mtade, till exempel, fr\u00e5n applikationsservrar, operativsystem osv.). Som detta, till exempel, g\u00f6rs i pgwatch2, d\u00e4r en kombination av InfluxDB + Grafana och en upps\u00e4ttning fr\u00e5gor mot systemvyer anv\u00e4nds, till vilka man ocks\u00e5 kan <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/cybertec-postgresql\/pgwatch2#adding-metrics\">l\u00e4gga till skr\u00e4ddarsydda fr\u00e5gor<\/a><\/noindex>.<\/p>\n<p><\/p>\n<h2 id=\"itogo\">Sammanfattningsvis<\/h2>\n<p><\/p>\n<p>Och detta \u00e4r bara en prelimin\u00e4r lista \u00f6ver vad man kan g\u00f6ra med v\u00e5r databas genom vanlig SQL-kod. Jag \u00e4r s\u00e4ker p\u00e5 att det finns m\u00e5nga fler till\u00e4mpningar, skriv g\u00e4rna i kommentarerna. N\u00e4sta g\u00e5ng ska vi prata om hur (och viktigast av allt varf\u00f6r) man automatiserar detta och integrerar det i sin CI\/CD-pipeline.<\/p>\n<p>K\u00e4lla: <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.4 - 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\/sv\/blog\/administrirovanie\/database-as-sode-experience\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.4\" \/>\n\t\t<meta property=\"og:locale\" content=\"sv_SE\" \/>\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\/sv\/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, vad kan vara enklare?","canonical_url":"https:\/\/prohoster.info\/sv\/blog\/administrirovanie\/database-as-sode-experience","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"sv_SE","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\/sv\/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\/sv\/wp-json\/wp\/v2\/posts\/83649","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/comments?post=83649"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/posts\/83649\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/media\/83650"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/media?parent=83649"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/categories?post=83649"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/tags?post=83649"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}