【问题标题】:SQL how to calculate median not based on rowsSQL如何计算不基于行的中位数
【发布时间】:2020-09-28 15:41:27
【问题描述】:

我的表中有一个汽车样本,我想使用 SQL 计算我的样本的中位价格。最好的方法是什么?

+-----+-------+----------+
| Car | Price | Quantity |
+-----+-------+----------+
| A   |   100 |        2 |
| B   |   150 |        4 |
| C   |   200 |        8 |
+-----+-------+----------+

如果我的表是这样的,我知道我可以使用 percentile_cont(或 percentile_disc):

+-----+-------+
| 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 |
+-----+-------+

但在现实世界中,我的第一个表大约有 1 亿行,第二个表应该有大约 3 个台球行(而且我不知道如何将我的第一个表转换为第二个)。

【问题讨论】:

  • 你试过了吗? sisense.com/blog/medians-in-sql
  • 所有方法都适用于我的第二张桌子,而不是第一张
  • jarlh,这将是平均价格而不是中位数
  • 请标记您正在使用的数据库
  • George Joseph,我有两个数据库 - Oracle 和 SQL Server

标签: sql sql-server median


【解决方案1】:

这是在 sql server 中执行此操作的一种方法

在第一步中,我要做的是计算与中位数的下限和上限相对应的索引(如果我们有奇数个元素,那么下限和上限相同,否则它基于 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 小提琴链接 https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=c5cfa645a22aa9c135032eb28f1749f6

【讨论】:

    【解决方案2】:

    这在少数结果上看起来是正确的,但请尝试在更大的集合上进行仔细检查。

    首先创建一个表,其中包含每辆车的总数(或使用 CTE 或子查询),您可以选择。我只是在这里创建一个单独的表。

        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
    

    正确返回 200 美元。

    【讨论】:

    • 我认为如果相同的值有超过一半的行数,这将不起作用....
    • 我使用了相同的方法和额外的步骤,我选择最接近 50% 的价格组。但是我想使用像 PERCINTILE_CONT 这样的线性插值,并且我希望它在我的样本中只有一个点时工作。
    猜你喜欢
    • 1970-01-01
    • 2015-07-03
    • 2018-06-03
    • 2011-01-14
    • 2021-12-26
    • 2013-01-20
    • 2019-11-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多