{"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\/en\/blog\/administrirovanie\/database-as-sode-experience","title":{"rendered":"\"Database as Code\" Experience","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"&quot;Database as Code&quot; Experience\" src=\"\/wp-content\/uploads\/2020\/06\/a1ac23deeded97559fbe787ed09184a5.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>SQL, what could be simpler? Each of us can write a simple query \u2014 just type <strong><em>select<\/em><\/strong>, list the required columns, then <strong><em>from<\/em><\/strong>, the table name, add a few conditions in <strong><em>where<\/em><\/strong> and that's it \u2014 we have useful data at our fingertips, (almost) regardless of which DBMS is running behind the scenes (or maybe it's not even a DBMS at all <noindex><a rel=\"nofollow\" href=\"https:\/\/osquery.io\/\">). As a result, working with virtually any data source (relational or otherwise) can be viewed through the lens of regular code (with all the implications \u2014 version control, code review, static analysis, automated tests, and all that). This concerns not just the data, schemas, and migrations, but the overall lifecycle of the repository. In this article, we will discuss everyday tasks and challenges of working with various databases through the prism of \"database as code\".<\/a><\/noindex>). As a result, working with virtually any data source (relational or otherwise) can be viewed in terms of regular code (with all its implications \u2014 version control, code review, static analysis, automated testing, and all that). This applies not only to the data themselves, schemas, and migrations but also to the overall operation of the repository. In this article, we will discuss the everyday tasks and challenges of working with various databases through the lens of 'database as code'.<\/p>\n<p><\/p>\n<p>ORM <noindex><a rel=\"nofollow\" href=\"https:\/\/www.yegor256.com\/2014\/12\/01\/orm-offensive-anti-pattern.html\">. The first battles of \"SQL vs ORM\" were noted back in<\/a><\/noindex>. The first battles of 'SQL vs ORM' were noted back in <noindex><a rel=\"nofollow\" href=\"https:\/\/www.sql.ru\/forum\/904343\/orm-vs-sql\">Object-Relational Mapping<\/a><\/noindex>.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h5 id=\"obektno-relyacionnyy-maping\">ORM proponents traditionally value the speed and simplicity of development, independence from the DBMS, and code cleanliness. For many of us, the code for working with the database (and often the database itself)<\/h5>\n<p><\/p>\n<p>usually looks something like this\u2026<\/p>\n<p><\/p>\n<p>                        <b class=\"spoiler_title\">usually looks something like this\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>\"SQL hatred\" <noindex><a rel=\"nofollow\" href=\"https:\/\/www.sql.ru\/forum\/1303231\/prichiny-nenavisti-k-yazyku-sql\">\"SQL hate\"<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>On the other side of the barricade, proponents of pure 'handmade' SQL emphasize the ability to squeeze every drop from their DBMS without additional layers and abstractions. As a result, 'data-centric' projects emerge, where a specially trained group of individuals (also known as 'database experts', 'database specialists', and so forth) handle the databases, leaving developers to simply 'pull' ready-made views and stored procedures without delving into details.<\/p>\n<p><\/p>\n<p>Yesql <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/krisajenkins\/yesql\">. I'll provide a couple of lines from the overall concept in my loose translation, but you can get to know it in more detail.<\/a><\/noindex>I will provide a couple of lines from the general concept in my free translation, and you can get to know it in more detail. <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/krisajenkins\/yesql#rationale\">here<\/a><\/noindex>.<\/p>\n<p><\/p>\n<blockquote><p>Clojure is a great language for creating DSLs, but SQL is already a powerful DSL by itself, and we do not need another one. S-expressions are beautiful, but they add nothing new here. Ultimately, we end up with parentheses for the sake of parentheses. Don't agree? Then wait until the abstraction over the database springs a leak, and you'll start fighting with functions. <em>(raw-sql)<\/em><\/p>\n<p>So what to do? Let's keep SQL as regular SQL \u2014 one file for one query:<\/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 and then read this file, turning it into a regular Clojure function:<\/p><\/blockquote>\n<p><\/p>\n<pre><code class=\"lisp\">(defqueries \"some\/where\/users_by_country.sql\"\n   {:connection db-spec})\n\n;;; A function named `users-by-country` has been created.\n;;; Let's use it:\n(users-by-country {:country_code \"GB\"})\n;=&gt; ({:name \"Kris\" :country_code \"GB\" ...} ...)<\/code><\/pre>\n<p><\/p>\n<blockquote><p>By adhering to the principle of 'SQL separately, Clojure separately', you get:<\/p>\n<ul>\n<li>No syntactic surprises. Your database (like any other) does not conform to the SQL standard 100% \u2014 but that doesn't matter for Yesql. You'll never waste time hunting for functions with SQL-equivalent syntax. You\u2019ll never have to return to the function <em>(raw-sql \"some ('funky' :: SYNTAX)\")<\/em>.<\/li>\n<li>Better editor support. Your editor already has excellent support for SQL. By keeping SQL as SQL, you can simply use it.<\/li>\n<li>Team compatibility. Your DBAs can read and write SQL that you use in your Clojure project.<\/li>\n<li>Easier performance tuning. Need to build a plan for a problematic query? It\u2019s not an issue when your query is plain SQL.<\/li>\n<li>Reuse of queries. Drag those same SQL files into other projects, because it's just good old SQL \u2014 simply share it.<\/li>\n<\/ul>\n<p>\n<\/p><\/blockquote>\n<p>In my opinion, the idea is very cool and at the same time very simple, which has allowed the project to gain a lot of traction <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/krisajenkins\/yesql#other-languages\">followers<\/a><\/noindex> in a variety of languages. And we will try to apply a similar philosophy of separating SQL code from everything else far beyond ORM.<\/p>\n<p><\/p>\n<h5 id=\"ide--db-menedzhery\">IDE &amp; DB managers<\/h5>\n<p><\/p>\n<p>Let's start with a simple everyday task. Often, we need to search for various objects in the database, for example, to find a table in a schema and examine its structure (what columns, keys, indexes, constraints, etc. are used). From any graphical IDE or even a basic DB manager, we primarily expect these capabilities. It should be fast, so we don't have to wait half an hour for a window to display the required information (especially with a slow connection to a remote database), and the information must be fresh and relevant, not stale cached data. Moreover, the more complex and larger the database, and the more of them there are, the harder it becomes to achieve this.<\/p>\n<p><\/p>\n<p>But usually, I toss the mouse aside and just write code. Let's say I need to find out which tables (and with what properties) are in the \"HR\" schema. In most DBMS, the desired result can be obtained with a simple query from 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>The contents of such reference tables vary from database to database, depending on the capabilities of each DBMS. For example, for MySQL, we can get specific parameters for that DBMS from the same reference:<\/p>\n<p><\/p>\n<pre><code class=\"sql\">select table_name\n     , storage_engine -- The storage engine used (\"MyISAM\", \"InnoDB\", etc.)\n     , row_format     -- The row format (\"Fixed\", \"Dynamic\", etc.)\n     , ...\n  from information_schema.tables\n where schema = 'HR'<\/code><\/pre>\n<p><\/p>\n<p>Oracle does not support information_schema, but it has <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Oracle_metadata\">Oracle metadata<\/a><\/noindex>, and it does not cause major issues:<\/p>\n<p><\/p>\n<pre><code class=\"sql\">select table_name\n     , pct_free       -- Minimum free space in the data block (%)\n     , pct_used       -- Minimum used space in the data block (%)\n     , last_analyzed  -- Date of last statistics collection\n     , ...\n  from all_tables\n where owner = 'HR'<\/code><\/pre>\n<p><\/p>\n<p>ClickHouse is no exception:<\/p>\n<p><\/p>\n<pre><code class=\"sql\">select name\n     , engine -- The storage engine used (\"MergeTree\", \"Dictionary\", etc.)\n     , ...\n  from system.tables\n where database = 'HR'<\/code><\/pre>\n<p><\/p>\n<p>Something similar can be done in Cassandra (where there are column families instead of tables and keyspaces instead of schemas):<\/p>\n<p><\/p>\n<pre><code class=\"sql\">select columnfamily_name\n     , compaction_strategy_class  -- Garbage collection strategy\n     , gc_grace_seconds           -- Garbage lifetime\n     , ...\n  from system.schema_columnfamilies\n where keyspace_name = 'HR'<\/code><\/pre>\n<p><\/p>\n<p>For most other databases, similar queries can also be conceived (even in Mongo, there is a <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.mongodb.com\/manual\/reference\/system-collections\/#%3Cdatabase%3E.system.namespaces\">special system collection<\/a><\/noindex>, which contains information about all collections in the system).<\/p>\n<p><\/p>\n<p>Of course, this method allows you to obtain information not only about tables but about any object at all. Occasionally, well-meaning individuals share such code for different databases, as seen in the series of Habr articles \"Functions for Documenting PostgreSQL Databases\" (<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/post\/415575\">aib<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/post\/415897\">ben<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/post\/418597\">gim<\/a><\/noindex>). Naturally, keeping all these queries in mind and constantly typing them out is not the most enjoyable task, so in my favorite IDE\/editor, I have a pre-prepared set of snippets for frequently used queries, and I only need to input the object names into the template.<\/p>\n<p><\/p>\n<p>As a result, this method of navigating and searching for objects is much more flexible, saves a lot of time, and allows obtaining exactly the information needed in the format currently required (as described in the post <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/company\/JetBrains\/blog\/342094\">\"Exporting data from a database in any format: what IDEs on the IntelliJ platform can do\"<\/a><\/noindex>).<\/p>\n<p><\/p>\n<h5 id=\"operacii-s-obektami\">Operations with Objects<\/h5>\n<p><\/p>\n<p>After we find and study the necessary objects, it's time to do something useful with them. Naturally, without taking our fingers off the keyboard.<\/p>\n<p><\/p>\n<p>It's no secret that simply deleting a table will look almost the same across all databases:<\/p>\n<p><\/p>\n<pre><code class=\"sql\">drop table hr.persons<\/code><\/pre>\n<p><\/p>\n<p>Creating a table is a bit more interesting. Virtually any DBMS (including many NoSQL databases) can handle \"create table\" in one form or another, and the core components won't differ much (name, list of columns, data types), but other details can vary significantly depending on the internal structure and capabilities of the specific DBMS. My favorite example is that the Oracle documentation only contains the \"bare\" BNF definitions for the syntax of \"create table\". <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.oracle.com\/en\/database\/oracle\/oracle-database\/19\/sqlrf\/sql-language-reference.pdf\">takes up 31 pages<\/a><\/noindex>. Other DBMS have more modest capabilities, but each also possesses many interesting and unique features for creating tables (<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>). It's unlikely that any graphical \"wizard\" from yet another IDE (especially a general-purpose one) can fully cover all these capabilities, and if it can, it would be a sight not for the faint-hearted. At the same time, a correctly and timely written command <strong><em>create table<\/em><\/strong> will allow you to easily take advantage of all of them, making storage and access to your data reliable, optimal, and as comfortable as possible.<\/p>\n<p><\/p>\n<p>Many DBMSs also have their own specific types of objects that are absent in others. Moreover, we can perform operations not only on database objects but also on the DBMS itself, such as \"killing\" a process, freeing a portion of memory, enabling tracing, switching to \"read only\" mode, and much more.<\/p>\n<p><\/p>\n<h5 id=\"a-teper-nemnogo-porisuem\">And now let's do some drawing<\/h5>\n<p><\/p>\n<p>One of the most common tasks is to create a diagram with database objects, to visually see the objects and the relationships between them. This can be done by virtually any graphical IDE, individual command line utilities, specialized graphical tools, and modelers. They will generate a diagram \"as they can\", and you can only slightly influence this process with a few parameters in the configuration file or checkboxes in the interface.<\/p>\n<p><\/p>\n<p>But this problem can be solved much more simply, flexibly, and elegantly, of course, with the help of code. For constructing diagrams of any complexity, we have several specialized markup languages (DOT, GraphML, etc.), and a plethora of applications (GraphViz, PlantUML, Mermaid) that can read such instructions and visualize them in various formats. Well, we already know how to obtain information about objects and their relationships.<\/p>\n<p><\/p>\n<p>Let's give a small example of what this could look like, using PlantUML and <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/postgrespro\/blog\/316428\/\">a demonstration database for PostgreSQL<\/a><\/noindex> (on the left, an SQL query that generates the necessary instruction for PlantUML, and on the right, the result):<\/p>\n<p>\n<img decoding=\"async\" alt=\"&quot;Database as Code&quot; 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>If you try a bit harder, you can get something that looks very much like a real ER diagram based on <noindex><a rel=\"nofollow\" href=\"https:\/\/gist.github.com\/QuantumGhost\/0955a45383a0b6c0bc24f9654b3cb561\">the ER template for PlantUML<\/a><\/noindex> :<\/p>\n<p><\/p>\n<p>                        <b class=\"spoiler_title\">The SQL query is just a little more complex<\/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=\"&quot;Database as Code&quot; Experience\" src=\"\/wp-content\/uploads\/2020\/06\/bd45420a4fd1bdfcf20cbe74a0638c7b.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p>If you look closely, many visualization tools under the hood also use similar queries. However, these queries are usually deeply <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/pgmodeler\/pgmodeler\/blob\/9c615c0b0871df3cd649983ce61e802b0ff0137b\/schemas\/catalog\/table.sch\">\"hardcoded\" in the application's code and are difficult to understand.<\/a><\/noindex>, not to mention any modifications.<\/p>\n<p><\/p>\n<h5 id=\"metriki-i-monitoring\">Metrics and monitoring<\/h5>\n<p><\/p>\n<p>Let's move on to the traditionally complex topic of database performance monitoring. I recall a small true story told to me by 'one of my friends'. In a certain project, there existed a powerful DBA, and few developers knew him personally or had ever seen him in person (despite the fact that he supposedly worked somewhere in the neighboring building). At the 'X hour', when the production system of a large retailer began to 'feel unwell' again, he silently sent screenshots of graphs from Oracle's Enterprise Manager, carefully highlighting critical areas in red for 'clarity' (which, to put it mildly, did little to help). And based on this 'snapshot', the issues had to be diagnosed. Meanwhile, no one had access to the precious (in both senses of the word) Enterprise Manager, as the system was complex and expensive; they feared that the 'developers might accidentally break something'. Therefore, developers would empirically discover the location and cause of the slowdowns and release a patch. If a stern letter from the DBA did not come again in the near future, everyone would breathe a sigh of relief and return to their current tasks (until the next Letter).<\/p>\n<p><\/p>\n<p>However, the monitoring process can look much more enjoyable and friendly, and most importantly \u2014 accessible and transparent for everyone. At least its basic part should be an addition to the main monitoring systems (which are undoubtedly useful and in many cases irreplaceable). Any DBMS is freely and absolutely willing to share information about its current state and performance. In the same 'bloody' Oracle DB, you can obtain almost any information about performance from system views, ranging from processes and sessions to the state of the buffer cache (for example, <noindex><a rel=\"nofollow\" href=\"https:\/\/oracle-base.com\/dba\/scripts\">DBA Scripts<\/a><\/noindex>, section 'Monitoring'). Postgresql also has a plethora of system views for <noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/docs\/current\/static\/monitoring-stats.html\">monitoring database operations<\/a><\/noindex>, including those indispensable for every DBA\u2019s everyday life, such as <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>performance metrics. <noindex><a rel=\"nofollow\" href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.0\/en\/performance-schema-table-descriptions.html\">In MySQL, there's even a separate schema designated for this purpose, called<\/a><\/noindex>performance_schema. <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.mongodb.com\/manual\/tutorial\/manage-the-database-profiler\/\">And in Mongo, the built-in<\/a><\/noindex> profiler <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.mongodb.com\/manual\/tutorial\/manage-the-database-profiler\/#view-profiler-data\">aggregates performance data into a system collection called 'system.profile.'<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Thus, armed with any metrics collector (Telegraf, Metricbeat, Collectd) that can execute custom SQL queries, a repository for these metrics (InfluxDB, Elasticsearch, Timescaledb), and a visualizer (Grafana, Kibana), you can create a sufficiently lightweight and flexible monitoring system that will be closely integrated with other system-wide metrics (obtained, for example, from the application server, from the OS, etc.). This is similar to how it is done in pgwatch2, which uses the combination of InfluxDB + Grafana along with a set of queries to system views, to which you can also <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/cybertec-postgresql\/pgwatch2#adding-metrics\">add custom queries<\/a><\/noindex>.<\/p>\n<p><\/p>\n<h2 id=\"itogo\">Total<\/h2>\n<p><\/p>\n<p>And this is just a rough outline of what can be done with our database using standard SQL code. I'm sure there are many more applications; feel free to share in the comments. Next time, we will discuss how (and, most importantly, why) to automate all of this and integrate it into your CI\/CD pipeline.<\/p>\n<p>Source: <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.2 - 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\/en\/blog\/administrirovanie\/database-as-sode-experience\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\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\/en\/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 \"Database as Code\" Experience | ProHoster","description":"SQL, can it be any simpler?","canonical_url":"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/database-as-sode-experience","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"en_US","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\/en\/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\/en\/wp-json\/wp\/v2\/posts\/83649","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/comments?post=83649"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts\/83649\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/media\/83650"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/media?parent=83649"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/categories?post=83649"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/tags?post=83649"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}