【问题标题】:SQL query to count unique csv in a table用于计算表中唯一 csv 的 SQL 查询
【发布时间】:2022-01-17 03:24:51
【问题描述】:

假设我有以下 SQLite v3 表,其中包含每个国家/地区的一些名称:

Germany   Peter,Jan,David,Florian
USA       James,Joe,Bob,David,Alan,George
UK        George,Jack,Peter
Israel    David,Moshe,Chaim

每个国家/地区的名称用逗号分隔。

我想计算总共有多少个唯一名,在本例中为 12 个(例如,大卫同时在德国、美国和以色列)。

有没有直接的方法通过 SQL 查询来做到这一点?

【问题讨论】:

  • 您正在使用/定位什么 RDBMS?处理字符串和逗号分隔的字符串在很大程度上取决于实际的数据库引擎 - mysqlpostgresqlsql-serveroracle 或您可能使用的任何其他内容 - 请添加适当的标签
  • SQLite(当然是 v3)。
  • 永远不要将数据存储为逗号分隔的项目。只会给你带来很多麻烦。
  • tbh 存储数据的方式取决于我 - 只要在列中给出列表。显然在现实中我有一个庞大的数据集,它必须以这种形式存储。
  • “只要列表在列内”?为什么?如果每个名称有一行,您可以按国家/地区分组,并且您可以使用基本 SQL 获得所需的任何内容,您将有更好的时间添加或删除名称,简单的不同查询将解决您的问题等。或者单独的表name+country_id,随便吧。

标签: sql sqlite csv


【解决方案1】:

有没有直接的方法通过 SQL 查询来做到这一点?

我相信以下将直接产生唯一名称的计数:-

WITH
    splt(value,rest) AS 
        (
            SELECT 
                substr(names,1,instr(names,',')-1),
                substr(names,instr(names,',')+1)||',' 
            FROM thetable
            UNION ALL SELECT 
                substr(rest,1,instr(rest,',')-1),
                substr(rest,instr(rest,',')+1) 
            FROM splt 
            WHERE length(rest) > 0 
            LIMIT 20 /* just in case limit to 20 iterations increase if more iterations exected */
    ),
    intermediate AS 
        (
            SELECT count(*),
                group_concat(value) 
            FROM splt 
            WHERE length(value) > 0 
            GROUP BY value
        )
SELECT count(*) AS unique_names FROM intermediate;

说明

这假设国家在一个列中,而名称在另一列中,并且列名是名为 thetable

的表中的 names

查询由 2 个 CTE(公用表表达式,基本上是临时表)组成。

第一个名为 splt 的 CTE 是递归的,它将列表中的每个名称提取为一行。

请注意,递归 CTE 必须具有确定何时停止迭代 WHERE 子句或 LIMIT 的方法。在两者都使用的情况下,当提取值的长度大于 0 时,WHERE 子句是停止迭代(基于每个源行)的正确检查。LIMIT 20 是一种预防措施,当然它可能是增加了。

名为 intermediate 的第二个 CTE 然后通过使用 splt CTE 的结果根据值分组来删除长度为 0 的名称和重复项。

最后统计剩余行数。

演示

使用以下来演示:-

DROP TABLE IF EXISTS thetable;
CREATE TABLE IF NOT EXISTS thetable (country TEXT, names TEXT);
INSERT INTO thetable VALUES
    ('Germany','Peter,Jan,David,Florian'),
    ('USA','James,Joe,Bob,David,Alan,George'),
    ('UK','George,Jack,Peter'),
    ('Isreal','David,Moshe,Chaim'),
    /*<<<<< ADDED to test resillience*/
    ('Spain',''), 
    ('France',null),
    ('Italy',zeroblob(100))
;
WITH
    splt(value,rest) AS 
        (
            SELECT 
                substr(names,1,instr(names,',')-1),
                substr(names,instr(names,',')+1)||',' 
            FROM thetable
            UNION ALL SELECT 
                substr(rest,1,instr(rest,',')-1),
                substr(rest,instr(rest,',')+1) 
            FROM splt 
            WHERE length(rest) > 0 
            LIMIT 20 /* just in case limit to 20 iterations increase if more iterations exected */
    ),
    intermediate AS 
        (
            SELECT count(*),
                group_concat(value) 
            FROM splt 
            WHERE length(value) > 0 
            GROUP BY value
        )
SELECT count(*) AS unique_names FROM intermediate;
DROP TABLE IF EXISTS thetable;

结果:-

【讨论】:

  • 难以置信!它完美地工作。非常非常感谢:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-08
  • 2021-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多