Запуск сценариев sql с взаимозависимостями

#postgresql #materialized-views

Вопрос:

У меня есть проект, использующий базу данных postgres в бэкэнде, и я работаю над созданием файла Dockerfile, который может автоматически настроить локальный экземпляр базы данных для разработки. База данных включает в себя большое количество функций, и они исторически хранились в соответствующих контексту файлах sql, например users.sql, companies.sql. Это было нормально, так как всякий раз, когда вносятся изменения, я могу просто выполнить соответствующие файлы sql, которые REPLACE все просматривают, и удалить/воссоздать все функции.

Однако при попытке запустить эти сценарии на новом экземпляре postgres (после CREATE заполнения всех таблиц) они завершаются неудачей, поскольку большинство представлений/функций ссылаются на другие функции представлений, которые еще не определены.

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

Есть ли какой-либо способ добиться этого, кроме реструктуризации сценариев?

Комментарии:

1. Я считаю, что реструктуризация ваших сценариев и порядок, в котором вы их применяете, — это правильный (и единственный) путь. Возможно, вы можете проявить творческий подход, просматривая сценарии в поисках зависимостей для изменения порядка, но, вероятно, было бы быстрее просто выполнить вручную, так как это одноразовая вещь. Возможно, в качестве временного промежутка вы можете просто повторять сценарии снова и снова, пока не будут созданы все объекты, игнорируя ошибки, пока все ошибки не исчезнут… 🙁

2. Я подумал, что это, скорее всего, так, спасибо за вашу помощь!

3. База данных pg_dump -Cs > db.sql «рабочей» базы данных должна дать вам довольно хорошее представление о том, как реструктурировать инструкции create.

4. Это действительно обеспечивает более простое решение, но не позволяет нам хранить модульные sql-файлы так, как мы это делали для справки. Спасибо за идею.

Ответ №1:

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

 WITH RECURSIVE viewids AS (
   /* all views that don't depend on other views */
   SELECT t.oid, 1 as level
   FROM pg_class t
      JOIN pg_rewrite AS r ON r.ev_class = t.oid
   WHERE r.rulename = '_RETURN'
     AND t.relkind = 'v'
     AND t.relnamespace NOT IN ('pg_catalog'::regnamespace,
                                'information_schema'::regnamespace,
                                'pg_toast'::regnamespace)
     AND NOT EXISTS (
            /* depends on a view */
            SELECT 1
            FROM pg_depend AS d
               JOIN pg_class AS t2 ON d.refobjid = t2.oid
            WHERE d.objid = r.oid
              AND d.classid = 'pg_rewrite'::regclass
              AND d.refclassid = 'pg_class'::regclass
              AND d.deptype = 'n'
              AND d.refobjsubid <> 0
              AND t2.relkind = 'v'
         )
     AND NOT EXISTS (
            /* depends on an extension */
            SELECT 1
            FROM pg_depend
            WHERE objid = t.oid
              AND classid = 'pg_class'::regclass
              AND refclassid = 'pg_extension'::regclass
              AND deptype = 'e'
         )
UNION ALL
   /* all views that depend on these views */
   SELECT t.oid, viewids.level   1
   FROM pg_class AS t
      JOIN pg_rewrite AS r ON r.ev_class = t.oid
      JOIN pg_depend AS d ON d.objid = r.oid
      JOIN viewids ON viewids.oid = d.refobjid
   WHERE t.relkind = 'v'
     AND r.rulename = '_RETURN'
     AND d.classid = 'pg_rewrite'::regclass                            
     AND d.refclassid = 'pg_class'::regclass
     AND d.deptype = 'n'
     AND d.refobjsubid <> 0
)
/* order the views by level, eliminating duplicates */
SELECT format('CREATE VIEW %s AS%s',
              oid::regclass,
              pg_get_viewdef(oid::regclass))
FROM viewids
GROUP BY oid
ORDER BY max(level);
 

Комментарии:

1. Как можно включить материализованные представления в этот запрос? И почему материализованные представления обрабатываются по-разному? Например, phpPgAdmin перечисляет не материализованные представления, а представления. Спасибо

2. pgpPgAdmin может не знать о материализованных представлениях. Чтобы включить их, вы должны включить pg_class записи с relkind = 'm' .