一张表有且只有一条记录(续) - 支持插入,并且查询、更新、删除只作用在最后一条记录上

标签

PostgreSQL , 有且只有一条记录


背景

之前写过一篇文档,介绍如何控制某张表有且只有一条记录。

《如何实现一张表有且只有一条记录 implement PostgreSQL table have one and only one row》

接下来这个需求与之类似,一张表好像有且只有一条记录,要求这样:

1、支持插入、更新、删除、查询操作,

2、有一个时间字段用来区分这条记录是什么时候插入、更新的。

3、更新只作用在最后一条记录(时间最大的那条)上,

4、查询只返回时间最大的一条记录。所以看起来就只有一条记录一样。(实际上如果插入了很多,那么就是很多条)

5、删除时,删除所有记录。

实现方法

建立2张表,一张视图,面向用户的是视图(所有的增删改查都基于视图,所以用户可以忘记基表和影子表)。

1、基表

create table base_tbl (
  id serial8 primary key,  -- 必须有一个PK
  info text,    --  用户自身需求的内容
  c1 int,       --  用户自身需求的内容
  c2 int,       --  用户自身需求的内容
  ts timestamp  --  时间(更新、插入时务必更新这个时间)
);  

create index idx_base_tbl on base_tbl (ts);

2、影子表(用于触发器),如果没有影子表,直接对基表建立触发器,会有锁错误。

create table shadow_base_tbl ();

3、基表的limit 1视图

create view tbl as select * from base_tbl order by ts desc limit 1;

创建规则和触发器,实现前面提到的需求。

1、视图insert, update, delete规则

create rule r1 AS ON INSERT TO tbl DO INSTEAD INSERT INTO base_tbl (info, c1, c2, ts) VALUES (new.info, new.c1, new.c2, clock_timestamp());
create rule r2 AS ON UPDATE TO tbl DO INSTEAD UPDATE base_tbl SET info = new.info, c1=new.c1, c2=new.c2, ts=clock_timestamp() WHERE base_tbl.id=old.id;
create rule r3 AS ON DELETE TO tbl DO INSTEAD DELETE FROM shadow_base_tbl;

2、影子表delete触发器

create or replace function tg_truncate_v() returns trigger as $$
declare
begin
  truncate base_tbl;
  return null;
end;
$$ language plpgsql strict;  

create trigger tg before delete on shadow_base_tbl for each statement execute procedure tg_truncate_v();

结构定义如下

postgres=# \d+ tbl
                                       View "public.tbl"
 Column |            Type             | Collation | Nullable | Default | Storage  | Description
--------+-----------------------------+-----------+----------+---------+----------+-------------
 id     | bigint                      |           |          |         | plain    |
 info   | text                        |           |          |         | extended |
 c1     | integer                     |           |          |         | plain    |
 c2     | integer                     |           |          |         | plain    |
 ts     | timestamp without time zone |           |          |         | plain    |
View definition:
 SELECT base_tbl.id,
    base_tbl.info,
    base_tbl.c1,
    base_tbl.c2,
    base_tbl.ts
   FROM base_tbl
  ORDER BY base_tbl.ts DESC
 LIMIT 1;
Rules:
 r1 AS
    ON INSERT TO tbl DO INSTEAD  INSERT INTO base_tbl (id, info, c1, c2, ts)
  VALUES (new.id, new.info, new.c1, new.c2, clock_timestamp())
 r2 AS
    ON UPDATE TO tbl DO INSTEAD  UPDATE base_tbl SET info = new.info, c1 = new.c1, c2 = new.c2, ts = clock_timestamp()
  WHERE base_tbl.id = old.id
 r3 AS
    ON DELETE TO tbl DO INSTEAD  DELETE FROM shadow_base_tbl  

postgres=# \d+ base_tbl
                                                          Table "public.base_tbl"
 Column |            Type             | Collation | Nullable |               Default                | Storage  | Stats target | Description
--------+-----------------------------+-----------+----------+--------------------------------------+----------+--------------+-------------
 id     | bigint                      |           | not null | nextval('base_tbl_id_seq'::regclass) | plain    |              |
 info   | text                        |           |          |                                      | extended |              |
 c1     | integer                     |           |          |                                      | plain    |              |
 c2     | integer                     |           |          |                                      | plain    |              |
 ts     | timestamp without time zone |           |          |                                      | plain    |              |
Indexes:
    "base_tbl_pkey" PRIMARY KEY, btree (id)
    "idx_base_tbl" btree (ts)  

postgres=# \d+ shadow_base_tbl
                            Table "public.shadow_base_tbl"
 Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
--------+------+-----------+----------+---------+---------+--------------+-------------
Triggers:
    tg BEFORE DELETE ON shadow_base_tbl FOR EACH STATEMENT EXECUTE PROCEDURE tg_truncate_v()

测试tbl视图的dml如下

1、插入多次

postgres=# insert into tbl(info,c1,c2,ts) values ('test',1,2,now());
INSERT 0 1
postgres=# insert into tbl(info,c1,c2,ts) values ('test12',2,222,now());
INSERT 0 1
postgres=# insert into tbl(info,c1,c2,ts) values ('test12',2,222,now());
INSERT 0 1
postgres=# insert into tbl(info,c1,c2,ts) values ('test12',2,222,now());
INSERT 0 1  

只显示最后一条记录的目的达到
postgres=# select * from tbl;
 id |  info  | c1 | c2  |            ts
----+--------+----+-----+---------------------------
  4 | test12 |  2 | 222 | 2017-07-11 20:39:56.75285
(1 row)  

查看基表,所有记录都在
postgres=# select * from base_tbl;
 id |  info  | c1 | c2  |             ts
----+--------+----+-----+----------------------------
  1 | test   |  1 |   2 | 2017-07-11 20:39:49.933267
  2 | test12 |  2 | 222 | 2017-07-11 20:39:54.939552
  3 | test12 |  2 | 222 | 2017-07-11 20:39:56.406619
  4 | test12 |  2 | 222 | 2017-07-11 20:39:56.75285
(4 rows)

2、查询

postgres=# select * from tbl;
 id |  info  | c1 | c2  |            ts
----+--------+----+-----+---------------------------
  4 | test12 |  2 | 222 | 2017-07-11 20:39:56.75285
(1 row)

3、更新

只会更新最后一条   

postgres=# update tbl set info='abcde';
UPDATE 1
postgres=# select * from base_tbl;
 id |  info  | c1 | c2  |             ts
----+--------+----+-----+----------------------------
  1 | test   |  1 |   2 | 2017-07-11 20:39:49.933267
  2 | test12 |  2 | 222 | 2017-07-11 20:39:54.939552
  3 | test12 |  2 | 222 | 2017-07-11 20:39:56.406619
  4 | abcde  |  2 | 222 | 2017-07-11 20:42:08.230306
(4 rows)  

postgres=# select * from tbl;
 id | info  | c1 | c2  |             ts
----+-------+----+-----+----------------------------
  4 | abcde |  2 | 222 | 2017-07-11 20:42:08.230306
(1 row)

4、删除

删除,触发truncate基表的动作  

postgres=# delete from tbl;
DELETE 0
postgres=# select * from tbl;
 id | info | c1 | c2 | ts
----+------+----+----+----
(0 rows)  

postgres=# select * from base_tbl;
 id | info | c1 | c2 | ts
----+------+----+----+----
(0 rows)

5、维护

实际上如果用户一直不调用delete,那么随着插入,基表会越来越大。

建议要经常维护基表(例如 锁住基表,把最后一条查出来,TRUNCATE 基表,然后把最后一条插进去)。

例子

begin;
set lock_timeout ='1s';
create LOCAL temp table tmp1 (like tbl) ;
lock table base_tbl in ACCESS EXCLUSIVE mode;
insert into tmp1 select * from tbl;
truncate base_tbl;
insert into tbl select * from tmp1;
drop table tmp1;
end;

注意

注意到,插入是不管你原来有没有记录的,并不是insert on conflict do update的做法。

所以插入的并发可以做到很高。

而更新,可能并发还是会较低,因为锁的是最后一条记录。记录成为锁热点并发就上不来。

时间: 2024-10-08 06:29:44

一张表有且只有一条记录(续) - 支持插入,并且查询、更新、删除只作用在最后一条记录上的相关文章

一张表中根据某一字段相同值得另一字段的所有值拼接起来,组成一条新的语句

问题描述 一张表中根据某一字段相同值得另一字段的所有值拼接起来,组成一条新的语句如下:表a,有字段 x,y,zx y z1 name p1 name w1 name d2 sex s2 sex c2 sex k语句运行后变成的结果为:字段1 字段2 字段3 1 name p,w,d 2 sex s,c,k 有哪位帮忙下,谢谢!! 问题补充:1楼牛人弄出来了是单组的,即把出group by 单一字段的其他字段都加了起来,其实,我的是字段1和2都相同的,字段1和2照着写,只有字段3不同, 也只是字段

根据搜条件实时统计某张表中某几个不重复字段的产品数量查询速度慢问题

问题描述 如图功能就是根据搜索条件分别统计出,分别统计出区域,品牌,星级,主题不同数量.尝试方案一:用最简单的groupby可以实现功能,但是速度会非常慢.尝试方案二:用缓存又不能保证数量根据查询条件而变化麻烦各位大神提供合理解决方案谢谢. 解决方案 解决方案二:查询分析器里,右键选择"显示已估计的执行计划",根据提示优化索引.解决方案三:存储过程+分页控制显示解决方案四:如果你用的数据库是sqlserver2005以上分页处理起来会简单,给你个例子Godeclare@page_siz

MySQL实现两张表数据的同步方法总结

有两张表A和B,要求往A里面插入一条记录的同时要向B里面也插入一条记录,向B里面插入一条记录的同时也向A插入一条记录.两张表的结构不同,需要将其中几个字段对应起来.可以用下面的触发器实现. 表A的触发器: begin set @disable=1; if @disable=1 and NOT EXISTS(SELECT 1 FROM tableB where ID=new.ID) then   insert into tableB (ID,对应字段1) values(new.ID,new.对应字

MySQL实现两张表数据的同步_Mysql

mysql通过触发器实现两个表的同步,需要了解的朋友可以看一下. 有两张表A和B,要求往A里面插入一条记录的同时要向B里面也插入一条记录,向B里面插入一条记录的同时也向A插入一条记录.两张表的结构不同,需要将其中几个字段对应起来.可以用下面的触发器实现. 表A的触发器: begin set @disable=1; if @disable=1 and NOT EXISTS(SELECT 1 FROM tableB where ID=new.ID) then insert into tableB (

学生上机信息统计——对两张表的联合查询

     在学生上机信息统计中,   此处,因为要查询两张表,而且两张表查询的先后有时要严格表明,所以,在查询时,有两种思路:      1,定义两个Recordset对象,并根据字段,判断是先查询学生信息表,还是先查询学生上机表      2,两张表有一个共同的字段:卡号,所以,可以根据卡号将两张表连起来查询,这样比较方便.    因为第一种方式比较复杂,容易出错,所以,我选择了第二种方式,将两张表联合起来.      下面是对两张表联合起来的查询的语句:   查询结果如下:   但是,,实际

更新表-sql 如何在建立一个存储过程时更新两张表的数据?

问题描述 sql 如何在建立一个存储过程时更新两张表的数据? create proc proc2 ( @OrderID int, @BookID varchar(20),--更新 orderInfo quantity:更新 orderSheet payment @BookStock int ) as update orderSheet set payment = b.newpay FROM orderInfo,orderSheet,(select (payment + price * @Book

java-今天面试的时候遇到一个问题,查三张表,有一百万条纪录,怎么查?他是想问什么,数据库优化吗

问题描述 今天面试的时候遇到一个问题,查三张表,有一百万条纪录,怎么查?他是想问什么,数据库优化吗 今天面试的时候遇到一个问题,查三张表,有一百万条纪录,怎么查?他是想问什么,数据库优化吗 解决方案 我不认为索引或分页是重点. 那不是怎么查的问题,而是怎么优化数据库的问题. 我觉得应该是查的方式或访问数据的方式,防止内存溢出,两种方法. 1.用游标查,而不是一下子取到内存中. 2.一回查询一定量数据,取多回. ps: 查的时候,在有必要的时候加上HINT句,可以优化效率. 这个你也说了的话,我觉

sql-oracle数据库的一张总表,通过一条数据分组拆分为多张表并excle导出。

问题描述 oracle数据库的一张总表,通过一条数据分组拆分为多张表并excle导出. oracle数据库的一张总表,通过一条数据分组拆分为多张表并excle导出.比如select * from studentinfo 里面有一个specode数据 通过不同的specode分为不同的表并导出,怎么写sql 解决方案 写个存储过程,定义一个游标,按你说的数据进行分组,然后循环把数据插入到不同的表里就行了,把表里的数据通过toad导出到excel里 解决方案二: 只要写出查询的语句查询出结果,然后另

MYSQL 中怎样把同一张表中相同字段的内容合并为一条记录?

问题描述 MYSQL 中怎样把同一张表中相同字段的内容合并为一条记录? 我的表现在是这样的: 客户id 购买产品id 1 123 1 124 1 125 1 126 2 168 2 169 想让表转变成: 客户id 购买产品id 1 123,124,125,126 2 168,169 也就是说把一个客户购买的产品合并显示在一条记录中. 解决方案 使用group_concat 解决方案二: 用行转列实现,今天上午看到一个和你的问题完全符合 http://blog.csdn.net/ning1093