{"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\/da\/blog\/administrirovanie\/database-as-sode-experience","title":{"rendered":"\u00abDatabase as Code\u00bb oplevelse","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"\u00abDatabase as Code\u00bb oplevelse\" src=\"\/wp-content\/uploads\/2020\/06\/a1ac23deeded97559fbe787ed09184a5.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>SQL, hvad kan v\u00e6re lettere? Hver af os kan skrive en simpel foresp\u00f8rgsel \u2014 vi indtaster <strong><em>select<\/em><\/strong>, angiver de n\u00f8dvendige kolonner, s\u00e5 <strong><em>fra<\/em><\/strong>, tabelnavnet, lidt betingelser i <strong><em>where<\/em><\/strong> og det hele \u2014 de nyttige data har vi i lommen, n\u00e6sten uafh\u00e6ngigt af hvilken DBMS der er under h\u00e6tten (eller m\u00e5ske endda <noindex><a rel=\"nofollow\" href=\"https:\/\/osquery.io\/\">slet ikke en DBMS<\/a><\/noindex>). Derfor kan arbejdet med n\u00e6sten enhver datakilde (b\u00e5de relationel og ikke s\u00e5 meget) betragtes ud fra et almindeligt kodeperspektiv (med alle de d\u00e9r konsekvenser \u2014 versionskontrol, kodegennemgang, statisk analyse, automatiserede tests og s\u00e5 videre). Og dette g\u00e6lder ikke kun for selve dataene, skemaerne og migrationerne, men generelt for hele livscyklussen af lageret. I denne artikel vil vi tale om de daglige opgaver og udfordringer i arbejdet med forskellige databaser med fokus p\u00e5 &quot;database as code&quot;.<\/p>\n<p><\/p>\n<p>Og lad os starte lige med <noindex><a rel=\"nofollow\" href=\"https:\/\/www.yegor256.com\/2014\/12\/01\/orm-offensive-anti-pattern.html\">ORM<\/a><\/noindex>. De f\u00f8rste kampe i stil med &quot;SQL vs ORM&quot; blev allerede bem\u00e6rket tilbage i <noindex><a rel=\"nofollow\" href=\"https:\/\/www.sql.ru\/forum\/904343\/orm-vs-sql\">f\u00f8r-peter-tiden i Rusland<\/a><\/noindex>.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h5 id=\"obektno-relyacionnyy-maping\">Objekt-relationel mapping<\/h5>\n<p><\/p>\n<p>Tilh\u00e6ngere af ORM v\u00e6rds\u00e6tter traditionelt hastighed og enkelhed i udviklingen, uafh\u00e6ngighed fra DBMS og kodekvalitet. For mange af os ser koden til at arbejde med databaser (og ofte selve databasen)<\/p>\n<p><\/p>\n<p>                        <b class=\"spoiler_title\">ser normalt cirka s\u00e5dan her ud\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>Modellen er udstyret med smarte annotationer, og et sted backstage genererer og udf\u00f8rer den tapre ORM ton af SQL-kode. For \u00f8vrigt fors\u00f8ger udviklerne med alle midler at afsk\u00e6rme sig fra deres DB med kilometer af abstraktioner, hvilket indikerer en form for <noindex><a rel=\"nofollow\" href=\"https:\/\/www.sql.ru\/forum\/1303231\/prichiny-nenavisti-k-yazyku-sql\">&quot;SQL-had&quot;<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>P\u00e5 den anden side af barrikaderne bem\u00e6rker tilh\u00e6ngerne af ren &quot;handmade&quot;-SQL muligheden for at f\u00e5 det maksimale ud af deres DBMS uden yderligere lag og abstraktioner. Hvilket resulterer i &quot;data-centric&quot; projekter, hvor databasen h\u00e5ndteres af specialuddannede folk (de kaldes ogs\u00e5 &quot;databasem\u00e6nd&quot;, &quot;databaser&quot; og s\u00e5 videre), mens udviklerne kun har til opgave at &quot;tr\u00e6kke&quot; f\u00e6rdige visninger og lagrede procedurer uden at g\u00e5 i detaljer.<\/p>\n<p><\/p>\n<p>Hvad hvis vi tager det bedste fra begge verdener? Hvordan dette er gjort i det fantastiske v\u00e6rkt\u00f8j med den livsbekr\u00e6ftende titel <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/krisajenkins\/yesql\">Yesql<\/a><\/noindex>. Jeg vil give et par linjer fra den overordnede koncept i min frie overs\u00e6ttelse, og mere om den kan man l\u00e6se <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/krisajenkins\/yesql#rationale\">her<\/a><\/noindex>.<\/p>\n<p><\/p>\n<blockquote><p>Clojure er et fantastisk sprog til at skabe DSL'er, men SQL er allerede et fantastisk DSL i sig selv, s\u00e5 vi har ikke brug for endnu et. S-udtryk er sk\u00f8nne, men her tilf\u00f8jer de ikke noget nyt. I sidste ende f\u00e5r vi parenteser for parentesernes skyld. Er du uenig? S\u00e5 vent til den abstraktion, der ligger over databasen, begynder at l\u00e6kke, og du starter kampen med funktionen <em>(raw-sql)<\/em><\/p>\n<p>Og hvad skal vi s\u00e5 g\u00f8re? Lad os holde SQL som almindelig SQL \u2014 \u00e9n fil til \u00e9n foresp\u00f8rgsel:<\/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 og l\u00e6s derefter denne fil, og g\u00f8r den til en almindelig Clojure-funktion:<\/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;;; En funktion med navnet `users-by-country` er blevet oprettet.\n;;; Lad os bruge den:\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>Ved at holde fast i princippet &quot;SQL for sig selv, Clojure for sig selv&quot;, f\u00e5r du:<\/p>\n<ul>\n<li>Ingen syntaktiske overraskelser. Din database (som enhver anden) overholder ikke SQL-standarden 100% \u2014 men for Yesql er det ikke vigtigt. Du vil aldrig spilde tid p\u00e5 at lede efter funktioner med syntaks svarende til SQL. Du vil aldrig v\u00e6re n\u00f8dt til at vende tilbage til funktionen <em>(raw-sql &quot;some (&#8216;funky&#8217; :: SYNTAX)&quot;))<\/em>.<\/li>\n<li>Bedre editorst\u00f8tte. Din editor har allerede fremragende underst\u00f8ttelse af SQL. Ved at holde SQL som SQL kan du bare bruge det.<\/li>\n<li>Kompatibilitet med teamet. Dine DBA'er kan l\u00e6se og skrive SQL, som du bruger i dit Clojure-projekt.<\/li>\n<li>Enklere performanceoptimering. Skal du opbygge en plan for en problematisk foresp\u00f8rgsel? Det er ikke et problem, n\u00e5r din foresp\u00f8rgsel er almindelig SQL.<\/li>\n<li>Genbrug af foresp\u00f8rgsler. Tr\u00e6k disse samme SQL-filer til andre projekter, fordi det blot er den gode gamle SQL \u2014 bare del dem.<\/li>\n<\/ul>\n<p>\n<\/p><\/blockquote>\n<p>For mig er id\u00e9en b\u00e5de rigtig cool og meget simpel, hvilket har givet projektet en masse <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/krisajenkins\/yesql#other-languages\">medarbejdere.<\/a><\/noindex> p\u00e5 mange forskellige sprog. Vi vil fors\u00f8ge at anvende en tilsvarende filosofi, der adskiller SQL-kode fra alt det andet, langt ud over ORM.<\/p>\n<p><\/p>\n<h5 id=\"ide--db-menedzhery\">IDE &amp; DB- managers<\/h5>\n<p><\/p>\n<p>Lad os starte med en simpel hverdagopgave. Ofte skal vi finde visse objekter i databasen, for eksempel at finde en tabel i skemaet og unders\u00f8ge dens struktur (hvilke kolonner, n\u00f8gler, indekser, begr\u00e6nsninger osv. der anvendes). Og fra enhver grafisk IDE eller et minimalt DB-manager forventer vi i f\u00f8rste omgang netop disse evner. Det skal g\u00e5 hurtigt, og man skal ikke vente halvtreds minutter p\u00e5, at vinduet med de n\u00f8dvendige oplysninger dukker op (is\u00e6r ved en langsom forbindelse til den fjerntliggende database), samtidig med at de oplysninger, der vises, er friske og aktuelle og ikke gammelt cachelagret data. Jo mere kompleks og st\u00f8rre databasen er, og jo flere databaser der er, jo sv\u00e6rere er det at g\u00f8re det.<\/p>\n<p><\/p>\n<p>Men normalt smider jeg musen v\u00e6k og skriver bare kode. Lad os sige, at jeg skal finde ud af, hvilke tabeller (og med hvilke egenskaber) der findes i skemaet \"HR\". I de fleste DBMS kan man opn\u00e5 det \u00f8nskede resultat med denne enkle foresp\u00f8rgsel fra 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>Indholdet af s\u00e5danne reference-tabeller varierer fra database til database afh\u00e6ngigt af hver databasesystems kapaciteter. For eksempel, for MySQL kan man fra den samme reference f\u00e5 specifikke parametre for denne database:<\/p>\n<p><\/p>\n<pre><code class=\"sql\">select table_name\n     , storage_engine -- Den anvendte \"motor\" (\"MyISAM\", \"InnoDB\" osv.)\n     , row_format     -- R\u00e6kkens format (\"Fixed\", \"Dynamic\" osv.)\n     , ...\n  from information_schema.tables\n where schema = 'HR'<\/code><\/pre>\n<p><\/p>\n<p>Oracle bruger ikke information_schema, men har derimod <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Oracle_metadata\">Oracle metadata<\/a><\/noindex>, og der opst\u00e5r ikke store problemer:<\/p>\n<p><\/p>\n<pre><code class=\"sql\">select table_name\n     , pct_free       -- Minimum fri plads i datablokken (%)\n     , pct_used       -- Minimum brugt plads i datablokken (%)\n     , last_analyzed  -- Dato for sidste statistiksamling\n     , ...\n  from all_tables\n where owner = 'HR'<\/code><\/pre>\n<p><\/p>\n<p>ClickHouse er ikke en undtagelse:<\/p>\n<p><\/p>\n<pre><code class=\"sql\">select name\n     , engine -- Den anvendte \"motor\" (\"MergeTree\", \"Dictionary\" osv.)\n     , ...\n  from system.tables\n where database = 'HR'<\/code><\/pre>\n<p><\/p>\n<p>Noget lignende kan g\u00f8res i Cassandra (hvor der er columnfamilies i stedet for tabeller og keyspaces i stedet for skemaer):<\/p>\n<p><\/p>\n<pre><code class=\"sql\">select columnfamily_name\n     , compaction_strategy_class  -- Affaldssamlingsstrategi\n     , gc_grace_seconds           -- Affalds livstid\n     , ...\n  from system.schema_columnfamilies\n where keyspace_name = 'HR'<\/code><\/pre>\n<p><\/p>\n<p>For de fleste andre databaser kan man ogs\u00e5 konstruere lignende foresp\u00f8rgsler (selv i Mongo findes der <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.mongodb.com\/manual\/reference\/system-collections\/#%3Cdatabase%3E.system.namespaces\">en speciel systemsamling<\/a><\/noindex>, som indeholder information om alle kollektioner i systemet).<\/p>\n<p><\/p>\n<p>Selvf\u00f8lgelig kan man p\u00e5 denne m\u00e5de f\u00e5 information ikke kun om tabeller, men om enhver objekttype. Med j\u00e6vne mellemrum deler venlige mennesker s\u00e5dan kode til forskellige databaser, som for eksempel i serien af Habr-artikler \"Funktioner til dokumentation af PostgreSQL-databaser\" (<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/post\/415575\">ib<\/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>). Selvf\u00f8lgelig er det \"lidt besv\u00e6rligt\" at holde alle disse foresp\u00f8rgsler i hovedet og konstant at indtaste dem, s\u00e5 jeg har et forberedt s\u00e6t snippets til ofte anvendte foresp\u00f8rgsler i min yndlings IDE\/editor, og det eneste der kr\u00e6ves, er at indtaste objekternes navne i skabelonen.<\/p>\n<p><\/p>\n<p>ikke s\u00e6rlig sjovt at holde denne enorme bunke foresp\u00f8rgsler i hovedet og konstant indtaste dem, s\u00e5 i min yndlings IDE\/redigeringsv\u00e6rkt\u00f8j har jeg et forudbestemt s\u00e6t snippets til ofte brugte foresp\u00f8rgsler, og jeg skal blot indtaste navne p\u00e5 objekter i skabelonen. <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/company\/JetBrains\/blog\/342094\">\"Eksport af data fra databasen i ethvert format: Hvad kan IDE'er p\u00e5 IntelliJ-platformen\"<\/a><\/noindex>).<\/p>\n<p><\/p>\n<h5 id=\"operacii-s-obektami\">\"Eksport af data fra databasen i ethvert format: hvad IDE'er p\u00e5 IntelliJ-platformen kan\"<\/h5>\n<p><\/p>\n<p>Operationer med objekter<\/p>\n<p><\/p>\n<p>Efter at vi har fundet og studeret de n\u00f8dvendige objekter, er det p\u00e5 tide at g\u00f8re noget nyttigt med dem. Naturligvis uden at tage fingrene v\u00e6k fra tastaturet.<\/p>\n<p><\/p>\n<pre><code class=\"sql\">Det er ingen hemmelighed, at en simpel sletning af en tabel vil se ens ud i n\u00e6sten alle databaser:<\/code><\/pre>\n<p><\/p>\n<p>Men arbejdet med at oprette tabeller er langt mere interessant. N\u00e6sten enhver DBMS (herunder mange NoSQL) kan p\u00e5 en eller anden m\u00e5de \"oprette tabel\", og den grundl\u00e6ggende del vil ikke adskille sig meget (navn, kolonneliste, datatyper), men de \u00f8vrige detaljer kan variere drastisk og afh\u00e6nger af den specifikke DBMS's indre struktur og muligheder. Et af mine yndlingseksempler er, at der i Oracle-dokumentationen kun findes \"n\u00f8gen\" BNF'er for syntaksen \"opret tabel\". <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.oracle.com\/en\/database\/oracle\/oracle-database\/19\/sqlrf\/sql-language-reference.pdf\">Men det bliver lidt mere interessant med oprettelsen af en tabel. N\u00e6sten enhver DBMS (inklusive mange NoSQL) kan p\u00e5 en eller anden m\u00e5de h\u00e5ndtere \"create table\", og dens hoveddel vil faktisk ikke adskille sig meget (navn, liste af kolonner, datatyper), men de \u00f8vrige detaljer kan variere meget og afh\u00e6ngige af den specifikke databases indhold og evner. Mit yndlingseksempel er, at dokumentationen for Oracle kun bruger \"n\u00f8gne\" BNF'er til syntaksen for \"create table\"<\/a><\/noindex>fylder 31 sider<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\">. Andre databaser har mere beskedne funktioner, men hver af dem har ogs\u00e5 mange interessante og unikke funktioner til oprettelse af tabeller (<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.datastax.com\/en\/cql\/3.3\/cql\/cql_reference\/cqlCreateTable.html\">cockroach<\/a><\/noindex>). Det er usandsynligt, at nogen grafisk \"wizard\" fra en tilf\u00e6ldig IDE (is\u00e6r en universel) vil kunne d\u00e6kke alle disse funktioner fuldt ud, og hvis det g\u00f8r, vil det v\u00e6re et syn, der kan skr\u00e6mme de svage af hjertet. Samtidig er en korrekt og rettidig skrevet erkl\u00e6ring <strong><em>). Det er usandsynligt, at en grafisk \"wizard\" fra en hvilken som helst IDE (is\u00e6r en universel) helt kan d\u00e6kke alle disse evner, og hvis den kan, vil det v\u00e6re et syn for f\u00f8lsomme sj\u00e6le. P\u00e5 den anden side vil en korrekt og rettidigt skrevet operator<\/em><\/strong> create table g\u00f8re det muligt at udnytte dem alle uden problemer og sikre, at opbevaring og adgang til dine data er p\u00e5lidelig, optimal og s\u00e5 komfortabel som muligt.<\/p>\n<p><\/p>\n<p>Desuden har mange DBMS'er deres egne specifikke objekttyper, der ikke findes i andre DBMS'er. Vi kan udf\u00f8re operationer ikke kun p\u00e5 databaseobjekterne, men ogs\u00e5 p\u00e5 selve DBMS'en, for eksempel \"dr\u00e6be\" en proces, frigive et bestemt omr\u00e5de af hukommelsen, aktivere tracing, skifte til \"l\u00e6se kun\"-tilstand og meget mere.<\/p>\n<p><\/p>\n<h5 id=\"a-teper-nemnogo-porisuem\">Og nu skal vi tegne lidt.<\/h5>\n<p><\/p>\n<p>En af de mest almindelige opgaver er at opbygge et diagram med databaseobjekter, s\u00e5 man kan se objekterne og relationerne mellem dem p\u00e5 et flot billede. Dette kan n\u00e6sten enhver grafisk IDE, enkeltst\u00e5ende \"command line\"-v\u00e6rkt\u00f8jer, specialiserede grafiske v\u00e6rkt\u00f8jer og modellerere. De vil tegne noget for dig \"som de kan\", men man kan kun p\u00e5virke denne proces lidt ved hj\u00e6lp af nogle parametre i konfigurationsfilen eller ved at klikke p\u00e5 nogle indstillinger i gr\u00e6nsefladen.<\/p>\n<p><\/p>\n<p>Men dette problem kan l\u00f8ses meget enklere, mere fleksibelt og elegantere, selvf\u00f8lgelig ved hj\u00e6lp af kode. Til opbygning af diagrammer af enhver kompleksitet har vi adskillige specialiserede markup-sprog (DOT, GraphML osv.), samt en r\u00e6kke applikationer (GraphViz, PlantUML, Mermaid), der kan l\u00e6se s\u00e5danne instruktioner og visualisere dem i de mest forskellige formater. Og vi ved allerede, hvordan vi kan f\u00e5 information om objekterne og relationerne mellem dem.<\/p>\n<p><\/p>\n<p>Lad os give et lille eksempel p\u00e5, hvordan dette kan se ud, med brug af PlantUML og <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/postgrespro\/blog\/316428\/\">en demonstrationsdatabase til PostgreSQL<\/a><\/noindex> (til venstre er SQL-foresp\u00f8rgslen, der genererer den n\u00f8dvendige instruktion til PlantUML, og til h\u00f8jre er resultatet):<\/p>\n<p>\n<img decoding=\"async\" alt=\"\u00abDatabase as Code\u00bb oplevelse\" 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>Og hvis man anstrenger sig lidt, kan man basere sig p\u00e5 <noindex><a rel=\"nofollow\" href=\"https:\/\/gist.github.com\/QuantumGhost\/0955a45383a0b6c0bc24f9654b3cb561\">ER-skabelonen for PlantUML<\/a><\/noindex> for at opn\u00e5 noget, der ligner en rigtig ER-diagram:<\/p>\n<p><\/p>\n<p>                        <b class=\"spoiler_title\">SQL-foresp\u00f8rgslen er en smule mere kompleks.<\/b><\/p>\n<pre><code class=\"sql\">-- Header\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-- Tables\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-- Relationships between tables\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 have 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-- Footer\nselect &#039;@enduml&#039;<\/code><\/pre>\n<p>\n<img decoding=\"async\" alt=\"\u00abDatabase as Code\u00bb oplevelse\" src=\"\/wp-content\/uploads\/2020\/06\/bd45420a4fd1bdfcf20cbe74a0638c7b.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p>Hvis man ser n\u00e6rmere efter, s\u00e5 bruger mange visualiseringsv\u00e6rkt\u00f8jer ogs\u00e5 lignende foresp\u00f8rgsler under overfladen. Dog er disse foresp\u00f8rgsler normalt dybt <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/pgmodeler\/pgmodeler\/blob\/9c615c0b0871df3cd649983ce61e802b0ff0137b\/schemas\/catalog\/table.sch\">\"indbygget\" i selve applikationens kode og er sv\u00e6re at forst\u00e5.<\/a><\/noindex>, for ikke at tale om at modificere dem.<\/p>\n<p><\/p>\n<h5 id=\"metriki-i-monitoring\">Metrikker og overv\u00e5gning<\/h5>\n<p><\/p>\n<p>Lad os g\u00e5 videre til et traditionelt komplekst emne \u2014 overv\u00e5gning af databaseydelse. Jeg vil huske en lille sand historie, som blev fortalt mig af \"en af mine venner\". I et projekt var der en magtfuld DBA, som f\u00e5 af udviklerne kendte personligt, endda havde set i \u00f8jnene (selvom han angiveligt arbejdede et sted i nabobygningen). P\u00e5 \"X-tidspunktet\", n\u00e5r produktionssystemet hos en stor detailhandler igen begyndte at \"f\u00f8le sig d\u00e5rligt\", sendte han tavs sk\u00e6rmbilleder af grafer fra Oracle Enterprise Manager, hvor han omhyggeligt markerede kritiske punkter med r\u00f8d mark\u00f8r for \"forst\u00e5elighed\" (det hjalp n\u00e6ppe, sagt mildt). Og s\u00e5 m\u00e5tte man helbrede baseret p\u00e5 dette \"fotografi\". Ingen havde adgang til den dyrebare (i begge betydninger) Enterprise Manager, da systemet var komplekst og dyrt, og man kunne ikke risikere, at udviklerne \"st\u00f8dte p\u00e5 noget og kom til at \u00f8del\u00e6gge alt\". Derfor fandt udviklerne umiddelbart stedet og \u00e5rsagen til forsinkelserne og frigav et patch. Hvis der ikke kom et truende brev fra DBA\u2019en igen i den n\u00e6rmeste fremtid, kunne alle \u00e5nde lettet op og vende tilbage til deres nuv\u00e6rende opgaver (indtil det nye brev).<\/p>\n<p><\/p>\n<p>Men overv\u00e5gningsprocessen kan se mere sjov og venlig ud, og vigtigst af alt \u2014 v\u00e6re tilg\u00e6ngelig og gennemsigtig for alle. I det mindste den grundl\u00e6ggende del som et supplement til de prim\u00e6re overv\u00e5gningssystemer (som uden tvivl er nyttige og i mange tilf\u00e6lde uundg\u00e5elige). Enhver DBMS er villig til helt gratis at dele information om sin nuv\u00e6rende tilstand og ydeevne. I den samme \"blodige\" Oracle DB kan man n\u00e6sten f\u00e5 enhver information om ydeevne fra systemvisninger, begyndende med processer og sessioner og sluttende med tilstanden af buffer-cachen (for eksempel, <noindex><a rel=\"nofollow\" href=\"https:\/\/oracle-base.com\/dba\/scripts\">DBA Scripts<\/a><\/noindex>, sektionen \"Overv\u00e5gning\"). I Postgresql findes der ogs\u00e5 et v\u00e6ld af systemvisninger til <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/static\/monitoring-stats.html\">overv\u00e5gning af databaseoperationer<\/a><\/noindex>, herunder s\u00e5danne uundg\u00e5elige i enhver DBA's hverdag, 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 er der endda et separat skema dedikeret til dette <noindex><a rel=\"nofollow\" href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.0\/en\/performance-schema-table-descriptions.html\">performance_schema<\/a><\/noindex>. Og i Mongo findes der en indbygget <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.mongodb.com\/manual\/tutorial\/manage-the-database-profiler\/\">profiler<\/a><\/noindex> der samler data om ydeevne 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, ved at udstyre sig med en eller anden metrics-samler (Telegraf, Metricbeat, Collectd), der er i stand til at udf\u00f8re tilpassede SQL-foresp\u00f8rgsler, et lager til disse m\u00e5linger (InfluxDB, Elasticsearch, Timescaledb) og en visualizer (Grafana, Kibana), kan man opn\u00e5 et relativt let og fleksibelt overv\u00e5gningssystem, der vil v\u00e6re t\u00e6t integreret med andre generelle systemmetrikker (f.eks. indsamlet fra applikationsserveren, fra operativsystemet osv.). Som for eksempel er gjort i pgwatch2, hvor der bruges et setup med InfluxDB + Grafana og et s\u00e6t foresp\u00f8rgsler til systemvisninger, som ogs\u00e5 kan <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/cybertec-postgresql\/pgwatch2#adding-metrics\">tilf\u00f8je tilpassede foresp\u00f8rgsler<\/a><\/noindex>.<\/p>\n<p><\/p>\n<h2 id=\"itogo\">I alt<\/h2>\n<p><\/p>\n<p>Og dette er kun en omtrentlig liste over, hvad der kan g\u00f8res med vores database ved hj\u00e6lp af almindelig SQL-kode. Jeg er sikker p\u00e5, at der kan findes mange flere anvendelser, skriv i kommentarerne. Og vi vil tale om, hvordan (og vigtigst af alt, hvorfor) man kan automatisere alt dette og inkludere det i sin CI\/CD-pipeline n\u00e6ste gang.<\/p>\n<p>Kilde: <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\/da\/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=\"da_DK\" \/>\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\/da\/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 Oplevelse | ProHoster","description":"SQL, hvad kan v\u00e6re lettere?","canonical_url":"https:\/\/prohoster.info\/da\/blog\/administrirovanie\/database-as-sode-experience","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"da_DK","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\/da\/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\/da\/wp-json\/wp\/v2\/posts\/83649","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/comments?post=83649"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/posts\/83649\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/media\/83650"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/media?parent=83649"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/categories?post=83649"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/da\/wp-json\/wp\/v2\/tags?post=83649"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}