1

背景

我有一个存储过程,它返回大量的用电量测量数据。每条记录由测量的日期和时间以及四个测量值组成。执行测量的速率从几秒到几分钟不等,在我的示例中(以及在我的真实数据中),间隔为 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 的新手,所以我搜索了一下,发现了一种将存储过程转换为表值函数的可能方法,因此编写了一个非常简单的 TVF,它基本上只是调用存储过程并返回一个我可以用来执行另一个SELECTs 的表,但我失败了,因为 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

错误:

消息 443,级别 16,状态 14,过程 fn_get_energy_consumption,第 16 行
在函数中无效使用副作用运算符“INSERT EXEC”。

我还没有尝试过的另一种可能性是使用OPENROWSETwhich 这是我想避免的东西,如果可能的话。

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

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

更新 1

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

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

4

2 回答 2

0

Because I don't have your data or statistical SP, this syntax is slightly off. While my normal instinct is to use a table variable; apparently those are explicitly not allowed. Instead I'm using a temp table.

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

In the above example, I'm not passing any parameters to "SP_STATISTICS_PACKAGE", but that's just laziness on my part. The main point is to demonstrate the syntax for populating a temp table via a nested stored procedure. I'm explicitly dropping my temp table, however it should drop whenever the connection is closed. If you run this USP more than once on the same connection without explicitly dropping it, you will receive a table exists error.
Also, I suggest using something more distinctive as a temp table name than #tbl.

于 2013-08-04T13:19:58.730 回答
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) 可能是更推荐的方法。看起来您每天都会创建一个汇总表。每天运行存储过程以汇总最新数据并将其添加到汇总表中。这假设您不需要最新信息,因此可以接受一天的延迟。

于 2013-08-04T14:13:53.493 回答