SQL, что может быть проще? Каждый из нас может написать простенький запрос — набираем select, перечисляем необходимые колонки, затем from, имя таблицы, немного условий в where и все — полезные данные у нас в кармане, причем (почти) независимо от того какая СУБД в это время находится под капотом (а может и не СУБД вовсе). В результате работу практически с любым источником данных (реляционным и не очень) можно рассматривать с точки зрения обычного кода (со всеми вытекающими — version control, code review, статический анализ, автотесты и вот это все). И это касается не только самих данных, схем и миграций, а вообще всей жизнедеятельности хранилища. В этой статье поговорим о повседневных задачах и проблемах работы с различными БД под прицелом "database as code".


И начнем прямо с ORM. Первые батлы вида "SQL vs ORM" были замечены еще в допетровской Руси.


Объектно-реляционный мапинг

Сторонники ORM традиционно ценят скорость и простоту разработки, независимость от СУБД и чистоту кода. Для многих из нас код работы с БД (а зачастую и сама БД)


обычно выглядит примерно так...
@Entity
@Table(name = "stock", catalog = "maindb", uniqueConstraints = {
        @UniqueConstraint(columnNames = "STOCK_NAME"),
        @UniqueConstraint(columnNames = "STOCK_CODE") })
public class Stock implements java.io.Serializable {

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "STOCK_ID", unique = true, nullable = false)
    public Integer getStockId() {
        return this.stockId;
    }
  ...

Модель обвешена умными аннотациями, а где-то за кулисами доблестный ORM генерирует и выполняет тонны какого-то SQL-кода. К слову сказать, разработчики всеми силами пытаются отгородиться от своей БД километрами абстракций, что говорит о некоторой "SQL ненависти".


По другую сторону баррикад приверженцы чистого "handmade"-SQL отмечают возможность выжимать все соки из своей СУБД без дополнительных прослоек и абстракций. В результате чего появляются "data-centric" проекты, где базой занимаются специально обученные люди (они же "базисты", они же "базовики", они же "базденьщики" и т.д.), а разработчикам только остается "дергать" готовые вьюхи и хранимые процедурки, не вдаваясь в подробности.


А что если взять лучшее из двух миров? Как это сделано в замечательном инструменте с жизнеутверждающим названием Yesql. Приведу пару строк из общей концепции в моем вольном переводе, а более подробно с ней можно познакомиться здесь.


Clojure это крутой язык для создания DSL'ей, но SQL уже сам по себе является крутым DSL, и нам не нужен еще один. S-выражения прекрасны, но здесь они не добавляют ничего нового. В итоге получаем скобки ради скобок. Не согласны? Тогда дождитесь того момента когда абстракция над БД даст течь, и вы начнете борьбу с функцией (raw-sql)

И что делать? Давайте оставим SQL обычным SQL'ем — один файл на один запрос:

-- name: users-by-country
select *
  from users
 where country_code = :country_code

… а затем прочитайте этот файл, превратив его в обычную Clojure функцию:

(defqueries "some/where/users_by_country.sql"
   {:connection db-spec})

;;; A function with the name `users-by-country` has been created.
;;; Let's use it:
(users-by-country {:country_code "GB"})
;=> ({:name "Kris" :country_code "GB" ...} ...)

Придерживаясь принципа "SQL отдельно, Clojure отдельно", вы получаете:
  • Никаких синтаксических сюрпризов. Ваша база данных (как и любая другая) не соответствует SQL стандарту на 100% — но для Yesql это не важно. Вы никогда не будете тратить время на охоту за функциями с синтаксисом эквивалентным SQL. Вам никогда не придется возвращаться к функции (raw-sql "some ('funky' :: SYNTAX)")).
  • Лучшая поддержка редактора. Ваш редактор уже имеет отличную поддержку SQL. Сохраняя SQL как SQL, вы можете просто использовать его.
  • Командная совместимость. Ваши DBA могут читать и писать SQL, который вы используете в своем Clojure проекте.
  • Более простая настройка производительности. Нужно построить план для проблемного запроса? Это не проблема, когда ваш запрос является обычным SQL.
  • Повторное использование запросов. Перетащите эти же SQL-файлы в другой проекты, потому что это просто старый добрый SQL — просто поделитесь им.

По моему идея очень крутая и при этом очень простая, благодаря чему проект обрел множество последователей на самых разных языках. А мы далее попробуем применить похожую философию отделения SQL-кода от всего остального далеко за пределами ORM.


IDE & DB-менеджеры

Начнем с простой повседневной задачи. Часто нам приходится искать какие-либо объекты в БД, например, найти таблицу в схеме и изучить ее структуру (какие используются колонки, ключи, индексы, констрейнты и прочее). И от любой графической IDE или маломальского DB-manager'а, в первую очередь, мы ждем именно этих способностей. Чтобы было быстро и не пришлось ждать по полчаса, пока нарисуется окошко с нужной информацией (особенно при медленном соединении с удаленной БД), и при этом чтобы полученная информация была свежей и актуальной, а не закешированное старье. Причем чем сложнее и крупнее БД и больше их количество, тем сложнее это сделать.


Но обычно я забрасываю мышь куда подальше и просто пишу код. Допустим, необходимо узнать, какие таблицы (и с какими свойствами) содержаться в схеме "HR". В большинстве СУБД нужного результата можно добиться вот таким нехитрым запросом из information_schema:


select table_name
     , ...
  from information_schema.tables
 where schema = 'HR'

От базы к базе содержимое таких таблиц-справочников варьируется в зависимости от способностей каждой СУБД. И, например, для MySQL из этого же справочника можно получить специфичные для этой СУБД параметры таблицы:


select table_name
     , storage_engine -- Используемый "движок" ("MyISAM", "InnoDB" etc)
     , row_format     -- Формат строки ("Fixed", "Dynamic" etc)
     , ...
  from information_schema.tables
 where schema = 'HR'

Oracle не умеет information_schema, зато у него есть Oracle metadata, и больших проблем не возникает:


select table_name
     , pct_free       -- Минимум свободного места в блоке данных (%)
     , pct_used       -- Минимум используемого места в блоке данных (%)
     , last_analyzed  -- Дата последнего сбора статистики
     , ...
  from all_tables
 where owner = 'HR'

Не исключение и ClickHouse:


select name
     , engine -- Используемый "движок" ("MergeTree", "Dictionary" etc)
     , ...
  from system.tables
 where database = 'HR'

Что-то похожее можно сделать и в Cassandra (где есть columnfamilies вместо tables и keyspace'ы вместо схем):


select columnfamily_name
     , compaction_strategy_class  -- Стратегия сборки мусора
     , gc_grace_seconds           -- Время жизни мусора
     , ...
  from system.schema_columnfamilies
 where keyspace_name = 'HR'

Для большинства остальных БД также можно придумать похожие запросы (даже в Mongo есть специальная системная коллекция, которая содержит в себе информацию о всех коллекциях в системе).


Само собой, таким способом можно получить информацию не только о таблицах, а вообще о любом объекте. Периодически добрые люди делятся таким кодом для разных БД, как, например, в серии хабра-статей "Функции для документирования баз данных PostgreSQL" (айб, бен, гим). Само собой, держать всю эту гору запросов в голове и постоянно набирать их — это "такое себе" удовольствие, поэтому в любимой IDE/редакторе у меня есть заранее заготовленный набор сниппетов для часто используемых запросов, и остается только впечатать имена объектов в шаблон.


В итоге такой способ навигации и поиска объектов гораздо более гибок, экономит много времени, позволяет получить именно ту информацию и в том виде, в котором сейчас необходимо (как, например, описано в посте "Экспорт данных из БД в любом формате: что умеют IDE на платформе IntelliJ").


Операции с объектами

После того как мы нашли и изучили нужные объекты, самое время с ними сделать что-нибудь полезное. Естественно, также не отрывая пальцев от клавиатуры.


Не секрет, что простое удаление таблицы будет выглядеть одинаково почти во всех БД:


drop table hr.persons

А вот с созданием таблицы уже поинтереснее. Практически любая СУБД (в том числе и многие NoSQL) в том или ином виде умеет "create table", и основная его часть даже мало будет отличаться (имя, список колонок, типы данных), но остальные детали могут разительно отличаться и зависят от внутреннего устройства и возможностей конкретной СУБД. Мой любимый пример — в документации Oracle только одни "голые" БНФ'ы для синтаксиса "create table" занимают 31 страницу. Другие СУБД обладают более скромными возможностями, но каждая из них также обладает множеством интересных и уникальных фич по созданию таблиц (postgres, mysql, cockroach, cassandra). Вряд ли какой-нибудь графический "wizard" из очередной IDE (особенно универсальной) сможет полностью покрыть все эти способности, а если и сможет, то это будет зрелище не для слабонервных. В то же время правильно и вовремя написанный оператор create table позволит без труда воспользоваться всеми из них, сделать хранение и доступ к вашим данным надежным, оптимальным и максимально комфортным.


Также во многих СУБД есть свои специфичные типы объектов, которые отсутствуют в других СУБД. Причем мы можем выполнять операции не только над объектами БД, но и над самой СУБД, например "убить" процесс, освободить какую-либо область памяти, включить трассировку, перейти в режим "read only" и многое другое.


А теперь немного порисуем

Одна из самых распространенных задач — построить диаграмму с объектами БД, на красивой картинке увидеть объекты и связи между ними. Это умеет практически любая графическая IDE, отдельные «command line»-утилиты, специализированные графические тулы и моделлеры. Которые вам что-то нарисуют "как умеют", а немного повлиять на этот процесс можно только с помощью нескольких параметров в конфигурационном файле или галочек в интерфейсе.


Но эту проблему можно решить гораздо проще, гибче и элегантнее, и конечно же с помощью кода. Для построения диаграмм любой сложности у нас есть сразу несколько специализированных языков разметки (DOT, GraphML etc), а к ним — целая россыпь приложений (GraphViz, PlantUML, Mermaid), которые умеют читать такие инструкции и визуализировать в самых разных форматах. Ну а информацию об объектах и связях между ними мы уже знаем как получить.


Приведем небольшой пример того, как это могло бы выглядеть, с использованием PlantUML и демонстрационной база данных для PostgreSQL (слева SQL-запрос, который сгенерирует нужную инструкцию для PlantUML, а справа результат):



select '@startuml'||chr(10)||'hide methods'||chr(10)||'hide stereotypes' union all
select distinct ccu.table_name || ' --|> ' ||
       tc.table_name as val
  from table_constraints as tc
  join key_column_usage as kcu
    on tc.constraint_name = kcu.constraint_name
  join constraint_column_usage as ccu
    on ccu.constraint_name = tc.constraint_name
 where tc.constraint_type = 'FOREIGN KEY'
   and tc.table_name ~ '.*' union all
select '@enduml'

А если немного постараться, то на основе ER-шаблона для PlantUML можно получить что-то сильно похожее на настоящую ER-диаграмму:


SQL-запрос чуууть посложнее
-- Шапка
select '@startuml
        !define Table(name,desc) class name as "desc" << (T,#FFAAAA) >>
        !define primary_key(x) <b>x</b>
        !define unique(x) <color:green>x</color>
        !define not_null(x) <u>x</u>
        hide methods
        hide stereotypes'
 union all
-- Таблицы
select format('Table(%s, "%s \n information about %s") {'||chr(10), table_name, table_name, table_name) ||
       (select string_agg(column_name || ' ' || upper(udt_name), chr(10))
          from information_schema.columns
         where table_schema = 'public'
           and table_name = t.table_name) || chr(10) || '}'
  from information_schema.tables t
 where table_schema = 'public'
 union all
-- Связи между таблицами
select distinct ccu.table_name || ' "1" --> "0..N" ' || tc.table_name || format(' : "A %s may have\n many %s"', ccu.table_name, tc.table_name)
  from information_schema.table_constraints as tc
  join information_schema.key_column_usage as kcu on tc.constraint_name = kcu.constraint_name
  join information_schema.constraint_column_usage as ccu on ccu.constraint_name = tc.constraint_name
 where tc.constraint_type = 'FOREIGN KEY'
   and ccu.constraint_schema = 'public'
   and tc.table_name ~ '.*'
 union all
-- Подвал
select '@enduml'


Если внимательно присмотреться, то под капотом многие инструменты-визуализаторы точно также используют похожие запросы. Правда, запросы эти обычно глубоко "зашиты" в код самого приложения и сложны для понимания, не говоря уже о какой-либо их модификации.


Метрики и мониторинг

Перейдем к традиционно сложной теме — мониторинг производительности БД. Вспомню небольшую true story, рассказанную мне "одним моим другом". На очередном проекте жил-был некий могущественный DBA, и мало кто из разработчиков был с ним знаком лично, да и вообще видел когда-нибудь его в глаза (несмотря на то, что трудился он, по слухам, где-то в соседнем корпусе). В час "X", когда poduction-система крупного ретейлера начинала в очередной раз "плохо себя чувствовать", он молча присылал скриншоты графиков из ораклового Enterprise Manager, на которых бережно выделял критичные места красным маркером для "понятности" (это, мягко говоря, мало помогало). И вот по этой "фотокарточке" приходилось лечить. При этом доступа к драгоценному (в обоих смыслах этого слова) Enterprise Manager ни у кого не было, т.к. система сложная и дорогая, вдруг "разрабы чего-нить натыкают и все поломают". Поэтому разработчики "эмпирическим" путем находили место и причину тормозов и выпускали патч. Если грозное письмо от DBA не приходило повторно в ближайшее время, то все с облегчением выдыхали и возвращались к своим текущим задачам (до нового Письма).


Но процесс мониторинга может выглядеть более весело и дружелюбно, а самое главное — доступно и прозрачно для всех. Хотя бы базовая его часть, как дополнение к основным системам мониторинга (которые безусловно полезны и во многих случаях незаменимы). Любая СУБД свободно и абсолютно безвозмездно готова поделиться информацией о своем текущем состоянии и производительности. В той же самой "кровавой" Oracle DB практически любую информацию о производительности можно получить из системных представлений, начиная от процессов и сессий и заканчивая состоянием буферного кеша (например, DBA Scripts, раздел "Monitoring"). В Postgresql также есть целая россыпь системных представлений для мониторинга работы БД, в частности такие незаменимые в повседневной жизни любого DBA, как pg_stat_activity, pg_stat_database, pg_stat_bgwriter. В MySQL для этого предназначена даже отдельная схема performance_schema. А В Mongo встроенный профайлер агрегирует данные о производительности в системную коллекцию system.profile.


Т.о., вооружившись каким-либо сборщиком метрик (Telegraf, Metricbeat, Collectd), который умеет выполнять кастомные sql-запросы, хранилищем этих метрик (InfluxDB, Elasticsearch, Timescaledb) и визуализатором (Grafana, Kibana), можно получить достаточно легкую и гибкую систему мониторинга, которая будет тесно интегрирована с другими общесистемными метриками (получаемыми, например, от сервера приложений, от ОС и пр.). Как, например, это сделано в pgwatch2, где используется связка InfluxDB + Grafana и набор запросов к системным представлениям, к которым также можно добавить кастомные запросы.


Итого


И это только приблизительный перечень того, что можно сделать с нашей БД посредством обычного SQL-кода. Уверен, можно найти еще множество применений, пишите в комментариях. А о том, как (и самое главное зачем) это все заавтоматизировать и включить в свой CI/CD pipeline мы поговорим в следующий раз.

Комментарии (15)


  1. anonymous
    01.06.2020 06:32


    1. mgramin Автор
      01.06.2020 06:32

      9 лет для ИТ отрасли этот как другая эпоха. В литературе этот прием называется гиперболой.
      С какой целью интересуетесь моим образованием?


      1. michael_v89
        01.06.2020 06:32

        Ну я вот тоже обратил на это внимание. 11-й год как-то не вяжется с таким названием, это же меньше 10 лет назад. Да и ORM сильно раньше появились. Ладно бы еще начало 2000-х. И вообще, я в 10-м универ закончил, а тут оказывается 11-й это "допетровская Русь")


  1. knagaev
    01.06.2020 06:32

    под прицелом «database as code»

    Переведите, пожалуйста, что понимаете под «database as code» — из статьи это не получается определить.
    А от этого и весь смысл статьи как-то утекает.
    Вы хотели показать как можно работать с различными СУБД при помощи запросов SQL?
    При этом упомянули ORM, но главную проблему ORM (попытка объединить два мира: множеств и скаляров), которую по-человечески не решили и вряд ли решат когда-нибудь, не затронули.
    Непонятно что хотели сказать.


    1. knagaev
      01.06.2020 06:32

      Ок, ответа наверно не будет.
      Я тогда просто ссылку положу здесь.
      Читали такое?
      Вьетнам компьютерной науки
      Если нет, то очень рекомендую.


      1. mgramin Автор
        01.06.2020 06:32

        Спасибо, почитаю. У меня еще в wait-листе «Дефрагментация мозга», там вроде тоже что-то похожее.
        На самом деле статья (и сама идея) не про маппинг, а про отношение, отношение к SQL-коду как к Коду.


    1. mgramin Автор
      01.06.2020 06:32

      Переведите, пожалуйста, что понимаете под «database as code» — из статьи это не получается определить.

      Это мое небольшое хобби. Идея простая — если есть «infrastructure as code», «configuration as code» и пр., то где «database as code»? Причем в случае с БД даже не надо выдумывать никаких yml-ов, ведь уже есть настоящий код — это SQL.
      Если интересно, то у меня есть еще одна статья на Хабре на эту тему, и небольшой доклад.
      Готов ответить на любые вопросы (если они будут).


      1. michael_v89
        01.06.2020 06:32

        Так вы на вопрос не ответили. Что вы хотели сказать-то? Ну вот допустим программист или сисадмин проникся концепцией "database as code", что конкретно должно измениться в его работе, что он должен начать делать по-другому?


        1. michael_v89
          01.06.2020 06:32

          Раз автор не отвечает, прокомментирую тезисы из статьи.


          Модель обвешена умными аннотациями, а где-то за кулисами доблестный ORM генерирует и выполняет тонны какого-то SQL-кода.

          А что вы предлагаете, писать SQL вручную? Нет, спасибо, это сложно в поддержке.


          А что если взять лучшее из двух миров? Как это сделано в замечательном инструменте с жизнеутверждающим названием Yesql.
          а затем прочитайте этот файл, превратив его в обычную Clojure функцию

          В PHP можно строки с SQL задавать, без всяких файлов. Это пишет каждый начинающий PHP-шник, который не умеет пользоваться фреймворками с ORM. Или опытный, который точно знает, что делает.


          Часто нам приходится искать какие-либо объекты в БД, например, найти таблицу в схеме и изучить ее структуру (какие используются колонки, ключи, индексы, констрейнты и прочее). И от любой графической IDE или маломальского DB-manager'а, в первую очередь, мы ждем именно этих способностей.
          В большинстве СУБД нужного результата можно добиться вот таким нехитрым запросом из information_schema

          Так IDE именно такой код и выполняют. И ORM тоже.
          От IDE и DB-менеджеров мы ждем наглядного представления и удобного редактирования, а не просто текстовые названия чего-то. И да, их используют чтобы такой запрос не писать самому.
          Про information_schema знают практически все, в том числе и начинающие, потому что она как раз выводится в любом DB-менеджере.


          В итоге такой способ навигации и поиска объектов гораздо более гибок, экономит много времени, позволяет получить именно ту информацию и в том виде, в котором сейчас необходимо

          Такой способ занимает много времени, потому что это надо руками писать, даже со сниппетами. Ну и как бы нет, не в том виде, в котором необходимо. Структура БД обычно нужна в наглядном графическом виде, со связями и прочим, с выпадающими списками для редактирования, а не в текстовом результате запроса.
          Ну и да, то, что к базе можно сделать запрос, знают практически все.


          В то же время правильно и вовремя написанный оператор create table позволит без труда воспользоваться всеми из них

          Ввод вручную в правильном синтаксисе это тоже труд. Именно его и избегают использованием графических клиентов. Все знают, что в базе есть CREATE TABLE, просто не все хотят писать его вручную.


          Причем мы можем выполнять операции не только над объектами БД, но и над самой СУБД, например "убить" процесс, освободить какую-либо область памяти, включить трассировку

          Я что-то сомневаюсь, что SHOW PROCESSLIST или аналог это команда, неизвестная для большинства разработчиков. А из знания о ней можно предположить и существование других функций.


          Ну а информацию об объектах и связях между ними мы уже знаем как получить.
          Приведем небольшой пример того, как это могло бы выглядеть, с использованием PlantUML и демонстрационной база данных для PostgreSQL (слева SQL-запрос, который сгенерирует нужную инструкцию для PlantUML, а справа результат)

          Да, только это дольше.
          Вместо того, чтобы воспользоваться IDE, надо изучить синтаксис PlantUML, составить запрос, и воспользоваться PlantUML. Какая в этом цель?


          А если немного постараться, то на основе ER-шаблона для PlantUML можно получить что-то сильно похожее на настоящую ER-диаграмму

          А если использовать DB-manager, то даже и стараться не надо. MySQL Workbench это точно умеет, MS SQL тоже, для остальных СУБД наверно тоже кто-то аналогичные инструменты сделал.


          Т.о., вооружившись каким-либо сборщиком метрик (Telegraf, Metricbeat, Collectd), который умеет выполнять кастомные sql-запросы, хранилищем этих метрик (InfluxDB, Elasticsearch, Timescaledb) и визуализатором (Grafana, Kibana), можно получить достаточно легкую и гибкую систему мониторинга. Как, например, это сделано в pgwatch2

          Что все и делают. Либо сами, либо используют инструменты типа pgwatch2 или Enterprise Manager. А если сами не делают, значит не понимают, что эта информация означает, и визуализатор это не исправит.


          Итого, все что описано в статье, давно всем известно и везде применяется.


          1. mgramin Автор
            01.06.2020 06:32

            Итого, все что описано в статье, давно всем известно

            Данная статья задумывалась как обновленная и более развернутая расшифровка одной из частей моего доклада с одноименным названием. Если здесь все понятно, то это хорошо.

            и везде применяется

            Идея не только в том, что везде используется SQL-код, а что относиться к нему стоит как к настоящему коду.
            А что мы делаем с кодом — храним его в git'е, настраиваем всякие пайплайны чтобы им управлять, делаем ревью и вот это все. При чем это относится и к коду приложений и инфраструктурному коду.
            В database as code тоже идея состоит в том, что весь SQL-код проходит через репозиторий (не только миграции). Написали запрос, закомитили, сделали merge request и он пошел по пайплайну.
            А именно:
            — запустились какие-то линтеры
            — сделали ревью
            — автотесты (почему бы и нет — github.com/dimitri/regresql)
            — обновились доки и диаграммы в них
            — в IDE/Manager добавилась новая закладка, где смотреть его результаты в более удобном виде, делать выгрузки всякие
            — при необходимости начали собираться метрики на базе результатов запроса
            — появился новый дашборд в Графане/Кибане на основании этих метрик
            — и пр.


            1. michael_v89
              01.06.2020 06:32

              А что мы делаем с кодом — храним его в git'е, настраиваем всякие пайплайны чтобы им управлять, делаем ревью
              В database as code тоже идея состоит в том, что весь SQL-код проходит через репозиторий

              Так собственно об этом и был изначальный вопрос — какой конкретно код вы предлагаете программисту хранить в git-е? Тот, который ORM генерирует? Это никому не нужно. Его именно потому и сделали генерируемым, а не захардкоженным, потому что это удобно.
              Это не говоря уже про динамический SQL, где число джойнов и условий в WHERE зависит от фильтров, заданных в интерфейсе. Предлагаете в репозитории хранить все возможные варианты?


              запустились какие-то линтеры

              Линтеры нужны, чтобы найти ошибку до выполнения кода, причем в основном ошибку в типах. В SQL довольно сложно сделать ошибку в типах, а уж коммитить SQL, который ни разу не запускался, это какая-то крайне исключительная ситуация.


              сделали ревью

              Ревью не надо делать, если генератор запросов проверен и протестирован. Ревью логики построения запроса лучше проверять в виде кода на языке программирования. Кстати про ревью, сырой SQL сложен в поддержке, вы почему-то это упорно игнорируете.


              автотесты

              Автотесты прекрасно работают с кодом, который этот SQL генерирует, для этого не надо хранить его в репозитории.


              при необходимости начали собираться метрики на базе результатов запроса

              Метрики нужны во время выполнения запроса, а не во время появления его в репозитории. И обычно нужны не все подряд, а вполне определенные, что задается кодом.


              в IDE/Manager добавилась новая закладка, где смотреть его результаты в более удобном виде, делать выгрузки всякие

              Да, получится несколько сотен закладок, и быстрее руками скопировать в редактор, чем искать среди них. Тем более что 99% запросов используют параметры — конкретные id или строки, и все равно без редактирования его не запустить.


              обновились доки и диаграммы в них

              Я не очень понимаю, что вы имеете в виду, и почему нельзя сделать то же самое для любого другого кода.


  1. AZverg
    01.06.2020 06:32

    9 лет для IT срок конечно не малый, но, в обсуждаемой области ORM каких то прорывных тенденций я не вижу.

    В литературе этот прием называется гиперболой.
    Гипербола это преувеличение, а не использование несоответствующих фактов. У допетровской эпохи есть конкретные даты, и именно поэтому у Вас поинтересовались достаточностью образования.
    С литературной же точки зрения можно было использовать формулировки «предыдущей эпохи», «доГейсовой эпохи» (или иное значимое в обсуждаемой категории имя).
    После фразы «допетровской» по ссылке ожидался некий исторический каламбур восходящий к 17 веку. Но увы.


    1. mgramin Автор
      01.06.2020 06:32

      9 лет для IT срок конечно не малый, но, в обсуждаемой области ORM каких то прорывных тенденций я не вижу.

      Да, и это одна из главных идей поста (по крайней мере я так планировал).

      Гипербола это преувеличение, а не использование несоответствующих фактов.

      Т.е. фразу «мы не виделись сто лет» могут употреблять только долгожители? Я уже молчу про «со скоростью пули», «задушить в объятьях» и пр.

      После фразы «допетровской» по ссылке ожидался некий исторический каламбур восходящий к 17 веку. Но увы.

      Приношу «тысячу извинений», что не оправдал Ваших надежд.

      Вообще не думал, что такой безобидный речевой оборот вызовет столько полемики.


      1. michael_v89
        01.06.2020 06:32

        Вообще не думал, что такой безобидный речевой оборот вызовет столько полемики.

        Ну возможно статья такая, что больше нечего обсуждать)


  1. gluck59
    01.06.2020 06:32

    Вообще хорошо что появляются такие статьи. Через год об авторах уже никто не вспомнит, а зерно сомнения уже посажено и ростки здравого смысла уже всходят.
    Ой, опять литературный прием, да еще и заезженный…