В предыдущем посте я поделился мыслями насчет преимуществ сырого SQL перед ORM. Забыл привести два следующих аргумента.

Миграции

Напомню, наши миграции – это SQL файлы. Специальный скрипт прогоняет их в особом порядке, оборачивая в транзакции. Преимущество в том, что я могу прогнать их самостоятельно через psql, если вдруг возникнет такая возможность.

Пример типичной миграции:

-- change column types
alter table users
    alter column username type text,
    alter column username set not null;

Здесь я просто меняю тип поля на text с varchar и добавляю ограничение на обязательное заполнение. Чтобы проверить миграцию, просто запускаю ее вручную:

psql --user foo foo-dbm < resources/migrations/20161107112942-change-username.up.sql

Если у вас база в Докере, то выигрыш в том, что при старте образа запускается специальный скрипт. Он автоматом прогоняет .sql-файлы из особой папки. Достаточно накидать в эту папку файлы миграций, и они выполнятся! Вот как это делается в compose-файле:

version: '2'
services:
  ...
  postgres:
    image: postgres
    volumes:
      - <папка с миграциями>:/docker-entrypoint-initdb.d:ro

Сравните теперь с типичной Django-миграцией:

from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [("migrations", "0001_initial")]

    operations = [
        migrations.DeleteModel("Tribble"),
        migrations.AddField("Author", "rating", models.IntegerField(default=0)),
    ]

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

Далее, этот код может быть запущен только на Питоне и только в особом окружении. Нужна Джанга, вирутальное окружение, верные переменные среды. Просто запустить его я не могу.

По Ричу Хики, SQL-миграция – просто, Джанго-миграция – легко. Я за простоту.

Рекурсивные запросы

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

При этом самые частые операции в коде были перемещение от элемента на самый вверх или на самый низ. Что сделал разработчик? Понятно, лезть в базу поштучно очень дорого, в цепочке может быть 20 узлов. Решил проблему радикально – вынимает из базы ВСЕ узлы, потом стоит иерархический словарь в памяти и перемещается по нему. Это работало, потому что в базе было мало узлов, около тысячи. Ради забавы я написал юнит-тест с 10000 узлами, код подвис секунд на пять.

А ведь достаточно было использовать рекурсивные запросы! Они не только в Постгресе есть, но даже в SQLite! Запрос разбивается на две подчасти. Первая – инициализирующая часть, вторая – накопительная. Пример с Хабра:

WITH RECURSIVE r AS (
   SELECT id, parent_id, name, 1 AS level
   FROM geo
   WHERE id = 4

   UNION ALL

   SELECT geo.id, geo.parent_id, geo.name, r.level + 1 AS level
   FROM geo
      JOIN r
          ON geo.parent_id = r.id
)

SELECT * FROM r;

 id | parent_id |      name       | level
----+-----------+-----------------+-------
  4 |         2 | Европа          |     1
  5 |         4 | Россия          |     2
  6 |         4 | Германия        |     2
  7 |         5 | Москва          |     3
  8 |         5 | Санкт-Петербург |     3
  9 |         6 | Берлин          |     3
(6 rows)

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

Любите SQL – прекрасный, понятный язык запросов к данным!