MySQL 如何實現(xiàn)一個只有一行數(shù)據(jù)的表
作者: 不剪發(fā)的Tony老師
畢業(yè)于北京航空航天大學(xué),十多年數(shù)據(jù)庫管理與開發(fā)經(jīng)驗,目前在一家全球性的金融公司從事數(shù)據(jù)庫架構(gòu)設(shè)計。CSDN學(xué)院簽約講師以及GitChat專欄作者。csdn上的博客收藏于以下地址:https://tonydong.blog.csdn.net
文章目錄
MySQL 5.6 實現(xiàn)
MySQL 5.7 實現(xiàn)
MySQL 8.0 實現(xiàn)
總結(jié)
大家好,我是只談技術(shù)不剪發(fā)的 Tony 老師。我們在之前的文章中介紹了如何在 PostgreSQL 中實現(xiàn)一個只能存儲一行數(shù)據(jù)的表。今天我們來看看如何在 MySQL 中實現(xiàn)相同的功能。
只有一行數(shù)據(jù)的表通常被用于某些特定的功能,例如存儲系統(tǒng)的版本或者配置信息,全局計數(shù)器,心跳表等。假如我們有一個表 t_visit,用于記錄網(wǎng)頁的訪問量:
create table t_visit(
hits bigint not null,
update_at datetime not null
);
在部署程序時需要生成一條記錄,將訪問量初始化為 0;以后每次用戶訪問時需要加 1;為了前端頁面能夠正確顯示,不允許用戶刪除該記錄。這種需求該如何實現(xiàn)?
MySQL 5.6 實現(xiàn)
對于 MySQL 5.6 以及之前的版本,可以通過增加一個 ENUM 枚舉字段和唯一約束限制表中只能存儲一行數(shù)據(jù):
alter table t_visit add only_one_row enum('Y') not null default 'Y';
alter table t_visit add constraint uk_visit unique key (only_one_row);
新增加的字段 only_one_row 非空且取值只能為 Y,同時基于該字段創(chuàng)建了一個唯一約束,因此整個表中只能存儲一行數(shù)據(jù)。
初始化時插入數(shù)據(jù)如下:
insert into t_visit(hits, update_at) values (0, now());
select * from t_visit;
hits|update_at |only_one_row|
----|-------------------|------------|
0|2020-07-07 08:05:03|Y |
如果數(shù)據(jù)已經(jīng)存在,再次插入數(shù)據(jù)將會返回錯誤:
insert into t_visit(hits, update_at) values (1, now());
ERROR 1062 (23000): Duplicate entry 'Y' for key 't_visit.uk_visit'
實際上此時應(yīng)該進行更新操作:
update t_visit
set hits=hits+1,
update_at= now()
where only_one_row = 'Y';
或者,可以使用INSERT ... ON DUPLICATE KEY UPDATE語句進行統(tǒng)一操作:
-- 清空數(shù)據(jù)
truncate table t_visit;
-- 第一次插入數(shù)據(jù)
insert into t_visit(hits, update_at) values (1, now())
on duplicate key update hits=ifnull(hits,0)+1, update_at= now();
select * from t_visit;
hits|update_at |only_one_row|
----|-------------------|------------|
1|2020-07-07 08:22:32|Y |
-- 第 N 次插入數(shù)據(jù)
insert into t_visit(hits, update_at) values (1, now())
on duplicate key update hits=ifnull(hits,0)+1, update_at= now();
select * from t_visit;
hits|update_at |only_one_row|
----|-------------------|------------|
2|2020-07-07 08:22:59|Y |
為了防止表中的數(shù)據(jù)被誤刪除,需要通過一個觸發(fā)器來實現(xiàn)禁止刪除操作:
delimiter $$
create trigger tri_disable_visit_del
before delete on t_visit
for each row
begin
signal sqlstate '45000'
set message_text = '禁止刪除訪問信息!';
end$$
delimiter ;
該觸發(fā)器是一個針對刪除操作的 BEFORE 觸發(fā)器,在任何刪除操作之前返回錯誤信息。創(chuàng)建觸發(fā)器之后,當(dāng)我們嘗試刪除表中的記錄:
delete
from t_visit
where only_one_row = 'Y';
ERROR 1644 (45000): 禁止刪除訪問信息!
select * from t_visit;
hits|update_at |only_one_row|
----|-------------------|------------|
2|2020-07-07 08:22:59|Y |
刪除語句返回了錯誤信息,表中的數(shù)據(jù)仍然存在。不過,TRUNCATE TABLE 語句仍然可以清除表中的數(shù)據(jù),因為它不會觸發(fā) DML 觸發(fā)器。
MySQL 5.7 實現(xiàn)
MySQL 5.7 除了使用前面的方法之外,還可以利用新增加的計算列(Generated Column)加唯一約束的方式。例如:
-- 刪除 t_visit 表
drop table t_visit;
create table t_visit(
hits bigint not null,
update_at datetime not null,
only_one_row int as (1) unique
);
其中,字段 only_one_row 是一個虛擬的計算列,同時具有唯一性。插入數(shù)據(jù)的結(jié)果如下:
insert into t_visit(hits, update_at) values (0, now());
select * from t_visit;
hits|update_at |only_one_row|
----|-------------------|------------|
0|2020-07-07 09:22:13| 1|
insert into t_visit(hits, update_at) values (1, now());
ERROR 1062 (23000): Duplicate entry '1' for key 't_visit.only_one_row'
同樣,我們也可以使用INSERT ... ON DUPLICATE KEY UPDATE語句進行統(tǒng)一操作,具體參考上文中的 MySQL 5.6 實現(xiàn)。
利用觸發(fā)器可以防止表中的數(shù)據(jù)被誤刪除,具體參考上文中的 MySQL 5.6 實現(xiàn)。
MySQL 8.0 實現(xiàn)
MySQL 8.0.13 之后除了可以使用上面介紹的兩種方法之外,還可以通過函數(shù)索引實現(xiàn)相同的功能。例如:
create table t_visit(
hits bigint not null,
update_at datetime not null
);
create unique index uk_visit on t_visit( (1) );
其中,索引 uk_visit 是一個基于常量 1 的函數(shù)索引,并且具有唯一性。函數(shù)索引實際上是虛擬列索引的擴展,因此兩者非常相似。
插入數(shù)據(jù)的結(jié)果如下:
insert into t_visit(hits, update_at) values (0, now());
select * from t_visit;
hits|update_at |only_one_row|
----|-------------------|------------|
0|2020-07-07 09:22:13| 1|
insert into t_visit(hits, update_at) values (1, now());
ERROR 1062 (23000): Duplicate entry '1' for key 't_visit.uk_visit'
同樣,我們也可以使用INSERT ... ON DUPLICATE KEY UPDATE語句進行統(tǒng)一操作,具體參考上文中的 MySQL 5.6 實現(xiàn)。
利用觸發(fā)器可以防止表中的數(shù)據(jù)被誤刪除,具體參考上文中的 MySQL 5.6 實現(xiàn)。
總結(jié)
在 MySQL 中為了實現(xiàn)一個只有一行數(shù)據(jù)的表,可以創(chuàng)建一個數(shù)值為常量的字段,并且基于該字段創(chuàng)建唯一索引。ENUM 枚舉數(shù)據(jù)類型和計算列都可以用于實現(xiàn)該功能,MySQL 8.0 新增的函數(shù)索引甚至不需要創(chuàng)建額外的字段就可以實現(xiàn)相同的功能。
除了本文使用的方法之外,你還有沒有其他的實現(xiàn)方法?歡迎關(guān)注??、評論??、點贊??