如何在 SQL 中计算逗号分隔列表中的字符串项
Posted
技术标签:
【中文标题】如何在 SQL 中计算逗号分隔列表中的字符串项【英文标题】:How to count string items in comma separated list in SQL 【发布时间】:2021-10-08 20:32:10 【问题描述】:在我的表格中,我有一列标签,它是一个字符串列表。
["conda"]
["intel"]
["pandas", "conda"]
["api", "partner"]
["dask", "distributed computing", "conda"]
我希望能够获得每个不同字符串的计数。
即
-------------------
tag | count
-------------------
conda | 3
pandas | 1
...
-------------------
到目前为止,我一直在用困难的方式来做这件事......
SELECT tags
FROM "public"."content"
WHERE concat(tags) LIKE '%INSERT_TAG_NAME_HERE%'
提前致谢!
【问题讨论】:
数据库是什么? 如果您使用的是关系引擎,那么最好将数据存储在 3NF 中。这种设计不符合 1NF 的最低级别。我强烈建议您重新设计数据库。 【参考方案1】:您使用的是什么版本的 SQL?如果使用 SQL Server (T-SQL),一种方法是使用表值函数将连接的文本字段拆分为多个片段。这样的事情可以让你开始:
编辑: 糟糕!较新的 SQL Server 具有string_split()
功能,您可以类似地使用cross apply
,从而无需tvfSplitToTableStrings()
。见the docs。
CREATE FUNCTION dbo.tvfSplitToTableStrings
(
@items NVARCHAR(max),
@delimiter CHAR(1)
)
RETURNS @itemTable table
(
[item] NVARCHAR(100)
, sequence INT
)
as
BEGIN
DECLARE
@tempItemList NVARCHAR(max),
@i int,
@item NVARCHAR(100)
SET @tempItemList = @items
SET @tempItemList = LTRIM(RTRIM(@items))
-- get index where our delimiter was found
SET @i = CHARINDEX(@delimiter, @tempItemList)
-- loop while all the characters in the list have not been traversed yet
declare @count int = 0
WHILE (LEN(@tempItemList) > 0)
BEGIN
IF @i = 0
-- if there are no delimiters, then this is the only item in our list
SET @item = @tempItemList
ELSE
-- get the first word (from the left) less the delimiter character
SET @item = LEFT(@tempItemList, @i - 1)
set @count = @count + 1
INSERT INTO @itemTable (item, sequence) VALUES (LTRIM(RTRIM(@item)), @count)
IF @i = 0
SET @tempItemList = ''
ELSE
-- remove the word we just added to the table
SET @tempItemList = RIGHT(@tempItemList, LEN(@tempItemList) - @i)
-- lather, rinse, repeat
SET @i = CHARINDEX(@delimiter, @tempItemList)
END
RETURN;
END
然后您可以将其与cross apply
一起使用,如下所示:
;with a as (
select 1 as id, 'dog,cat,fish' as animal
union
select 2 as id, 'snake,cat,racoon' as animal
)
select a.id, b.item, b.sequence
from a
cross apply app.tvfSplitToTableStrings(a.animal, ',') b
由于您的字符串似乎包含额外的标记["...", "..."]
,您可以使用TRANSLATE()
清除它。或者您可以修改tvfSplitToTableStrings
函数本身以更智能地解析和去除不需要的标记。假设您的字符串值不包含任何标记字符(包括嵌入的空格),以下应该有效:
;with a as (
select 1 as id, '["dog", "cat", "fish"]' as animal
union
select 2 as id, '["snake", "cat", "racoon"]' as animal
)
, b as (
select a.id
, TRIM(TRANSLATE(b.item, '[]" ', ' ')) as item
, b.sequence
from a
cross apply app.tvfSplitToTableStrings(a.animal, ',') b
)
select item, count(*) as [count]
from b
group by item
order by item;
如果您使用的是 mysql,您可能希望采用https://***.com/a/17942691 之类的方法。
另请注意,您可能需要注意性能。 cross apply
适用于较小的数据集,但可能无法很好地扩展。
【讨论】:
我实际上并不清楚正在使用哪个版本的 SQL,因为我正在尝试在 Metabase 中执行这种 voo doo 魔术。 metabase.com/docs/latest/users-guide/writing-sql.html【参考方案2】:我现在对此有了更好的理解。关于 Metabase 的说明,Metabase 使用的服务在我的情况下使用的是 Postgres。
我认为我的一些查询的问题在于一些 null 和空值。
SELECT tag, count(*) AS cnt FROM (
SELECT
content.id, content.tags, tag FROM content,
jsonb_array_elements(
case jsonb_typeof(content.tags::jsonb)
when 'array' then content.tags
else '[]' end
) j(tag)
) inn
GROUP BY tag
ORDER BY cnt DESC
希望这对将来的其他人有所帮助。
【讨论】:
以上是关于如何在 SQL 中计算逗号分隔列表中的字符串项的主要内容,如果未能解决你的问题,请参考以下文章
如何通过 Oracle 中的正则表达式从逗号分隔列表中删除重复项,但我不想要重复值? [复制]
如何在选择语句的“NOT IN”子句中使用逗号分隔的字符串列表作为 pl/sql 存储的函数参数
如何通过 Oracle 中的 regexp_replace 从逗号分隔列表中删除重复项?
如何通过 Oracle regexp_replace 中的正则表达式从逗号分隔列表中删除重复项? [复制]