【问题标题】:How can I calculate the median of values in SQLite?如何计算 SQLite 中值的中位数?
【发布时间】:2013-03-23 17:43:49
【问题描述】:

我想计算数字行中的中值。我如何在 SQLite 4 中做到这一点?

【问题讨论】:

    标签: sqlite median


    【解决方案1】:

    Dixtroy 通过 group_concat() 提供了最佳解决方案。 这是一个完整的示例:

    DROP TABLE [t];
    CREATE TABLE [t] (name, value INT);
    INSERT INTO t VALUES ('A', 2);
    INSERT INTO t VALUES ('A', 3);
    INSERT INTO t VALUES ('B', 4);
    INSERT INTO t VALUES ('B', 5);
    INSERT INTO t VALUES ('B', 6);
    INSERT INTO t VALUES ('C', 7);
    

    该表的结果:

    name|value
    A|2
    A|3
    B|4
    B|5
    B|6
    C|7
    

    现在我们使用来自 Dextroy 的(稍作修改的)查询:

    SELECT name, --string_list, count, middle,
        CASE WHEN count%2=0 THEN
            0.5 * substr(string_list, middle-10, 10) + 0.5 * substr(string_list, middle, 10)
        ELSE
            1.0 * substr(string_list, middle, 10)
        END AS median
    FROM (
        SELECT name, 
            group_concat(value_string,"") AS string_list,
            count() AS count, 
            1 + 10*(count()/2) AS middle
        FROM (
            SELECT name, 
                printf( '%010d',value) AS value_string
            FROM [t]
            ORDER BY name,value_string
        )
        GROUP BY name
    );
    

    ...得到这个结果:

    name|median
    A|2.5
    B|5.0
    C|7.0
    

    【讨论】:

      【解决方案2】:

      有一个带有时间戳、标签和延迟的日志表。我们希望看到每个标签的延迟中值,按时间戳分组。将所有延迟值格式化为带前导零的 15 个字符长度,将其连接起来,然后将定位值削减一半。.. 有中位数。

      select L, --V, 
             case when C % 2 = 0 then
             ( substr( V, ( C - 1 ) * 15 + 1, 15) * 1 + substr( V, C * 15 + 1, 15) * 1 ) / 2
             else
              substr( V, C * 15 + 1, 15) * 1
             end as MEDST
      from (
          select L, group_concat(ST, "") as V, count(ST) / 2 as C
          from (
              select label as L, 
                     substr( timeStamp, 1, 8) * 1 as T, 
                     printf( '%015d',latency) as ST
              from log
              where label not like '%-%' and responseMessage = 'OK'
              order by L, T, ST ) as XX
          group by L
          ) as YY
      

      【讨论】:

        【解决方案3】:

        有一个用于 sqlite3 的各种数学函数的扩展包。它包括像中位数这样的组函数。

        这将比 CL 的答案做更多的工作,但如果您认为您需要任何其他功能,这可能是值得的。

        http://www.sqlite.org/contrib/download/extension-functions.c?get=25

        Here 是如何编译和加载 SQLite 扩展的指南。)

        来自描述:

        使用可加载扩展机制为 SQL 查询提供数学和字符串扩展函数。数学:acos,asin,atan,atn2,atan2,acosh,asinh,atanh,差异,度,弧度,cos,sin,tan,cot,cosh,sinh,tanh,coth,exp,log,log10,power,sign, sqrt,正方形,天花板,地板,圆周率。字符串:replicate、charindex、leftstr、rightstr、ltrim、rtrim、trim、replace、reverse、proper、padl、padr、padc、strfilter。聚合:stdev、variance、mode、median、lower_quartile、upper_quartile。

        2015-04-12 更新:修复“未定义符号:sinh”

        正如 cmets 中提到的,尽管编译成功,但此扩展可能无法正常工作。

        例如,编译可能有效,在 Linux 上,您可以将生成的 .so 文件复制到 /usr/local/lib。但是来自sqlite3 shell 的.load /usr/local/lib/libsqlitefunctions 可能会产生这个错误:

        Error: /usr/local/lib/libsqlitefunctions.so: undefined symbol: sinh
        

        以这种方式编译似乎可行:

        gcc -fPIC -shared extension-functions.c -o libsqlitefunctions.so -lm
        

        并且将.so 文件复制到/usr/local/lib 没有显示类似错误:

        sqlite> .load /usr/local/lib/libsqlitefunctions
        
        sqlite> select cos(pi()/4.0);
        ---> 0.707106781186548
        

        我不确定为什么 gcc 的选项顺序在这种特殊情况下很重要,但显然它确实如此。

        注意到这一点的功劳归功于Ludvick Lidickythis blog post的评论

        【讨论】:

        • 知道如何安装吗?文件本身没有多大帮助。
        • @jameshfisher 尝试在另一个问题中询问它,这是一个良好的开端。出于好奇,我今晚尝试编译扩展。遵循文件顶部 C cmets 中包含的说明非常简单(您确实阅读了文件并找到了这些说明,对吗?)但也有一些错误。它在 Ubuntu 14.04 LTS 上使用 gcc 编译,具有先决条件“libsqlite3-dev”,从而生成共享库“libsqlitefunctions.so”。当给定命令 SELECT load_extension('./libsqlitefunctions') 时,相同的 Ubuntu 的 sqlite3 尝试加载它,但抛出错误“未定义符号:sinh”。
        • 这比我预期的要容易设置。加油!
        • 这个链接 (github.com/yajirobee/environment/blob/master/common/…) 给出了一个编译好的 so 文件。它有效!
        • @Kassym Dorsel:从sqlite.org/2019/sqlite-amalgamation-3290000.zip 下载上述 extension-functions.c 文件和 sqlite-amalgamation 文件,并将所有文件解压缩到同一个文件夹中。然后安装 MinGw 安装程序并从这里将包“mingw32-base-bin”安装到 c:\MinGW。现在打开 DOS 命令并更改为 c:\MinGW\bin 并运行命令 gcc -g -shared "C:\YourPath\extension-functions.c" -o "C:\YourPath\extension-functions.dll"最后通过 .load C:/YourPath/extension-functions.dll 将 dll 加载到 SQLite 中
        【解决方案4】:

        SELECT AVG(x) 只返回格式为 YYYY-MM-DD 的日期值的年份,因此我稍微调整了 CL 的解决方案以适应日期:

        SELECT DATE(JULIANDAY(MIN(MyDate)) + (JULIANDAY(MAX(MyDate)) - JULIANDAY(MIN(MyDate)))/2) as Median_Date
        FROM (
           SELECT MyDate
              FROM MyTable
              ORDER BY MyDate
              LIMIT 2 - ((SELECT COUNT(*) FROM MyTable) % 2) -- odd 1, even 2
              OFFSET (SELECT (COUNT(*) - 1) / 2 FROM MyTable)
        );
        

        【讨论】:

          【解决方案5】:

          假设中位数是有序列表中间的元素。

          SQLite(4 或 3)没有任何内置函数,但可以手动完成:

          SELECT x
          FROM MyTable
          ORDER BY x
          LIMIT 1
          OFFSET (SELECT COUNT(*)
                  FROM MyTable) / 2
          

          当记录数为偶数时,通常将中位数定义为中间两条记录的平均值。 在这种情况下,可以这样计算平均值:

          SELECT AVG(x)
          FROM (SELECT x
                FROM MyTable
                ORDER BY x
                LIMIT 2
                OFFSET (SELECT (COUNT(*) - 1) / 2
                        FROM MyTable))
          

          将奇数和偶数结合起来会得到这样的结果:

          SELECT AVG(x)
          FROM (SELECT x
                FROM MyTable
                ORDER BY x
                LIMIT 2 - (SELECT COUNT(*) FROM MyTable) % 2    -- odd 1, even 2
                OFFSET (SELECT (COUNT(*) - 1) / 2
                        FROM MyTable))
          

          【讨论】:

          • 这是一个不错的解决方案,但如果您想计算“分组依据”结果而不是整个表的中位数,似乎很难使用它。考虑“按 grp 从表组中选择 grp、min(val)、median(val)、max(val)”。
          • @Acer - 我知道你是对的。在这种情况下,如果没有对 MEDIAN 的数据库支持,我就没有优雅的单语句解决方案。想到的是: 1) 使用 group by 子句和 SELECT INTO(将此表称为“G”)以排序形式创建一个表,并添加一个 AUTOINCREMENT 列(称为列“i”)。 2) 创建一个查询,计算每个组的 (max(G.i)+min(G.i))/2.0(将此列称为“x”)。 3) 使用 Pick 表,从 G 中选择 ABS(G.i-Pick.x)
          • 似乎您可以使用视图而不是实际表格来做同样的事情
          猜你喜欢
          • 2013-12-17
          • 1970-01-01
          • 1970-01-01
          • 2020-09-28
          • 2021-01-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-19
          相关资源
          最近更新 更多