Why this recursive concat produces: Data too long

2019-08-22 21:23发布

I have this table on MySQL 8:

create table tbl(id varchar(2), val int);
insert into tbl values ('A',  1), ('B',  2), ('C',  3), ('D',  4), ('E',  5);

The following query should find out which record sets have values that add up to a value not greater than 6 (without importance of order):

with recursive cte(greatest_id, ids, total) as (
    select     id,
               concat(id, ''), 
               val
    from       tbl
    union all
    select     tbl.id,
               concat(cte.ids, tbl.id),
               cte.total + tbl.val
    from       cte 
    inner join tbl 
            on tbl.id > cte.greatest_id
           and cte.total + tbl.val <= 6
) 
select ids, total from cte

Running it results in the following error:

Error: ER_DATA_TOO_LONG: Data too long for column concat(id, '') at row 7

Why does MySQL produce this error?

For info, the desired output is below:

 IDS | TOTAL
 ----+------
 A   |  1
 B   |  2
 C   |  3
 D   |  4
 E   |  5
 AB  |  3
 AC  |  4
 AD  |  5
 AE  |  6
 BC  |  5
 BD  |  6
 ABC |  6

I want to know by which (documented?) rule MySQL produces this error here.

By comparison, the query works fine on PostgreSQL and Oracle (using their syntax variation), so I don't really understand why MySQL is having a problem with it.

1条回答
做自己的国王
2楼-- · 2019-08-22 22:05

A long way down the MySQL 8 CTE manual page is an example that shows the problem you are having. Basically the problem is that your ids column is too narrow for the ABC value being assigned to it as it gets its width from the non-recursive part of the CTE (which is effectively the length of id i.e. 2 characters). You can solve that problem with a CAST to a big enough width to fit all the results e.g.:

with recursive cte(greatest_id, ids, total) as (
    select     id,
               CAST(id AS CHAR(5)) AS ids, 
               val
    from       tbl
    union all
    select     tbl.id,
               concat(cte.ids, tbl.id),
               cte.total + tbl.val
    from       cte 
    inner join tbl 
            on tbl.id > cte.greatest_id
           and cte.total + tbl.val <= 6
) 
select ids, total from cte

Update of your demo

查看更多
登录 后发表回答