SQL Server:存储过程结果的统计函数?

时间:2013-08-04 12:40:36

标签: sql-server stored-procedures user-defined-functions

背景

我有一个存储过程,它返回一大组耗电量测量数据。每条记录包括测量的日期和时间以及测量的四个值。执行测量的速率范围从几秒到几分钟,在我的示例中(以及在我的实际数据中)间隔为15分钟,但可能更低。

由于数据的存储方式(所有测量值都是以原始格式压缩并存储在单列中),我正在使用的存储过程是调用外部程序集来处理数据并返回结果集。

实施例

EXEC dbo.sp_get_energy_consumption @Identify, @StartTime, @EndTime, @Args

现在存储过程需要一些参数。 @Identifyint,代表测量设备的标识符。 @Argsnvarchar,表示四个测量值中的哪一个将包含在结果集中。 @StartTime@EndTime都非常简单,它们是datetime,用于限制记录的范围。

存储过程本身定义为:

CREATE PROCEDURE sp_get_energy_consumption
    @identify int,
    @startTime datetime,
    @endTime datetime,
    @args nvarchar(60)
AS
    EXTERNAL NAME Procedury.StoredProcedures.akd_energy_consumption_list

执行时,根据参数,结果可能如下所示。

EXEC dbo.sp_get_energy_consumption 1, N'2013-01-08 00:00:00', N'2013-01-09 00:00:00', N'i,e'

ID   | Time                | V1     | V2    | V3    | V4    |
1    | 2013-01-08 15:30:00 | 111.42 | 0.24  | NULL  | NULL  |
2    | 2013-01-08 15:45:00 | 111.90 | 0.24  | NULL  | NULL  |
3    | 2013-01-08 16:00:00 | 112.34 | 0.24  | NULL  | NULL  |
4    | 2013-01-08 16:15:00 | 112.96 | 0.24  | NULL  | NULL  |
...

问题

我即将开发的Web应用程序应以表示所选日期范围的图表形式显示这些数据。我还必须根据日期范围和图表比例按小时,天,周或月分组记录,因为将大约3,000条记录转移到客户端只是为了呈现一些小的一个月图表是不行的。例如,我必须减少数字并计算一个月或一周中每天的最小值,最大值,平均值和标准差。

我是SQL Server的新手,所以我用Google搜索了一下,我发现了一种可能的方法将存储过程转换为表值函数,所以写了一个非常简单的TVF,基本上只调用存储过程并返回一个我可以用来执行另一个SELECT的表,但是我失败了,因为SQL Server不允许我INSERT EXEC进入TVF结果表。

CREATE FUNCTION dbo.fn_get_energy_consumption(@Identify int, @StartTime datetime, @EndTime datetime, @Args nvarchar(30))
RETURNS @ConsumptionList TABLE
(
    Id INT IDENTITY,
    Time DATETIME,
    V1 FLOAT NULL,
    V2 FLOAT NULL,
    V3 FLOAT NULL,
    V4 FLOAT NULL
)
AS
BEGIN
    INSERT @ConsumptionList
    EXEC dbo.sp_get_energy_consumption @Identify, @StartTime, @EndTime, @Args

    RETURN
END
GO

错误:

  

Msg 443,Level 16,State 14,Procedure fn_get_energy_consumption,第16行
  在函数中无效使用副作用运算符'INSERT EXEC'。

我没有尝试过的另一种可能是使用OPENROWSET,如果可能,我想避免这种情况。

我也有点担心整体性能,因为在我3岁的四核工作站上执行存储过程调用需要大约5分钟,返回大约6,800条记录(差不多2.5个月),所有四个测量值(它只花了一半时间,只选择了两个)并且如果没有在某些表格或其他东西中缓存它,我无能为力。

但是现在我很乐意弄清楚如何从存储过程中获取表格。

更新1

由于存储过程的性能不佳,我正在考虑编写周期性任务执行长时间运行的CLR存储过程(sp_get_energy_consumption)并将结果保存到常规表中作为缓存。通过这种方式,我将通过使用表值函数实现我所能得到的,并且执行时间更短,以便以后进行查询。

就目前而言,我想到的唯一缺点就是放弃访问实时数据,因为周期性任务执行之间的间隔总会有一些延迟。

2 个答案:

答案 0 :(得分:0)

由于我没有您的数据或统计SP,因此这种语法略有不同。 虽然我的正常本能是使用表变量;显然那些是明确禁止的。相反,我使用临时表。

ALTER PROCEDURE [dbo].[STEVETEST] 
-- Add the parameters for the stored procedure here
@Identify INT,
@StartTime DATETIME,
@EndTime Datetime,
@args nvarchar(30)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
CREATE TABLE #tbl (ID INT IDENTITY, IDEN INT, TIME1 DATETIME, V1 FLOAT NULL, V2 FLOAT NULL, V3 FLOAT NULL, V4 FLOAT NULL)
INSERT INTO #tbl EXEC SP_STATITISTICS_PACKAGE
SELECT * FROM #tbl
DROP TABLE #tbl

在上面的示例中,我没有将任何参数传递给" SP_STATISTICS_PACKAGE",但这只是我的懒惰。 重点是演示通过嵌套存储过程填充临时表的语法。我明确地删除了我的临时表,但是只要连接关闭它就会丢弃。如果您在同一连接上多次运行此USP而未明确删除它,您将收到一个表存在错误 另外,我建议使用比#tbl更具特色的临时表名称。

答案 1 :(得分:0)

以下是一些需要考虑的方法:

(1)使用返回表的存储过程,但始终将其称为:

insert into xxx
    exec(. . .)

然后您可以重复使用该表,一次用于向用户显示,再次用于计算统计数据。

(2)有一个规范表,将在存储过程运行时填充。所以存储过程本质上变成:

truncate table xxx
do work
insert into xxx(...)
    <whatever should go here>

这适用于单用户系统,您无需担心两个用户互相干扰。

(3)传入表名并在存储过程中填充它。但是,只有在应用程序已经广泛使用动态SQL时才应该这样做。在大多数情况下,强烈建议不要这样做。

(4)(2的变化)保持所有跑步的历史。在表中包含一个runid并传回runid。然后代码看起来像:

insert into runHistory(@RunId, . . .)
    select @RunId,  . . .

存储过程返回@RunId,然后由应用程序使用。通过适当的索引,表的大小应该对性能影响很小。另外,你可以保留历史。

(5)可能是更推荐的方法。看起来您每天都会创建一个汇总表。每天运行存储过程以汇总最新数据,并将其添加到汇总表中。这假设您不需要最新的信息,因此可以接受一天的延迟。