将列的值更改为外部列中的值

时间:2015-04-18 14:10:30

标签: sql sqlite foreign-keys

你好我的表看起来像这样:

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列," posts"。当它不为空时," v"列应该采用"标题"的值来自"帖子"。

的专栏

当引用的帖子记录更新时,我想更改" v"此表格中的列标题为"标题"帖子表的列。这可能吗?

这样的东西
... 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;

但我不知道它是否正确

2 个答案:

答案 0 :(得分:3)

根据您的说法,听起来这是主要目标:

  

使 my_eav 表格中的 v 列保持同步   帖子表中的标题列。

如果这是正确的,我首先想到的是你可以通过仅在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');

这给了我们这样一个表格:

--------------------
|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实例我必须使用pragma命令强制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     |
-------------------------------

希望我理解这个问题,这有帮助。

答案 1 :(得分:3)

最佳解决方案是使用如上所述的连接。

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

但是,如果你想要触发器,你就会得到它们。 ;)
您可以使用“SQLite(WebSQL)”选项在http://sqlfiddle.com/上测试下面的所有代码 你必须选择查询终止符 - 管道[/]

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 - 确定

select * from my_eav

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

检查update_posts_title - 确定

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 - 确定

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 - 确定

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 - 确定

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

多数民众赞成。真的使用left join posts