2015-04-18 5 views
0

Привет мой стол выглядит следующим образом:Изменить значение столбца значения из внешней колонки

CREATE TABLE my_eav(
    id INTEGER PRIMARY KEY AUTOINCREMENT, 
    k TEXT NOT NULL, 
    v TEXT, 
    v_link INTEGER, 
    UNIQUE(k, v) ON CONFLICT REPLACE, 
    FOREIGN KEY(v_link) REFERENCES posts(id) ON UPDATE ??? 
); 

v_link это столбец ID другой таблицы, «сообщения». Когда он не равен нулю, столбец «v» должен принимать значение столбца «title» из «posts».

Когда обновленная ссылочная запись указана, я хотел бы изменить столбец «v» в этой таблице на столбец «title» таблицы posts. Это возможно?

Что-то вроде

... ON UPDATE SET v = posts.title 

, но это, конечно, не работает :(

Я сделал этот триггер:

CREATE TRIGGER my_trigger 
AFTER INSERT ON my_eav 
WHEN v_link IS NOT NULL 
BEGIN 
    UPDATE my_eav SET v = (SELECT title 
       FROM posts 
       WHERE id = v_link); 
END; 

Но я не знаю, если это правильно

+0

Это то, к чему присоединяются, есть ли причина, по которой вы не хотите использовать соединение? – benedikt

+0

Я знаю, но я не думаю, что объединить работы внутри create table statement –

+1

Нет, но это не главное. Если вы соединяете таблицы при запросе данных, вы можете получить 'my_eav.k' и' posts.title' вместе. – benedikt

ответ

3

Лучшим решением является использование соединения, как указано выше.

select * from my_eav 
left join posts on my_eav.v_link = posts.id 

Но, если вы хотите триггеры, вы их получите. ;)
Вы можете проверить весь код ниже на http://sqlfiddle.com/ с опцией 'SQLite (WebSQL)'
И вы должны выбрать терминатор запроса - труба [/]

CREATE TABLE posts(id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT)/ 

CREATE TABLE my_eav(
    id INTEGER PRIMARY KEY AUTOINCREMENT, 
    k TEXT NOT NULL, 
    v TEXT, 
    v_link INTEGER, 
    UNIQUE(k, v) ON CONFLICT REPLACE, 
    FOREIGN KEY(v_link) REFERENCES posts(id) 
)/ 

CREATE TRIGGER my_trigger_ins 
AFTER INSERT ON my_eav 
BEGIN 
    UPDATE my_eav SET v = (SELECT title FROM posts WHERE id = NEW.v_link) where id = NEW.id; 
END/ 

CREATE TRIGGER my_trigger_upd 
AFTER UPDATE ON my_eav 
WHEN (OLD.v_link <> NEW.v_link) or (OLD.v_link is not null and NEW.v_link is null) 
    or (NEW.v_link is not null and OLD.v_link is null) 
BEGIN 
    UPDATE my_eav SET v = (SELECT title FROM posts WHERE id = NEW.v_link) where id = OLD.id; 
END/ 

CREATE TRIGGER my_trigger_b_ins BEFORE INSERT ON my_eav 
WHEN NEW.v is not null and NEW.v <> (SELECT title FROM posts WHERE id = NEW.v_link) 
BEGIN 
    SELECT RAISE(ABORT,'V must be from posts.'); 
END/ 

CREATE TRIGGER my_trigger_b_upd BEFORE UPDATE ON my_eav 
WHEN NEW.v is not null and NEW.v <> (SELECT title FROM posts WHERE id = NEW.v_link) 
BEGIN 
    SELECT RAISE(ABORT,'V must be from posts.'); 
END/ 

CREATE TRIGGER update_posts_title UPDATE OF title ON posts 
BEGIN 
    UPDATE my_eav SET v = new.title WHERE v_link = old.id; 
END/ 

Вот несколько тестов. Давайте заполним таблицы некоторыми данными.

insert into posts (title) values('Romeo')/ 
insert into posts (title) values('Hamlet')/ 
insert into my_eav(k, v_link) values('test1', 1)/ 
insert into my_eav(k, v_link) values('test2', 2)/ 
insert into my_eav(k) values('test3')/ 

Проверить my_trigger_ins - OK

select * from my_eav 

id k  v  v_link 
1 test1 Romeo 1 
2 test2 Hamlet 2 
3 test3 (null) (null) 

Проверить update_posts_title - OK

update posts set title = 'Romeo1' where id = 1 

select * from my_ea 

id k  v  v_link 
1 test1 Romeo1 1 
2 test2 Hamlet 2 
3 test3 (null) (null) 

Проверить my_trigger_upd - OK

update my_eav set k = 'test11' where id = 1 

select * from my_ea 

id k  v  v_link 
1 test11 Romeo 1 
2 test2 Hamlet 2 
3 test3 (null) (null) 

Проверить my_trigger_b_ins - OK

insert into my_eav(k, v, v_link) values('test1rrrr', 'rrrrr', 2) 

Result: could not execute statement due to a constaint failure (V must be from posts.) - OK 

Проверить my_trigger_b_upd - OK

update my_eav set v = 'xxxx' where id = 1 

Result: could not execute statement due to a constaint failure (V must be from posts.) - OK 

Thats All. Используйте действительно left join posts.

3

Основываясь на том, что вы говорите, похоже, что это главная цель:

Держите колонку против в таблице my_eav синхронизированный с в заголовке в колонке сообщений таблицы.

Если это так, моя первая мысль была бы, что вы могли бы нормализовать базу данных, просто хранить название в posts только, и если вам нужен заголовок, просто получить его всякий раз, когда вы запрашиваете my_eav с объединением:

select * from my_eav inner join posts on my_eav.v_link = posts.id 
where my_ev.id = <my desired record> 

Однако это вполне возможно с помощью триггеров.

Вы не включили дизайн posts таблицы, но это звучит, как будто это что-то вроде этого:

CREATE TABLE posts(id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT); 

Так давайте вставить пару пробных записей там:

insert into posts (title) values('Romeo & Juliet'); 
insert into posts (title) values('Hamlet'); 

Что дает us a table like this:

-------------------- 
|id |title   | 
-------------------- 
| 1 |Romeo & Juliet | 
| 2 |Hamlet   | 
-------------------- 

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

CREATE TABLE my_eav(id INTEGER PRIMARY KEY AUTOINCREMENT, k TEXT NOT NULL, v TEXT, 
v_link INTEGER, FOREIGN KEY(v_link) REFERENCES posts(id)); 

Так что теперь пустой my_eav таблица выглядит следующим образом:

------------------- 
|id|k |v |v_link | 
------------------- 
------------------- 

Давайте проверим внешний ключ, чтобы убедиться, что она не позволяет записи в my_eav, если они не существуют в posts (с моей SQLITE например Я должен был использовать команду ргадта, чтобы заставить SQLite в честь внешнего ключа):

pragma foreign_keys=on; 
insert into my_eav(k, v_link) values('test', 3); 
Error: FOREIGN KEY constraint failed 

Так что это хорошо, мы не можем вставить запись в my_eav, если она не существует в posts.

Чтобы сохранить титул синхронизированного между двумя таблицами, мы будем нуждаться в два триггеров, а не только один: триггер на my_eav когда запись первой создана, а также триггер posts, когда заголовок изменяются там.

Вот триггер на my_eav, чтобы захватить титул, когда запись первого вставлено:

CREATE TRIGGER get_title AFTER INSERT ON my_eav 
BEGIN 
    UPDATE my_eav SET v = (select title from posts where posts.id = v_link); 
END; 

Если мы протестируем его, добавив запись:

insert into my_eav(k, v_link) values('test1', 1); 

Он захватывает титул, как это предполагалось чтобы:

------------------------------- 
|id|k |v    |v_link| 
------------------------------- 
|1 |test1|Romeo & Juliet|1  | 
------------------------------- 

Другая рекомендация:

insert into my_eav(k, v_link) values('test2', 1); 

таблица в настоящее время:

------------------------------- 
|id|k |v    |v_link| 
------------------------------- 
|1 |test1|Romeo & Juliet|1  | 
|2 |test2|Romeo & Juliet|1  | 
------------------------------- 

и еще один:

insert into my_eav(k, v_link) values('test3', 2); 

таблица в настоящее время:

------------------------------- 
|id|k |v    |v_link| 
------------------------------- 
|1 |test1|Romeo & Juliet|1  | 
|2 |test2|Romeo & Juliet|1  | 
|3 |test3|Hamlet  |2  | 
------------------------------- 

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

CREATE TRIGGER update_title AFTER UPDATE ON posts 
BEGIN 
    UPDATE my_eav SET v = (select title from posts where posts.id = v_link); 
END; 

Теперь давайте проверим его обновляя название в posts таблице:

update posts set title='Othello' where title='Romeo & Juliet'; 

Убедитесь, что обновление произошло:

select * from posts; 

Таблица теперь показывает:

-------------------- 
|id |title   | 
-------------------- 
| 1 |Othello  | 
| 2 |Hamlet   | 
-------------------- 

И если мы проверим my_eav,

select * from my_eav; 

Триггер на posts держали его в синхронизации:

------------------------------- 
|id|k |v    |v_link| 
------------------------------- 
|1 |test1|Othello  |1  | 
|2 |test2|Othello  |1  | 
|3 |test3|Hamlet  |2  | 
------------------------------- 

Надеюсь недеформированной Правильная проблема, и это помогает.

+0

Сохранение двух таблиц в синхронизации через триггеры может быть сложным. Есть как минимум еще два способа разбить синхронизацию. 1) 'UPDATE my_eav SET v_link = 2 WHERE id = 1' Мы изменили ссылку на другую запись, но' v' по-прежнему остается прежней. Будет ли ваш текущий триггер на 'my_eav' обрабатывать' UPDATE' правильно или работает только на 'INSERT'? 2) 'UPDATE my_eav SET v = 'Hobbit' WHERE id = 1' Мы изменили копию названия на то, что даже не существует в' posts'. –

+0

Да, я вижу вашу точку зрения, если вы действительно хотите, чтобы они были прочными в синхронизации, вам нужно рассмотреть все возможные вещи, которые могут произойти. Это похоже на попытку синхронизации автономных баз данных; вы попадаете в ситуации, когда пользователь должен быть тай-брейкером. Это хорошая иллюстрация, почему вы должны пытаться нормализовать. Когда вы начинаете использовать базы данных, вы задаетесь вопросом: «Что плохого в хранении в 2-х местах», пока вы не увидите из первых рук, когда данные не синхронизируются, и тогда это заставляет вас «о, вот почему». –

Смежные вопросы