我有一个存储过程,它返回一大组耗电量测量数据。每条记录包括测量的日期和时间以及测量的四个值。执行测量的速率范围从几秒到几分钟,在我的示例中(以及在我的实际数据中)间隔为15分钟,但可能更低。
由于数据的存储方式(所有测量值都是以原始格式压缩并存储在单列中),我正在使用的存储过程是调用外部程序集来处理数据并返回结果集。
EXEC dbo.sp_get_energy_consumption @Identify, @StartTime, @EndTime, @Args
现在存储过程需要一些参数。 @Identify
是int
,代表测量设备的标识符。 @Args
为nvarchar
,表示四个测量值中的哪一个将包含在结果集中。 @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个月),所有四个测量值(它只花了一半时间,只选择了两个)并且如果没有在某些表格或其他东西中缓存它,我无能为力。
但是现在我很乐意弄清楚如何从存储过程中获取表格。
由于存储过程的性能不佳,我正在考虑编写周期性任务执行长时间运行的CLR存储过程(sp_get_energy_consumption
)并将结果保存到常规表中作为缓存。通过这种方式,我将通过使用表值函数实现我所能得到的,并且执行时间更短,以便以后进行查询。
就目前而言,我想到的唯一缺点就是放弃访问实时数据,因为周期性任务执行之间的间隔总会有一些延迟。
答案 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)可能是更推荐的方法。看起来您每天都会创建一个汇总表。每天运行存储过程以汇总最新数据,并将其添加到汇总表中。这假设您不需要最新的信息,因此可以接受一天的延迟。