可以获得结果集,但我实际上只是将其转换为另一个表,每个作者一行。我不想从应用程序代码中运行这样的查询。
该SUBSTRING_INDEX
函数可用于提取第一个、第二个等。列表中的作者,例如
SUBSTRING_INDEX(SUBSTRING_INDEX(authors,',', 1 ),',',-1) AS author1
SUBSTRING_INDEX(SUBSTRING_INDEX(authors,',', 2 ),',',-1) AS author2
SUBSTRING_INDEX(SUBSTRING_INDEX(authors,',', 3 ),',',-1) AS author3
但这最终会变得混乱,因为当您检索超出列表长度时,您将获得最后一位作者。
所以,你可以用一个相当难看的表达式来计算逗号的数量:
LENGTH(authors)-LENGTH(REPLACE(authors,',','')) AS count_commas
但是附加一个尾随逗号同样容易,然后将空字符串转换为 NULL 因此,将作者替换为:
CONCAT(authors,',')
然后将其包装在 TRIM 和 NULLIF 函数中。
NULLIF(TRIM( foo ),'')
然后,您可以编写一个从每一行获取第一作者的查询,另一个从每一行获取第二作者的查询(与第一个查询相同,只需将“1”更改为“2”,第三作者等. 最多列值中的最大作者数。将所有这些查询与 UNION 操作结合在一起(这将为您消除重复项。)
所以,这个查询:
SELECT NULLIF(TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(a.authors,','),',',1),',',-1)),'') AS author
FROM unfortunately_designed_table a
UNION
SELECT NULLIF(TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(a.authors,','),',',2),',',-1)),'')
FROM unfortunately_designed_table a
UNION
SELECT NULLIF(TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(a.authors,','),',',3),',',-1)),'')
FROM unfortunately_designed_table a
UNION
SELECT NULLIF(TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(a.authors,','),',',4),',',-1)),'')
FROM unfortunately_designed_table a
这将返回唯一作者姓名的结果集(无疑是 NULL)。这只是获得列表中的前四位作者,您需要扩展它以获得第五、第六等。
您可以通过查找最大逗号数并加 1 来获得该列中的最大条目数
SELECT MAX(LENGTH(a.authors)-LENGTH(REPLACE(a.authors,',','')))+1 AS max_count
FROM unfortunately_designed_table a
这让您知道您需要将上面的查询扩展多远才能获取所有作者值(在您运行查询的特定时间点......没有什么可以阻止某人稍后将另一个作者添加到列中的列表中时间。
在完成了在不同行上获得不同作者值的所有工作之后,您可能希望将它们留在这样的列表中。使用起来更容易。
但是,当然,也可以将该结果集转换回以逗号分隔的列表,尽管返回的字符串的大小受max_allowed_packet
会话变量 (iirc) 的限制。
要将其作为单行返回,使用逗号分隔的列表,请从上面获取整个查询,并将其包装在括号中作为行视图,给它一个别名,然后使用该GROUP_CONCAT
函数。
SELECT GROUP_CONCAT(d.author ORDER BY d.author) AS distinct_authors
FROM (
...
) d
WHERE d.author IS NOT NULL
如果您认为所有这些表达式都很丑陋,并且应该有一种更简单的方法来做到这一点,不幸的是(除了编写程序代码),真的没有。关系数据库旨在处理元组(行)中的信息,每一行代表一个实体。将多个实体或值填充到单个列中违背了关系设计。因此,SQL 没有提供一种简单的方法来将字符串中的值提取到单独的元组中,这就是执行此操作的代码如此混乱的原因。