首页 > 解决方案 > 未捕获的 mysqli_sql_exception:子查询返回多于 1 行

问题描述

我有两张桌子,一张有名字和电话号码,第二张有电话

addressbook  name(VARCHAR) number(VARCHAR)

calls        date(DATE) number(VARCHAR) name(VARCHAR)

我想用地址簿中的条目更新调用表中的名称列

UPDATE calls
SET name = ( SELECT name FROM addressbook WHERE number = calls.number )
WHERE DATE = "2020.01.01" 
ORDER BY DATE

我得到Uncaught mysqli_sql_exception: Subquery return more than 1 row但地址簿中没有双引号我检查了几次。

标签: mysql

解决方案


您的更新语句可能失败的唯一方法

子查询返回超过 1 行

是如果至少有calls一行的数字在 中出现多次addressbook。您可以使用以下查询找到它们:

select number, count(*)
from addressbook
group by number
having count(*) > 1;

假设您有这两行addressbook

name   number
------ ------
fred   123
barney 123

假设这是行calls

date       name number
---------- ---- ------
2020.01.01 null 123

当您执行 Stefano 的更新语句时,限制子句不是确定性的,因为子查询中没有关联order by的子句。也没有任何共同的属性callsaddressbook这会使它有意义。关于更新的order by条款是无关紧要的。因此,您不能保证将哪个名称分配给该calls行。这就是我在对 Stefano 的回答发表评论时试图表达的观点。

如果系统的设计是允许多个人在一段时间内拥有一个号码(他们当然是这样),那么您的架构是不完整的。如果这是真的,那么addressbook需要号码所有者的生效日期。

如果系统的设计不允许随着时间的推移多个人拥有一个号码,那么您必须删除重复的行。

无论哪种情况,您都需要做两件事:

  • 采用声明性参照完整性约束,这样您就不会再次发生冲突
  • 停止更新calls:插入(不更新)名称或完全删除列

如果我要实现电话系统的表格,我会从以下内容开始:

create table PERSON (
       PERSON_ID          integer      not null primary key,
       NAME               varchar(100) not null /*lots of other columns*/);
create table PERSON_PHONE (
       PERSON_PHONE_ID    integer      not null primary key,
       PERSON_ID          integer      not null,
       PHONE_NUM          varchar(30)  not null,
       CONTRACTED         date         not null, /*lots of other columns*/
       unique (PERSON_ID, PHONE_NUM, CONTRACTED),
       foreign key (PERSON_ID) references PERSON(PERSON_ID));
create table PHONE_CALL (
       START_DATE         date         not null,
       END_DATE           date         not null,
       PERSON_PHONE_ID    integer      not null,
       primary key (PERSON_PHONE_ID, START_DATE),
       foreign key (PERSON_PHONE_ID) references PERSON_PHONE(PERSON_PHONE_ID));

确实,有时,为了使用更少的资源更快地完成查询,人们有时会对模式进行非规范化以减少原本需要的连接操作的数量。非规范化需要仔细考虑。


推荐阅读