【问题标题】:sqlite: get the average of the top X% for every itemsqlite:获取每个项目的前 X% 的平均值
【发布时间】:2013-04-08 11:42:04
【问题描述】:

是否可以获得组中前 X% 项的平均值?

例如:
我有一个表,其中包含 item_id、时间戳和价格列。输出应按 item_id 和时间戳分组,“价格列”应取平均值。对于平均而言,应仅使用该组中最低 X% 的价格。

我发现了类似的问题 (How to select top x records for every group),但这不适用于 sqlite。

【问题讨论】:

  • “最低 X% 价格”是什么意思?百分比的依据是什么?价格?数数?你能举个例子吗?
  • 按时间戳分组有意义吗?您能否更清楚地说明您想要实现的目标——查询应该返回什么?
  • @Tim 是的,在我的情况下这是有道理的,因为有许多项目具有完全相同的时间戳(因为它们是作为批次插入/更新的)
  • @CL 它是基于记录的计数(抱歉造成误解)

标签: sql sqlite aggregate-functions


【解决方案1】:

获取每个组中的前 n 条记录需要计数。假设没有重复项,以下查询返回项目的记录数:

select t.*,
       (select count(*) from t t2 where t2.item_id = t.item_id
       ) as NumPrices
from t

这称为相关子查询。现在,让我们扩展这个想法,包括一个排名,然后计算正确组的平均值:

select item_id, avg(price)
from (select t.*,
             (select count(*) from t t2 where t2.item_id = t.item_id
             ) as NumPrices,
             (select count(*) from t t2 where t2.item_id = t.item_id and t2.price <= t.price
             ) as PriceRank
      from t
     ) t
where (100.0*PriceRank / NumPrices) <= X
group by item_id

为了提高性能,您需要在(item_id, price) 上建立索引。

【讨论】:

  • 我已经尝试过了——虽然查询本身是有意义的——但速度非常慢(10 分钟后没有完成)。我的表有 ~1000000 条记录,它们应该被分组到 ~8000 条记录。如果没有更好的答案,无论如何我都会接受答案
  • 目前只有价格列的索引
  • @丹尼尔。 . .尝试使用建议的索引。
  • @GordonLinoff 感谢您的建议,但它仍然很慢。当我删除“where-clause”时,我得到了快速的结果,但可能是它仍在后台计算或其他什么。
【解决方案2】:

要获取 ID I 和时间戳 T 的组中的记录数,请使用以下查询:

SELECT COUNT(*)
FROM MyTable
WHERE item_id = I
  AND timestamp = T

要得到限制,乘以X,然后使用ROUND/CAST 转换为整数:

SELECT CAST(ROUND(COUNT(*) * X / 100) AS INTEGER)
FROM MyTable
WHERE item_id = I
  AND timestamp = T

要获取特定组中在该限制范围内的所有记录,请按价格对组中的记录进行排序,并限制返回的计数:

SELECT *
FROM MyTable
WHERE item_id = I
  AND timestamp = T
ORDER BY price
LIMIT (SELECT CAST(ROUND(COUNT(*) * X / 100) AS INTEGER)
       FROM MyTable
       WHERE item_id = I
         AND timestamp = T)

理论上,要获得组平均值,请在其周围添加GROUP BY

SELECT item_id,
       timestamp,
       (SELECT AVG(price)
        FROM (SELECT price
              FROM MyTable T2
              WHERE T2.item_id = T1.item_id
                AND T2.timestamp = T1.timestamp
              ORDER BY price
              LIMIT (SELECT CAST(ROUND(COUNT(*) * X / 100) AS INTEGER)
                     FROM MyTable T3
                     WHERE T3.item_id = T1.item_id
                       AND T3.timestamp = T1.timestamp)
             )
       ) AS AvgPriceLowestX
FROM MyTable T1
GROUP BY item_id,
         timestamp

但是,SQLite 似乎不允许从LIMIT 子句内部访问相关变量,因此这在实践中不起作用。 您必须获取所有组的 ID (SELECT DISTINCT item_id, timestamp FROM MyTable) 并为每个组执行上面的第三个查询。

无论如何,请确保在item_idtimestampprice 三列上都有一个索引以获得良好的性能。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-23
    • 1970-01-01
    • 2021-01-17
    • 2021-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多