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

Change value of column to a value from a foreign column

你好,我的 table 看起来像这样:

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是另一个table、"posts"的id列。当它不为空时,"v" 列应从 "posts".

中获取 "title" 列的值

当引用的 post 记录更新时,我想将此 table 上的 "v" 列更改为 [=34] 的 "title" 列=]s table。这可能吗?

类似

... 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;

但是不知道对不对

根据你所说的,这听起来像是主要目标:

Keep the v column in the my_eav table synchronized with the title column in the posts table.

如果这是正确的,我的第一个想法是您可以通过仅将标题存储在 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 table 设计,但听起来是这样的:

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

所以让我们在其中插入一些示例记录:

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

这给了我们这样的 table:

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

现在让我们用外键创建 my_eav table(我暂时省略了唯一约束,所以我们可以专注于触发器)。

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 table 看起来像这样:

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

很好,如果 posts 中不存在记录,我们无法将记录插入 my_eav

为了使标题在两个 table 之间保持同步,我们将需要 两个 触发器,而不仅仅是一个:[=31= 上的触发器] 首次创建记录时触发,并在 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);

Table现在:

-------------------------------
|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);

Table现在:

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

现在,这是我们需要创建的第二个触发器,它将在 posts 发生变化时保持 my_eav 更新。 请注意,我们正在 posts table 上创建此触发器。 在您的示例代码中,看起来您正试图 使用 my_eav 上的触发器从 posts 更改的值,而实际上您需要 将更改的值 推送到 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 table:

中的标题来测试它
update posts set title='Othello' where title='Romeo & Juliet';

检查更新是否发生:

select * from posts;

Table 现在显示:

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

希望我对问题的理解是正确的,这对您有所帮助。

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

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真的。