sqlite diff до и после транзакции - PullRequest
1 голос
/ 17 октября 2019

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

  1. Скопируйте весь файл sqlite в папку tmp (old-database.sqlite)
  2. Выполните транзакцию (например, INSERT, SCHEMA CHANGE, ...)
  3. Чем sqldiff --primary-ключ old-database.sqlite database.sqlite

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

Существует ли какой-либо механизм, который я могу использовать для получения того же результата без копирования базы данных. Может быть, что-то с журналом?

С уважением, Даан

Ответы [ 2 ]

1 голос
/ 18 октября 2019

Возможно, это немного неудобно для реализации и, возможно, на каком-то этапе (с точки зрения размера) неэффективно, но вы можете использовать комбинацию TRIGGERS (4 на каждую контролируемую таблицу - это неудобная часть) и снимок до после sqlite_master .

То есть, для каждой контролируемой таблицы можно иметь копию этой таблицы (по структуре) с двумя столбцами. 1 для обозначения действия (вставлено, перед обновлением, после обновления или удаления), 2-е (необязательно) для записи метки времени.

Затем для каждой отслеживаемой таблицы есть AFTER INSERT, BEFORE DELETE, AFTER и BEFORE. UPDATE TRIGGERS для копирования записи add в таблицу журнала таблицы, которая будет очищена до транзакции (если требуется, если нет, то временная метка, вероятно, будет обязательным столбцом).

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

Пример, рассмотрим следующий пример / demo: -

CREATE TABLE IF NOT EXISTS mytable (id INTEGER PRIMARY KEY, mycolumn TEXT);
CREATE TABLE IF NOT EXISTS mytablelog AS SELECT * FROM mytable WHERE 0 = 1;
ALTER TABLE mytablelog ADD COLUMN logtype INTEGER;
ALTER TABLE mytablelog ADD COLUMN timestamp INTEGER TEXT;

CREATE TABLE IF NOT EXISTS schema_log AS SELECT * FROM sqlite_master WHERE 0=1;
CREATE TABLE IF NOT EXISTS pretrans_schema AS SELECT * FROM sqlite_master WHERE 0=1;

ALTER TABLE schema_log ADD COLUMN logtype INTEGER;
ALTER TABLE schema_log ADD COLUMN timestamp INTEGER TEXT;
CREATE TRIGGER IF NOT EXISTS mytable_inserts AFTER INSERT ON mytable 
    BEGIN 
        INSERT INTO mytablelog SELECT *,0,strftime('%s','now') FROM mytable WHERE id = new.id;
    END
;
CREATE TRIGGER IF NOT EXISTS mytable_deletions BEFORE DELETE ON mytable
    BEGIN
        INSERT INTO mytablelog SELECT *,1,strftime('%s','now') FROM mytable WHERE id =  old.id; 
    END
;
CREATE TRIGGER IF NOT EXISTS mytable_preupdates BEFORE UPDATE ON mytable
    BEGIN
        INSERT INTO mytablelog SELECT *,2,strftime('%s','now') FROM mytable WHERE id = old.id;
    END
;
CREATE TRIGGER IF NOT EXISTS mytable_postupdates AFTER UPDATE ON mytable
    BEGIN 
        INSERT INTO mytablelog SELECT *,3,strftime('%s','now') FROM mytable WHERE id = new.id;
    END
;
-- SELECT * FROM sqlite_master WHERE name LIKE 'sqlite_%';


/* BEFORE TRANSACTION PREPATION */
DELETE FROM mytablelog;
DELETE FROM schema_log;
DELETE FROM pretrans_schema;
INSERT INTO pretrans_schema SELECT * FROM sqlite_master;

/* DO SOMETHING AKA THE TRANSACTIONS */
CREATE TABLE IF NOT EXISTS newtable (id INTEGER PRIMARY KEY, acolumn TEXT);
INSERT INTO mytable (mycolumn) VALUES ('Mary')
 -- ,('Fred'),('Jane'),('Anne'),('Alfred'),('George'),('Alan')
 ,('Susan'),('Betty'),('Catherine'),('John')
 ,(100),(200)
;
UPDATE mytable SET mycolumn = mycolumn||' has the detected letter.' WHERE mycolumn LIKE '%n%';
DELETE FROM mytable WHERE CAST(mycolumn AS INTEGER) > 0;


/* AFTER TRANSACTION */
SELECT rowid,'sm',* FROM sqlite_master UNION ALL SELECT rowid,'pt',* FROM pretrans_schema ORDER BY type,name; /* FOR DEMO/TESTING */ 
/* Get items added to the schema */
INSERT INTO schema_log SELECT *,4,strftime('%s','now') FROM sqlite_master WHERE name NOT IN (SELECT name FROM pretrans_schema);
/* Get items deleted from the schema */
INSERT INTO schema_log SELECT *,5,strftime('%s','now') FROM pretrans_schema WHERE name NOT IN (SELECT name FROM sqlite_master);
/* get original schema if schema updates */
INSERT INTO schema_log SELECT *,6,strftime('%s','now') FROM sqlite_master AS s 
    WHERE (sql <> (SELECT sql FROM pretrans_schema WHERE type = s.type AND name = s.name )) AND type IS NOT NULL /* AND NAME <> 'pretrans_schema' optional */
;
/* get current schema if schema updates */
INSERT INTO schema_log SELECT *,7,strftime('%s','now') FROM pretrans_schema AS s 
    WHERE (sql <> (SELECT sql FROM sqlite_master WHERE type = s.type AND name = s.name)) AND type IS NOT NULL /* AND NAME <> 'pretrans_schema' */
;
SELECT * FROM schema_log;
SELECT * FROM mytablelog;

Результаты

1 схема до и после (часть)

enter image description here

  • Обратите внимание навыделенная строка, нет записи pt , показывающей, что таблица была добавлена ​​(нет записи sm, тогда она была бы удалена).

2 - журнал изменений схемы ( schema_log таблица)

enter image description here

  • 4 указывает на новый элемент в схеме (5 удалено, 6 до обновления, 7 после обновления)
  • Таким образом, таблица newtable была добавлена ​​как часть транзакции.

3 - транзакции mytable;

enter image description here

  • O - это вставки, 1 удаляются 2 и 3 (парные) до и после обновления соответственно.
0 голосов
/ 12 ноября 2019

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

После долгих поисков я обнаружил, что решение запекается в SQLite, но его нужно включить при компиляции. время. Он называется «Расширение сеанса» и это именно то, что мне нужно:

https://www.sqlite.org/sessionintro.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...