如何从聚合查询中排除异常值?

时间:2011-01-17 20:23:16

标签: sql-server

我正在创建一份报告,比较不同单位的总时间和数量。这里简化了我目前正在使用的查询:

SELECT  m.Unit,
        COUNT(*) AS Count,
        SUM(m.TimeInMinutes) AS TotalTime
FROM    main_table m
WHERE   m.unit <> ''
        AND m.TimeInMinutes > 0
GROUP BY m.Unit
HAVING  COUNT(*) > 15

然而,我被告知我需要排除行的时间最高或最低5%的情况,以试图摆脱一些古怪的异常值。 (如在应用聚合之前删除行。)

我该怎么做?

4 个答案:

答案 0 :(得分:9)

您可以使用NTILE

排除上下x百分位数
SELECT m.Unit,
        COUNT(*) AS Count,
        SUM(m.TimeInMinutes) AS TotalTime
FROM    
        (SELECT
             m.Unit,
             NTILE(20) OVER (ORDER BY m.TimeInMinutes) AS Buckets
         FROM
             main_table m
         WHERE
             m.unit <> '' AND m.TimeInMinutes > 0
        ) m
WHERE   
      Buckets BETWEEN 2 AND 19
GROUP BY m.Unit
HAVING  COUNT(*) > 15

编辑:此article也有几种技术

答案 1 :(得分:2)

一种方法是使用not in子句排除异常值:

where  m.ID not in 
       (
       select  top 5 percent ID
       from    main_table 
       order by 
               TimeInMinutes desc
       )

最后5%的另一个not in条款。

答案 2 :(得分:2)

NTile非常不精确。如果您针对下面的示例视图运行NTile,您将看到它从中心捕获一些不确定的行数而不是90%。建议使用TOP 95%,然后反转TOP 90%几乎是正确的,除了90%x 95%只给出原始数据集的85.5%。所以你必须这样做

select top 94.7368 percent *
from (
select top 95 percent *
    from 
    order by .. ASC
) X
order by .. DESC

首先创建一个与您的表列名匹配的视图

create view main_table
as
select type unit, number as timeinminutes from master..spt_values

试试这个

select Unit, COUNT(*), SUM(TimeInMinutes)
FROM
(
    select *,
        ROW_NUMBER() over (order by TimeInMinutes) rn,
        COUNT(*) over () countRows
    from main_table
) N -- Numbered
where rn between countRows * 0.05 and countRows * 0.95
group by Unit, N.countRows * 0.05, N.countRows * 0.95
having count(*) > 20

在删除异常值后,HAVING子句将应用于剩余的集合。 对于1,1,1,1,1,1,2,5,6,19的数据集,使用ROW_NUMBER可以正确地删除1的一个实例。

答案 3 :(得分:0)

我认为最强大的方法是将列表按顺序排序,然后排除顶部和底部的极值。对于一百个值,您将按升序排序并取第一个95 PERCENT,然后按降序排序并取第一个90 PERCENT。