【问题标题】:Group data without changing query flow在不更改查询流程的情况下分组数据
【发布时间】:2015-03-28 07:47:34
【问题描述】:

对我来说很难解释我想要什么,所以文章名称可能不清楚,但我希望我能用代码描述它。

我有一些数据有两个最重要的值,所以让它成为时间t 和值f(t)。它存储在表中,例如

1 - 1000
2 - 1200
3 - 1100
4 - 1500
...

我想用它来绘制一个图表,这个图表应该包含 N 个点。如果 table 的行数少于这个 N,那么我们只返回这个 table。但是如果没有,我们应该将这些点分组,例如N = Count/2,那么对于上面的示例:

1 - (1000+1200)/2 = 1100
2 - (1100+1500)/2 = 1300
...

我写了一个 SQL 脚本(它适用于 N >> Count)(MonitoringDateTime - 是t,如果是f(t),则为ResultCount)

ALTER PROCEDURE [dbo].[usp_GetRequestStatisticsData]
    @ResourceTypeID bigint,        
    @DateFrom datetime,         
    @DateTo datetime,            
    @EstimatedPointCount int
AS

BEGIN   
    SET NOCOUNT ON;
    SET ARITHABORT ON; 


    declare @groupSize int;  
    declare @resourceCount int;

    select @resourceCount = Count(*)
    from ResourceType
    where ID & @ResourceTypeID > 0


    SELECT d.ResultCount        
          ,MonitoringDateTime = d.GeneratedOnUtc
          ,ResourceType = a.ResourceTypeID,
          ROW_NUMBER() OVER(ORDER BY d.GeneratedOnUtc asc) AS Row
    into #t
    FROM dbo.AgentData d
      INNER JOIN dbo.Agent a ON a.CheckID = d.CheckID
    WHERE d.EventType = 'Result' AND
          a.ResourceTypeID & @ResourceTypeID > 0 AND
          d.GeneratedOnUtc between @DateFrom AND @DateTo AND
          d.Result = 1


    select @groupSize = Count(*) / (@EstimatedPointCount * @resourceCount)
    from #t

    if @groupSize = 0 -- return all points

        select ResourceType, MonitoringDateTime, ResultCount
        from #t

    else

        select ResourceType,   CAST(AVG(CAST(#t.MonitoringDateTime AS DECIMAL( 18, 6))) AS DATETIME) MonitoringDateTime, AVG(ResultCount) ResultCount
        from #t 
        where [Row] % @groupSize = 0 
        group by ResourceType, [Row]
        order by MonitoringDateTime
END

,但它不适用于 N ~= Count,并且花费大量时间进行插入。 这就是为什么我想使用CTE's,但它不适用于if else 语句。

所以我计算了一个组号的公式(用于 GroupBy 子句),因为我们有

GroupNumber = Count < N ? Row : Row*NumberOfGroups

其中 Count - 表中的行数,并且 NumberOfGroups = Count/EstimatedPointCount

使用一些简单的数学我们得到一个公式

GroupNumber = Row + (Row*Count/EstimatedPointCount - Row)*MAX(Count - Count/EstimatedPointCount,0)/(Count - Count/EstimatedPointCount)

但由于 Count 聚合函数,它不起作用:

Column 'dbo.AgentData.ResultCount' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.

我的英语很差,我知道(我正在努力提高它),但希望最后死去,所以请指教。


查询结果

SELECT d.ResultCount        
          , MonitoringDateTime = d.GeneratedOnUtc
          , ResourceType = a.ResourceTypeID
    FROM dbo.AgentData d
      INNER JOIN dbo.Agent a ON a.CheckID = d.CheckID
    WHERE   d.GeneratedOnUtc between '2015-01-28' AND '2015-01-30' AND
            a.ResourceTypeID & 1376256 > 0 AND
            d.EventType = 'Result' AND   
            d.Result = 1

https://onedrive.live.com/redir?resid=58A31FC352FC3D1A!6118&authkey=!AATDebemNJIgHoo&ithint=file%2ccsv

【问题讨论】:

  • 你能放一些样本数据,这样我就可以对它运行查询,然后我们就可以开始工作了。
  • 本质上我们需要实现类似Bresenham's line algorithm
  • 您能否确认其余代码的行为与您希望的一样,并且您只需要else 语句中的部分的帮助?如果是这样,我会选择@Damien_The_Unbeliever 的答案的一些变体。你只需要一些伪值来分组。
  • @GB 我还没有尝试整个解决方案,但它是最好的,而且我得到了一些新信息。所以我将其标记为答案,感谢您的帮助,这是无价的

标签: sql sql-server performance sql-server-2008 sql-server-2008-r2


【解决方案1】:

这是一个使用NTILE 的示例,您的问题顶部的简单示例数据:

declare @samples table (ID int, sample int)
insert into @samples (ID,sample) values
(1,1000),
(2,1200),
(3,1100),
(4,1500)

declare @results int
set @results = 2

;With grouped as (
    select *,NTILE(@results) OVER (order by ID) as nt
    from @samples
)
select nt,AVG(sample) from grouped
group by nt

产生:

nt                   
-------------------- -----------
1                    1100
2                    1300

如果将 @results 更改为 4(或任何更高的数字),那么您只需返回原始结果集。

很遗憾,我没有您的完整数据,也无法完全理解您要对完整存储过程做什么,因此可能需要对上述内容进行一些调整。

【讨论】:

  • NTILE 是一个很好的解决方案,但并不理想。如果分区中的行数不能被 integer_expression 整除,这将导致两个大小的组相差一个成员。较大的组按 OVER 子句指定的顺序较小的组之前出现。例如,如果总行数为 53,组数为 5,则前三个组将有 11 行,其余两个组将各有 10 行。理想情况下(至少对我而言,作者可能会有不同的想法),较大的组会与较小的组均匀地洗牌:LsLsL,而不是LLLss
【解决方案2】:

我没试过,但不如代替

select ResourceType,   CAST(AVG(CAST(#t.MonitoringDateTime AS DECIMAL( 18, 6))) AS DATETIME) MonitoringDateTime, AVG(ResultCount) ResultCount
        from #t 
        where [Row] % @groupSize = 0 
        group by ResourceType, [Row]
        order by MonitoringDateTime

可能是这样的

select ResourceType,   CAST(AVG(CAST(#t.MonitoringDateTime AS DECIMAL( 18, 6))) AS DATETIME) MonitoringDateTime, AVG(ResultCount) ResultCount
        from #t 
        group by ResourceType, convert(int,[Row]/@groupSize)
        order by MonitoringDateTime

也许这会为您指明新的方向?通过转换为 int,我们将截断小数点后的所有内容,所以我希望这会给你一个更好的分组?您可能需要将您的行号放在资源类型上才能使其正常工作?

【讨论】:

  • 晚饭后我会回来的:-)
  • 是的,我知道这段代码会从一组中挑选第一个项目,但正如我所说,这段代码有很多问题,我正在寻找一种解决方案来消除它们。感谢您的帮助。
猜你喜欢
  • 2019-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-19
  • 2019-12-27
  • 1970-01-01
  • 2021-05-27
  • 1970-01-01
相关资源
最近更新 更多