Я создаю приложение, в котором пользователи могут выполнять задания, которые должны быть одобрены другим пользователем. Таким образом, если утверждающий изменяет (обновляет) какие-либо данные, связанные с этой записью, мне нужно сохранить эти изменения в таблице аудита, например https://github.com/spatie/laravel-activitylog.
Каков наилучший подход? Реализовать с помощью триггера или сравнить объекты? Есть ли у Android библиотека, которая может с ним работать?
Ниже представлена модель работы:
public class Operation {
private int id;
private String started_at;
private int company_id;
private int collaborator_id;
private int approver_id;
private int building_id;
private String cost_center;
private String ended_at;
private int shift_id;
private int service_id;
private String invoice_number;
private String transport_number;
private String cod;
private String lot;
private int vehicle_id;
private int carrier_id;
private String vehicle_plate;
private String cart_plate;
private int amount;
private int loose_cargo;
private int palletized_load;
private int gross_weight;
private String comments;
private int number_assistants;
private int number_operators;
private int number_tractors;
private int number_samples;
private int number_others;
}
Я пытался создать его, как показано ниже:
"CREATE TRIGGER " + OperationEntry.TRIGGER_NAME +
" AFTER UPDATE " +
" ON["+ OperationEntry.TABLE_NAME +"] " +
" FOR EACH ROW " +
" BEGIN " +
" DECLARE changes VARCHAR(8000); " +
" SET changes = '{'; " +
" IF OLD. " + OperationEntry.COLUMN_ID + " <> " + " NEW. " + OperationEntry.COLUMN_ID + " THEN " +
"SET changes = CONCAT(changes, );" +
" END IF; " +
" IF OLD. " + OperationEntry.COLUMN_COMPANY + " <> " + " NEW. " + OperationEntry.COLUMN_COMPANY + " THEN " +
"SET changes = CONCAT(changes, );" +
" END IF; " +
" IF OLD. " + OperationEntry.COLUMN_BUILDING + " <> " + " NEW. " + OperationEntry.COLUMN_BUILDING + " THEN " +
"SET changes = CONCAT(changes, );" +
" END IF; " +
" SET changes = CONCAT(changes, '}'); " +
" END; ");
Использование ПОСЛЕ ОБНОВЛЕНИЯ TRIGGER представляется подходящим и не требует дополнительного кода, кроме кода, необходимого для генерации TRIGGER.
Проблема в том, что существование TRIGGER неочевидно, но этот недостаток можно преодолеть с помощью соответствующих комментариев в коде.
ре: -
I was trying to create it as below:
"CREATE TRIGGER " + OperationEntry.TRIGGER_NAME + " AFTER UPDATE " + " ON["+ OperationEntry.TABLE_NAME +"] " + " FOR EACH ROW " + " BEGIN " + " DECLARE changes VARCHAR(8000); " + " SET changes = '{'; " + " IF OLD. " + OperationEntry.COLUMN_ID + " <> " + " NEW. " + OperationEntry.COLUMN_ID + " THEN " + "SET changes = CONCAT(changes, );" + " END IF; " + " IF OLD. " + OperationEntry.COLUMN_COMPANY + " <> " + " NEW. " + OperationEntry.COLUMN_COMPANY + " THEN " + "SET changes = CONCAT(changes, );" + " END IF; " + " IF OLD. " + OperationEntry.COLUMN_BUILDING + " <> " + " NEW. " + OperationEntry.COLUMN_BUILDING + " THEN " + "SET changes = CONCAT(changes, );" + " END IF; " + " SET changes = CONCAT(changes, '}'); " + " END; ");
Я не верю, что многое из вышеперечисленного сработает. В реализации SQL SQLITE нет ключевого слова DECLARE, а ключевое слово ЕСЛИ очень ограничено.
Конкатенация значений также довольно неудобна.
Я бы предложил рассмотреть следующее, основываясь на том, что вы, кажется, пытаетесь: -
-- Drop tables and triggers (allows rerunability for testing)
DROP TABLE IF EXISTS OperationEntry;
DROP TABLE IF EXISTS OperationEntryChanges;
DROP TRIGGER IF EXISTS OperationEntryTrigger;
-- Create the original/core table
CREATE TABLE IF NOT EXISTS OperationEntry (
id INTEGER PRIMARY KEY,
company TEXT, building TEXT
);
-- Create the logging table
CREATE TABLE IF NOT EXISTS OperationEntryChanges (
oldid INTEGER,
newid INTEGER,
oldcompany TEXT,
newcompany TEXT,
oldbuilding TEXT,
newbuilding TEXT,
timestamp DEFAULT CURRENT_TIMESTAMP, -- assumes that you want some indications of when the update was made
updatecounter DEFAULT 0 -- maybe over the top/not required may replace or compliment timestamp
);
-- Create the logging trigger
CREATE TRIGGER OperationEntryTrigger
AFTER UPDATE ON OperationEntry
BEGIN
INSERT INTO OperationEntryChanges
(oldid, newid, oldcompany, newcompany, oldbuilding, newbuilding, updatecounter)
VALUES
(old.id, new.id, old.company, new.company, old.building, new.building, (SELECT count(*) + 1 FROM OperationEntryChanges)
);
END
;
-- Add some testing data
INSERT INTO OperationEntry (company, building) VALUES
('company1','Building1'),('company1','Building2'),('company1','Building3'),('company1','Building4'),
('company2','Building1'),('company2','Building2'),('company2','Building3'),
('company3','Building1'),('company3','Building2'),('company3','Building3'),('company3','Building4'),('company3','Building5')
;
-- Show the data prior to any updates
SELECT * FROM OperationEntry;
SELECT * FROM OperationEntryChanges;
-- Apply some updates
UPDATE OperationEntry SET company = company || 'A' WHERE Building = 'Building2';
UPDATE OperationEntry SET building = replace(building,'Building','Bldg') WHERE building = 'Building5';
UPDATE OperationEntry SET building = building||'X', company = company||'X' WHERE company = 'company1' AND building = 'Building3';
-- Show the data post updates
SELECT * FROM OperationEntry;
SELECT * FROM OperationEntryChanges;
-- Show the changes made
SELECT
datetime(timestamp),
updatecounter,
CASE
WHEN oldcompany <> newcompany AND oldbuilding <> newbuilding THEN 'Company changed from '|| oldcompany || ' to ' || newcompany || ', also Building changed from ' || oldbuilding || ' to ' || newbuilding
WHEN oldcompany <> newcompany THEN 'Company changed from '|| oldcompany || ' to ' || newcompany
WHEN oldbuilding <> newbuilding THEN 'Building changed from ' || oldbuilding || ' to ' || newbuilding END AS changemade
FROM OperationEntryChanges
;
Ниже будут результаты: -
Основная таблица после загрузки данных: -
Таблица изменений/журнала после загрузки данных (т.е. пустая, так как нет обновлений)
Измененная основная таблица (изменения выделены)
Таблица журнала изменений (после всех 5 обновлений (изменено 6 значений))
Изменения, сделанные в более удобном для человека формате
@Tarcisiofl у вас есть доступ ко всем значениям из обновляемой строки до и после. Используя old.column_name
или new.column_name
(т. е. добавляя к соответствующему столбцу префикс Старый. или новый.), см. SQL в понимании SQLite — CREATE TRIGGER
Да, я знаю это! Проблема в том, что я пытаюсь сохранить все изменения данных в одном столбце, объединяя каждое изменение.
@Tarcisiofl, это должен быть новый вопрос, учитывая исходную ограниченную информацию. Однако см. обновленный ответ.
Я реализовал его и обнаружил, что SQLite не поддерживает объявление переменных. Я пытаюсь найти обходной путь к этому!