在使用达梦数据库管理工具时,我们测试过程中时常需要更新表数据,有时为了便捷,会直接使用管理工具修改表数据的值,但偶尔会遇到“结果集不可更新,请确认查询列是否出自同一张表,并且包含值唯一的列。”的报错,那么这究竟是怎么回事呢。
一、问题复现:
1.创建一张测试表,并插入数据
2.使用SQL查询该表数据
3.修改表数据
在修改表数据的过程中,出现了报错
正常情况下,点击查询结果集这里的“小锁”图标,即可修改数据
但这里点击时,弹出了报错窗口,报错信息为“结果集不可更新,请确认查询列是否出自同一张表,并且包含值唯一的列。”
二、问题分析
根据我们的操作可以确认,查询结果集就是出自同一张表。如果查询结果集确实不是出自同一张表(即实际并非本文中的操作方式,而是几张表关联查询),那一定是不可以用这种方式更新表数据的。
所以,根据其报错提示,该问题主要是由于该表查询的列不唯一,也就是说表中不存在主键、唯一约束、唯一索引,无法确定数据唯一性。
进一步排查,在我们操作管理工具执行更新操作前,打开数据库SQL日志,看看操作管理工具,数据库具体执行了什么操作呢?
通过抓取管理工具操作的SQL,查询SQL日志发现,日志中有这样一段SQL:
格式化该SQL,简单分析下可以得知,该条SQL语句,其实就是看查询的结果集对应的表上是不是有唯一索引(达梦创建唯一键时,会自动创建一条由系统内部维护的唯一索引),且唯一索引在哪些列上。
执行一下该条SQL,该语句对应的参数分别是模式名和表名,根据SQL日志中的PARAMS参数提示,确认两个参数实际为模式名'SYSDBA'和表名'TEST'
实际的查询结果为空。
那么如果在C1列上创建唯一索引,效果会如何呢?
创建唯一索引的方法有很多,可以创建主键,唯一约束,唯一索引(创建主键和唯一约束时,数据库内部都会创建一个由数据库内部自己维护的唯一索引)。
这里创建主键试试:
ALTER TABLE "SYSDBA"."TEST" ADD PRIMARY KEY("C1");
主键创建完毕,再次执行SQL日志中的那条查询语句,此时已经有结果集了,查询结果显示在SYSDBA模式下的TEST表中C1列上,创建了一个升序排列的唯一索引,索引名为INDEX33649530(该索引由数据库系统自动创建,内部自己维护,用户不可主动删除,删除时会报错“试图删除系统索引”)。当然,如果创建唯一约束和唯一索引,效果是一样的,区别在于如果创建的是唯一索引,这里查询结果显示的就是我们自定义的唯一索引名字了。
此时再去用管理工具执行修改数据的操作,则不会再报错,可以提交并修改成功
再次查询,数据确认已修改
三、注意事项
需要注意:
1.如果在执行SQL查询时,查询结果集中的列必须全部包含创建唯一索引时对应的列,否则则会报错。
如下图中,只有SQL1,SQL4,SQL6,SQL7可以更新表数据(即查询结果集必须包含C1列):
而下图中,只有SQL6,SQL7可以更新表数据(即查询结果集必须包含C1,C3列):
四、解决方法
解决该问题,有以下三种方法可供参考:
(1)添加主键约束
ALTER TABLE "SYSDBA"."TEST" ADD CONSTRAINT PK_TEST_C1 PRIMARY KEY("C1");
或
ALTER TABLE "SYSDBA"."TEST" ADD PRIMARY KEY("C1");
(2)添加唯一约束
ALTER TABLE "SYSDBA"."TEST" ADD CONSTRAINT "CONS_UNI_TEST_C1" UNIQUE("C1");
(3)创建唯一索引
CREATE UNIQUE INDEX "UIDX_TEST_C1" ON "SYSDBA"."TABLE1"("C1");