Я разрабатываю CMS, который имеет некоторые функции контроля версий. Он основан на MySQL Db.
Идея состоит в том, чтобы показать посетителям публичного сайта «определенную ревизию» данных и пользователей backoffice предварительный просмотр «последней редакции». Публикация чего-то просто означает установить «определенную ревизию», равную последней (и, возможно, удаление данных старых версий).
Я прочитал некоторые вопросы и ответы о теме на SO, большинство из них считают, что держать «старые» и «новые» строки в одной таблице плохи. Но, поскольку мне нужно объединять таблицы, все они «версируются», разбивая старые и новые в разных таблицах, также не идеальны (как приложение должно знать, является ли «контент» из одной редакции старым или новым, и, следовательно, найденный в таблице «истории» или нет?).
Поэтому я решил использовать только одну таблицу для каждого типа контента.
Дизайн, который я использовал: каждая таблица содержит столбец «ревизия INT NOT NULL» (часть первичного ключа вместе с столбцом идентификатора).
Изменение чего-то означает вставку новой строки с измененными значениями, увеличенной ревизией, но с тем же идентификатором.
Вставка чего-то означает вставку новой строки с увеличенным идентификатором и увеличенной ревизией.
Удаление чего-то означает вставку пустой строки с тем же идентификатором, увеличенной версией и флагом «thumbstone», установленным в «true».
Пример: есть страницы и есть «виды» («вид не в смысле MVC, вид в конкретном приложении»). «Представления» версии. Одна страница имеет много просмотров. Это (часть) «Просмотры».
CREATE TABLE `_views` ( `_id` int(11) NOT NULL, `_rev` int(11) NOT NULL, `_ts` BIT(1) DEFAULT b'0', `page` int(11) NOT NULL, `order` int(11) NOT NULL, PRIMARY KEY (`_id`,`_rev`) )
Мне нужно выбрать все представления, которые содержит страница, вплоть до «определенной ревизии» в порядке, указанном «заказ».
Этот запрос работает:
SELECT * FROM ( SELECT * FROM `_views` WHERE `page` = :page AND `_rev` <= :revision ORDER BY `_rev` DESC ) AS `all` GROUP BY `_id` HAVING `_ts` = 0 ORDER BY `order`
подзапрос выбирает все виды страницы, которые были однажды опубликованы (какая версия меньше или равна «опубликованной» ревизии). Внешний запрос группирует их до последней версии, удаляет группы с надписью и заказывает их по конкретным критериям приложения.
Поскольку для CMS масштабируемость и производительность имеют решающее значение, нет ли лучшего, более элегантного способа, чем подзапросы?
… или я должен просто сосредоточиться на кешировании?
Использование подзапросов для определения текущей версии – не лучший подход; вы действительно не хотите туда идти.
Более простой способ – добавить флаг, который расскажет вам о самой последней версии:
`_rev` int(11) NOT NULL, `_current` BIT(1),
Это требует, чтобы ручное UPDATE устанавливало флаг _current
всякий раз, когда добавляется новая ревизия или _ts
флаг _ts
. Но, по крайней мере, это позволяет избежать выполнения подзапроса на каждом дисплее страницы.
В качестве альтернативы вы все равно можете разбить свои данные на _current
и _history
. Вместо этого вы просто создали представление для обоих случаев, если вам нужно снова присоединиться к наборам результатов:
CREATE VIEW pages_all AS SELECT * FROM pages_current UNION ALL SELECT * FROM pages_history
Также может быть возможно создать подтаблицу всех активных (без предварительного просмотра) ревизий, если вам нужно их часто группировать. Хотя это приведет к еще большему ручному микроуправлению, чем флаг _current, или просто просмотр таблицы _history.