如何从 MySQL 中的逗号分隔字符串值计算中位数?
How to calculate median from comma separated string values in MySQL?
问题是计算分布在 table 中的值的中位数。我在针对相同 uid 的 table 列中有多个值,我需要在其中为每个 uid 找到这些值的中值。为了做到这一点,首先我连接了这些值,然后尝试找到针对每个 uid 的分组值的中值。组连接后,我将值放入逗号分隔列表中。
现在,我需要从 "text_responded_in_hour" 列中的以下 table 中给出的逗号分隔值中找到中位数。
uId |text_responded_in_hour
----|-----------------------
176 |70,660,70
177 |102
194 |102,102
我试过Google但没有找到任何相关的解决方案。
这样做并不可行。由于这样的问题,逗号分隔列表在数据库中通常是一个非常糟糕的主意。
即使不快速或不灵活,也有可能。
下面通过生成从 1 到 1000(或比逗号数多 1)的数字列表来实现。它仅在逗号分隔值的最大数量小于 1000 时才有效。可以轻松扩展以应对更大的数字,但随着它的扩展,效率会变得更低。
然后使用该数字范围从逗号分隔列表中提取值。
一旦完成,就可以使用 AVG 函数了。
SELECT uID,
AVG(individual_responded_in_hour)
FROM
(
SELECT uID,
CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(text_responded_in_hour, ',', (hundreds.aCnt * 100 + tens.aCnt * 10 + units.aCnt + 1)), ',', -1) AS SIGNED) AS individual_responded_in_hour
FROM some_table
CROSS JOIN (SELECT 1 AS aCnt UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10) units
CROSS JOIN (SELECT 1 AS aCnt UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10) tens
CROSS JOIN (SELECT 1 AS aCnt UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10) hundreds
WHERE (hundreds.aCnt * 100 + tens.aCnt * 10 + units.aCnt) <= (LENGTH(text_responded_in_hour) - LENGTH((REPLACE(text_responded_in_hour, ',', '')))
) sub0
GROUP BY uID
可以使用自定义编写的 MySQL 函数来执行此操作,这可能更有效。
但无论哪种方式,我都认为这样做的复杂性是不将值存储在逗号分隔列表中的一个很好的理由。
你真的应该使用编程语言,比如 python,来做到这一点。你不能用 MySQL 轻松做到这一点,你可以用 postgres 或 MSSQL 或任何其他 dbms 来做到这一点。
在 MySQL 中,如果您知道有多少个值,则可以使用 json 函数破解它。但这看起来不对(适用于 MySQL 5.7.9+):
insert into yourtable
select uId as id, concat('[', text_responded_in_hour, ']') as jsoncol
from startingtable;
select ID, AVG(val)
(
select id, jsoncol->'$[0]' as val from yourtable
union all select id, jsoncol->'$[1]' from yourtable
union all select id, jsoncol->'$[2]' from yourtable
-- as many times as needed
) as a
group by ID
以下用于计算中位数的代码适用于偶数和奇数个值。这段代码达到了目的:)
SELECT
uId,
date,
(SUBSTRING_INDEX(
SUBSTRING_INDEX(
GROUP_CONCAT(responded_text_time_in_hour ORDER BY responded_text_time_in_hour),
',',
((
ROUND(
LENGTH(GROUP_CONCAT(responded_text_time_in_hour)) -
LENGTH(
REPLACE(
GROUP_CONCAT(responded_text_time_in_hour),
',',
''
)
)
) / 2) + 1
)),
',',
-1
) +
SUBSTRING_INDEX(
SUBSTRING_INDEX(
GROUP_CONCAT(responded_text_time_in_hour ORDER BY responded_text_time_in_hour), ',', (COUNT(*)/2) )
, ',', -1))/2 as median
FROM outTable
WHERE
(responded_text_time_in_hour>0 AND responded_text_time_in_hour <=3600)
GROUP BY 1,2
问题是计算分布在 table 中的值的中位数。我在针对相同 uid 的 table 列中有多个值,我需要在其中为每个 uid 找到这些值的中值。为了做到这一点,首先我连接了这些值,然后尝试找到针对每个 uid 的分组值的中值。组连接后,我将值放入逗号分隔列表中。
现在,我需要从 "text_responded_in_hour" 列中的以下 table 中给出的逗号分隔值中找到中位数。
uId |text_responded_in_hour
----|-----------------------
176 |70,660,70
177 |102
194 |102,102
我试过Google但没有找到任何相关的解决方案。
这样做并不可行。由于这样的问题,逗号分隔列表在数据库中通常是一个非常糟糕的主意。
即使不快速或不灵活,也有可能。
下面通过生成从 1 到 1000(或比逗号数多 1)的数字列表来实现。它仅在逗号分隔值的最大数量小于 1000 时才有效。可以轻松扩展以应对更大的数字,但随着它的扩展,效率会变得更低。
然后使用该数字范围从逗号分隔列表中提取值。
一旦完成,就可以使用 AVG 函数了。
SELECT uID,
AVG(individual_responded_in_hour)
FROM
(
SELECT uID,
CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(text_responded_in_hour, ',', (hundreds.aCnt * 100 + tens.aCnt * 10 + units.aCnt + 1)), ',', -1) AS SIGNED) AS individual_responded_in_hour
FROM some_table
CROSS JOIN (SELECT 1 AS aCnt UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10) units
CROSS JOIN (SELECT 1 AS aCnt UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10) tens
CROSS JOIN (SELECT 1 AS aCnt UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10) hundreds
WHERE (hundreds.aCnt * 100 + tens.aCnt * 10 + units.aCnt) <= (LENGTH(text_responded_in_hour) - LENGTH((REPLACE(text_responded_in_hour, ',', '')))
) sub0
GROUP BY uID
可以使用自定义编写的 MySQL 函数来执行此操作,这可能更有效。
但无论哪种方式,我都认为这样做的复杂性是不将值存储在逗号分隔列表中的一个很好的理由。
你真的应该使用编程语言,比如 python,来做到这一点。你不能用 MySQL 轻松做到这一点,你可以用 postgres 或 MSSQL 或任何其他 dbms 来做到这一点。 在 MySQL 中,如果您知道有多少个值,则可以使用 json 函数破解它。但这看起来不对(适用于 MySQL 5.7.9+):
insert into yourtable
select uId as id, concat('[', text_responded_in_hour, ']') as jsoncol
from startingtable;
select ID, AVG(val)
(
select id, jsoncol->'$[0]' as val from yourtable
union all select id, jsoncol->'$[1]' from yourtable
union all select id, jsoncol->'$[2]' from yourtable
-- as many times as needed
) as a
group by ID
以下用于计算中位数的代码适用于偶数和奇数个值。这段代码达到了目的:)
SELECT
uId,
date,
(SUBSTRING_INDEX(
SUBSTRING_INDEX(
GROUP_CONCAT(responded_text_time_in_hour ORDER BY responded_text_time_in_hour),
',',
((
ROUND(
LENGTH(GROUP_CONCAT(responded_text_time_in_hour)) -
LENGTH(
REPLACE(
GROUP_CONCAT(responded_text_time_in_hour),
',',
''
)
)
) / 2) + 1
)),
',',
-1
) +
SUBSTRING_INDEX(
SUBSTRING_INDEX(
GROUP_CONCAT(responded_text_time_in_hour ORDER BY responded_text_time_in_hour), ',', (COUNT(*)/2) )
, ',', -1))/2 as median
FROM outTable
WHERE
(responded_text_time_in_hour>0 AND responded_text_time_in_hour <=3600)
GROUP BY 1,2