SQL 如何计算不基于行的中位数
SQL how to calculate median not based on rows
我的 table 中有一个汽车样本,我想用 SQL 计算我的样本的中位数价格。最好的方法是什么?
+-----+-------+----------+
| Car | Price | Quantity |
+-----+-------+----------+
| A | 100 | 2 |
| B | 150 | 4 |
| C | 200 | 8 |
+-----+-------+----------+
我知道我可以使用 percentile_cont(或 percentile_disc),如果我的 table 是这样的:
+-----+-------+
| Car | Price |
+-----+-------+
| A | 100 |
| A | 100 |
| B | 150 |
| B | 150 |
| B | 150 |
| B | 150 |
| C | 200 |
| C | 200 |
| C | 200 |
| C | 200 |
| C | 200 |
| C | 200 |
| C | 200 |
| C | 200 |
+-----+-------+
但在现实世界中,我的第一个 table 有大约 1 亿行,第二个 table 应该有大约 3 个台球行(而且我不知道如何转换我的第一个table进入第二个)。
这在少数结果上看起来是正确的,但尝试更大的结果集 double-check。
首先创建一个 table,其中包含每辆车的总数(或使用 CTE 或 sub-query),您可以选择。我只是在这里创建一个单独的 table。
create table table2 as
(
select car,
quantity,
price,
price * quantity as total
from table1
)
然后运行这个查询,查找落在中间的价格组。
select price
from (
select car, price,
sum(total) over (order by car) as rollsum,
sum(total) over () as total
from table2
)a
where rollsum >= total/2
正确地 returns 价值 200 美元。
这是在 sql 服务器
中执行此操作的方法
在第一步中,我做的是计算对应于中位数下限和上限的索引(如果我们有奇数个元素,则下限和上限相同,否则它基于 x/2 和 x/2+第 1 个值)
然后得到数量的累加和,用它来选择下限和上限对应的元素如下
with median_dt
as (
select case when sum(quantity)%2=0 then
sum(quantity)/2
else
sum(quantity)/2 + 1
end as lower_limit
,case when sum(quantity)%2=0 then
(sum(quantity)/2) + 1
else
sum(quantity)/2 + 1
end as upper_limit
from t
)
,data
as (
select *,sum(quantity) over(order by price asc) as cum_sum
from t
)
,rnk_val
as(select *
from (
select price,row_number() over(order by d.cum_sum asc) as rnk
from data d
join median_dt b
on b.lower_limit<=d.cum_sum
)x
where x.rnk=1
union all
select *
from (
select price,row_number() over(order by d.cum_sum asc) as rnk
from data d
join median_dt b
on b.upper_limit<=d.cum_sum
)x
where x.rnk=1
)
select avg(price) as median
from rnk_val
+--------+
| median |
+--------+
| 200 |
+--------+
db fiddle link
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=c5cfa645a22aa9c135032eb28f1749f6
我的 table 中有一个汽车样本,我想用 SQL 计算我的样本的中位数价格。最好的方法是什么?
+-----+-------+----------+
| Car | Price | Quantity |
+-----+-------+----------+
| A | 100 | 2 |
| B | 150 | 4 |
| C | 200 | 8 |
+-----+-------+----------+
我知道我可以使用 percentile_cont(或 percentile_disc),如果我的 table 是这样的:
+-----+-------+
| Car | Price |
+-----+-------+
| A | 100 |
| A | 100 |
| B | 150 |
| B | 150 |
| B | 150 |
| B | 150 |
| C | 200 |
| C | 200 |
| C | 200 |
| C | 200 |
| C | 200 |
| C | 200 |
| C | 200 |
| C | 200 |
+-----+-------+
但在现实世界中,我的第一个 table 有大约 1 亿行,第二个 table 应该有大约 3 个台球行(而且我不知道如何转换我的第一个table进入第二个)。
这在少数结果上看起来是正确的,但尝试更大的结果集 double-check。
首先创建一个 table,其中包含每辆车的总数(或使用 CTE 或 sub-query),您可以选择。我只是在这里创建一个单独的 table。
create table table2 as
(
select car,
quantity,
price,
price * quantity as total
from table1
)
然后运行这个查询,查找落在中间的价格组。
select price
from (
select car, price,
sum(total) over (order by car) as rollsum,
sum(total) over () as total
from table2
)a
where rollsum >= total/2
正确地 returns 价值 200 美元。
这是在 sql 服务器
中执行此操作的方法在第一步中,我做的是计算对应于中位数下限和上限的索引(如果我们有奇数个元素,则下限和上限相同,否则它基于 x/2 和 x/2+第 1 个值)
然后得到数量的累加和,用它来选择下限和上限对应的元素如下
with median_dt
as (
select case when sum(quantity)%2=0 then
sum(quantity)/2
else
sum(quantity)/2 + 1
end as lower_limit
,case when sum(quantity)%2=0 then
(sum(quantity)/2) + 1
else
sum(quantity)/2 + 1
end as upper_limit
from t
)
,data
as (
select *,sum(quantity) over(order by price asc) as cum_sum
from t
)
,rnk_val
as(select *
from (
select price,row_number() over(order by d.cum_sum asc) as rnk
from data d
join median_dt b
on b.lower_limit<=d.cum_sum
)x
where x.rnk=1
union all
select *
from (
select price,row_number() over(order by d.cum_sum asc) as rnk
from data d
join median_dt b
on b.upper_limit<=d.cum_sum
)x
where x.rnk=1
)
select avg(price) as median
from rnk_val
+--------+
| median |
+--------+
| 200 |
+--------+
db fiddle link https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=c5cfa645a22aa9c135032eb28f1749f6