使用case when查询报错:Subquery returns more than 1 row

category_base.sql (89.1 MB)
category_team_developer_config.sql (6.0 MB)
【 TiDB 使用环境】测试
【 TiDB 版本】7.5
表结构:

CREATE TABLE `category_base` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `platformCode` char(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '平台简码',
  `siteCode` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '站点名称',
  `categoryId` char(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '分类id',
  `parentId` char(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '父类id',
  `categoryIdPath` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
  `categoryIdPathReverse` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
  `categoryName` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '分类名称',
  `categoryNamePath` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin COMMENT '分类名称路径',
  `type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '分类类型(homePage:首页;newProductList: 新品榜单)',
  `url` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '分类url',
  `level` int unsigned NOT NULL DEFAULT '0' COMMENT '级别',
  `isDel` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否删除 1.删除 0.未删除,默认为0',
  `isLeaf` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否有子分类 1是 0否',
  `version` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '版本号',
  `createdTime` datetime NOT NULL DEFAULT '2000-01-01 00:00:00' COMMENT '创建日期',
  `createdUser` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '创建人编号',
  `updateTime` datetime NOT NULL DEFAULT '2000-01-01 00:00:00' COMMENT '最后更新时间',
  `updateUser` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '更新人',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `uk_platform_site_level_type` (`platformCode`,`siteCode`,`categoryId`,`level`,`type`) USING BTREE,
  KEY `idx_site_category` (`siteCode`,`categoryName`) USING BTREE,
  KEY `idx_categoryId` (`categoryId`) USING BTREE,
  KEY `idx_parentId` (`parentId`) USING BTREE,
  KEY `idx_categoryIdPath` (`categoryIdPath`) USING BTREE,
  KEY `idx_categoryIdPathReverse` (`categoryIdPathReverse`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=329579 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='category base 分类';

CREATE TABLE `category_team_developer_config` (
  `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
  `teamConfigId` int unsigned NOT NULL COMMENT '团队分类配置id',
  `teamName` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '团队名称',
  `platform` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '平台',
  `site` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '站点',
  `categoryId` char(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '0' COMMENT '分类id',
  `level` tinyint(1) NOT NULL DEFAULT '0' COMMENT '分类级别',
  `parentCategoryId` char(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '0' COMMENT '父分类id',
  `developer` varchar(5000) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '开发员,多个逗号隔开',
  `createTime` datetime NOT NULL DEFAULT '2000-01-01 00:00:00' COMMENT '配置创建时间',
  `createUser` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '配置创建人',
  `updateTime` datetime NOT NULL DEFAULT '2000-01-01 00:00:00' COMMENT '配置更新时间',
  `updateUser` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '配置更新人',
  `isDel` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否删除 1.是 0.否',
  `delTime` datetime NOT NULL DEFAULT '2000-01-01 00:00:00' COMMENT '删除时间',
  `delUser` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '删除人',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `uk_t_p_s_c_d` (`teamName`,`platform`,`site`,`categoryId`,`delTime`) USING BTREE,
  KEY `idx_platform` (`platform`) USING BTREE,
  KEY `idx_site` (`site`) USING BTREE,
  KEY `idx_categoryId` (`categoryId`) USING BTREE,
  KEY `idx_teamConfigId` (`teamConfigId`) USING BTREE,
  KEY `idx_del` (`isDel`) USING BTREE,
  KEY `idex_teamName` (`teamName`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=56710 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='团队分类开发员配置';

查询SQL:

 SELECT   a.`teamConfigId`, a.`teamName`, a.`platform`,
            CASE
            WHEN a.platform = 'EB'
            THEN (SELECT categoryIdPath FROM `category_base` WHERE isDel = 0 AND platformCode = a.platform AND siteCode = a.site AND categoryId = a.categoryId AND a.level = `level`)
            WHEN a.platform = 'SM'
            THEN (SELECT categoryIdPath FROM `category_base` WHERE isDel = 0 AND platformCode = a.platform AND siteCode = a.site AND categoryId = a.categoryId AND `type` = 'java' AND a.level = `level`)
            WHEN a.platform = 'YA'
            THEN (SELECT categoryIdPath FROM `category_base` WHERE isDel = 0 AND platformCode = a.platform AND siteCode = a.site AND categoryId = a.categoryId AND `type` = 'homePage' AND a.level = `level`)
            WHEN a.platform = 'BB'
            THEN (SELECT categoryIdPath FROM `category_base` WHERE isDel = 0 AND platformCode = a.platform AND siteCode = a.site AND categoryId = a.categoryId AND `type` = 'list' AND a.level = `level`)
            END AS `categoryIdPath`
        FROM
            `category_team_developer_config` as `a`
        WHERE 
            a.`isDel` = 0 AND (FIND_IN_SET('liupan', `developer`)) LIMIT 0,10

报错信息:

> 1242 - Subquery returns more than 1 row
> 时间: 0.067s

同样的表结构和数据,在mysql和DB2的数据库里面里面是正常的,但是在tidb里面是报错,并且看了下数据,不应该存在这个错误

大家可以拿这个SQL和脚本数据测试下,是不是我的写法有问题,还是说这是一个BUG

数据:
category_base.sql (89.1 MB)
category_team_developer_config.sql (6.0 MB)

then 后面的sql语句里加上limit 1试试

加上这个肯定是可以的,但目前的条件里里面是不会跑到大于2条的when里面去,怎么回报错

:flushed:加上可以的话,就说明会跑出大于2条的。

正确安全的写法是when里面都加上里limit 1,不过这个数据我看不应该落入到大于2条的条件里面,同样的SQL和数据,在mysql和DB2里面测试是正常的

附件的sql里包含可重现的数据么?

包含的

绿框里的数据应该包含下面三个条件的数据吧,肯定会大于一条,是不是漏了什么条件?

image

1 个赞

:+1:火眼金睛

image
这个SQL的数据是’SM’,应该是走case platform = ‘SM’,不应该走你截图的那个里面

这个条件的数据只有一条,platform = ‘SM’
所以不应该走到
WHEN a.platform = ‘EB’
THEN (SELECT categoryIdPath FROM category_base WHERE isDel = 0 AND platformCode = a.platform AND siteCode = a.site AND categoryId = a.categoryId AND a.level = level)
这个SQL里面去判断,这里是platform = 'EB’不符合这个数据,应该走这个条件找这个SQL:
WHEN a.platform = ‘SM’
THEN (SELECT categoryIdPath FROM category_base WHERE isDel = 0 AND platformCode = a.platform AND siteCode = a.site AND categoryId = a.categoryId AND type = ‘java’ AND a.level = level)

还原数据库后,能够复现问题,按第一个case when是能查询出多个记录的。建议加上limit 1
image

注意,tidb是分布式数据库,是从各个tikv节点取到数据后,在tidb端进行关联过滤,所以要保证tikv层取数时就要符合逻辑。
从执行计划上可以明显看出来是并行执行的。原语句中只是传入的条件导致的不会返回多条,如果传入其他条件,还是会返回多条,所以要保证逻辑上的正确性。

你说的这个在其他数据库都一样,都有可能出现多条数据,要保障逻辑正确,确保不会出错确实加limit 1是对的,只是现在针对的是这个条件的数据来说会报错,虽然这是分布式的,那也应该是要根据过滤的数据(这个条件是只有一条)去取才对的。

按这个逻辑,就要求数据库第一步先按where条件从各个节点获取数据,然后第二部拿筛选后的数据再去各个节点取数。第一步和第二步就不能并行执行了。看上面的执行计划,目前第一步和第二步是并行执行的。

万能的社区 :+1:

https://github.com/pingcap/tidb/issues/35706

获取一行却得到多行就会出现这个错误

此话题已在最后回复的 60 天后被自动关闭。不再允许新回复。