如何在SQLite中计算中值?

mznpcxlj  于 2022-11-24  发布在  SQLite
关注(0)|答案(6)|浏览(252)

我想计算一个数字行的中值。我该如何在SQLite 4中实现呢?

yhqotfr8

yhqotfr81#

假设中值是有序列表中间的元素。
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))
41ik7eoe

41ik7eoe2#

sqlite3有一个包含各种数学函数的扩展包,它包括像median这样的组函数。
这将是更多的工作得到这比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、幂、符号、平方、平方、天花板、地板、圆周率。字符串:复制、字符索引、左字符串、右字符串、ltrim、rtrim、修剪、替换、反转、正确、padl、padr、padc、字符串过滤器。聚合:标准差、方差、众数、中位数、下四分位数、上四分位数。

更新日期:2015年4月12日:修复“未定义符号:辛”

正如在注解中提到的,尽管编译成功,但此扩展可能无法正常工作。
例如,编译可能会正常工作,在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的评论

mo49yndu

mo49yndu3#

有一个包含时间戳,标签和延迟的日志表。我们希望查看每个标签的延迟中值,按时间戳分组。将所有延迟值的格式设置为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
tkclm6bt

tkclm6bt4#

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
okxuctiv

okxuctiv5#

如果您使用PDO,那么Paul答案中使用的::loadExtension()可能对您不可用。
假设您使用的是PHP,则create an aggregate function是一种替代方法。

$pdo_handle->sqliteCreateAggregate(
    'median', // the name of the function to declare
    function($context, $row_number, $value){ // a method called for each row
        $context[] = $value; // store the values
        return $context; 
    },
    function($context, $row_count){ // a method called once all row have been iterated over
        // sort the values
        sort($context, SORT_NUMERIC);
        // cound the number of values
        $count = count($context);
        // get the mid point of array (lowest one)
        $middle = floor($count/2);
        // if there is an even amount of values
        if (($count % 2) == 0) {
            // average the two middle values to find the median
            return ($context[$middle--] + $context[$middle])/2;        
        } else {
            // odd amount of elements, so the median value is simply the one in the middle
            return $context[$middle];
        }    
    },
    1
);

然后,您可以自由地执行

SELECT median("column_name") FROM "table_name";

类似的“create函数”可能在其他语言中可用。

vsikbqxv

vsikbqxv6#

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)
);

相关问题