【问题标题】:SQL grouping by distinct values in a multi-value string columnSQL按多值字符串列中的不同值分组
【发布时间】:2019-03-25 01:49:14
【问题描述】:

(我想根据具有多个值的字符串列中的不同值执行分组

该列有一个标准格式的字符串列表,用逗号分隔。潜在值仅为a,b,c,d

例如列collection(类型:字符串)包含:

Row 1: ["a","b"]
Row 2: ["b","c"]
Row 3: ["b","c","a"]
Row 4: ["d"]`

预期的输出是唯一值的计数:

collection | count
a | 2
b | 3
c | 2
d | 1

【问题讨论】:

  • 请用您正在使用的数据库标记您的问题并描述collection的数据类型。

标签: sql group-by amazon-redshift


【解决方案1】:

对于以下所有内容,我都使用了这张表:

create table tmp (
 id INT auto_increment,
 test VARCHAR(255),
 PRIMARY KEY (id)
);

insert into tmp (test) values 
    ("a,b"),
    ("b,c"),
    ("b,c,a"),
    ("d")
;

如果可能的值只有a,b,c,d,您可以尝试以下方法之一: 请注意,这仅在您没有像testtest_new 这样相似的值时才有效,因为这样test 也将与所有test_new 行合并,并且计数将不匹配

select collection, COUNT(*) as count from tmp JOIN (
    select CONCAT("%", tb.collection, "%") as like_collection, collection from (
        select "a" COLLATE utf8_general_ci as collection
        union select "b" COLLATE utf8_general_ci as collection
        union select "c" COLLATE utf8_general_ci as collection
        union select "d" COLLATE utf8_general_ci as collection
    ) tb
) tb1 
ON tmp.test LIKE tb1.like_collection
GROUP BY tb1.collection;

这会给你想要的结果

collection | count
    a      |   2
    b      |   3
    c      |   2
    d      |   1

或者你可以试试这个

SELECT 
   (SELECT COUNT(*) FROM tmp WHERE test LIKE '%a%') as a_count,
   (SELECT COUNT(*) FROM tmp WHERE test LIKE '%b%') as b_count,
   (SELECT COUNT(*) FROM tmp WHERE test LIKE '%c%') as c_count,
   (SELECT COUNT(*) FROM tmp WHERE test LIKE '%d%') as d_count
;

结果会是这样的

a_count | b_count | c_count | d_count
2       |    3    |   2     |   1

【讨论】:

    【解决方案2】:

    您需要做的是首先将集合列分解成单独的行(如flatMap 操作)。在redshift中,生成新行的唯一方法是JOIN - 所以让我们CROSS JOIN你的输入表带有一个具有连续数字的静态表,并且只取id小于或等于的表集合中的元素数量。然后我们将使用split_part 函数读取正确索引处的项目。一旦我们有了 exploaded 表,我们将做一个简单的GROUP BY

    如果您的项目存储为 JSON 数组字符串 ('["a", "b", "c"]'),那么您可以分别使用 JSON_ARRAY_LENGTHJSON_EXTRACT_ARRAY_ELEMENT_TEXT 而不是 REGEXP_COUNTSPLIT_PART

    with 
        index as (
            select 1 as i 
            union all select 2 
            union all select 3 
            union all select 4 -- could be substituted with 'select row_number() over () as i from arbitrary_table limit 4'
        ), 
        agg as (
            select 'a,b' as collection
             union all select 'b,c'
             union all select 'b,c,a'
             union all select 'd'
        )
        select 
            split_part(collection, ',', i) as item,
            count(*)
        from index,agg
        where regexp_count(agg.collection, ',') + 1 >= index.i -- only get rows where number of items matches
        group by 1
    

    【讨论】:

      猜你喜欢
      • 2014-01-05
      • 2012-02-18
      • 1970-01-01
      • 2019-07-19
      • 2021-02-14
      • 2014-08-20
      • 2014-07-31
      • 1970-01-01
      • 2018-12-09
      相关资源
      最近更新 更多