动态内部查询

时间:2012-03-01 22:50:55

标签: sql-server sql-server-2005

有没有办法编写动态内部查询?基本上,我发现自己一遍又一遍地输入类似下面的查询:

;with tempData as (
   --this inner query is the part that changes, but there's always a timeGMT column.
   select timeGMT, dataCol2, dataCol3
   from tbl1 t1
      join tbl2 t2 on t1.ID=t2.ID
)
select dateadd(ss,d.gmtOffset,t.timeGMT) timeLocal,
   t.*
from tempData t
   join dst d on t.timeGMT between d.sTimeGMT and d.eTimeGMT
where d.zone = 'US-Eastern'

我唯一能想到的是存储过程,内部查询文本作为一些动态sql的输入...但是,我对优化器的理解(无可否认,有限)说这不是真的一个好主意。

2 个答案:

答案 0 :(得分:1)

从性能的角度来看,你所拥有的是我希望优化器能够做到最好的版本。

如果示例的“外部”部分是静态的,代码维护会覆盖性能,我会将dateadd结果封装在表值函数(TVF)中。由于时间转换是这些查询中的常见线程,因此我肯定会关注工作负载的这一部分。

例如,您的查询可能会有所不同:

select timeGMT, dataCol2, dataCol3, lt.timeLocal
from   tbl1 t1
       join tbl2 t2 on t1.ID = t2.ID
       cross apply dbo.LocalTimeGet(timeGMT, 'US-Eastern') AS lt

TVF dbo.LocalTimeGet包含dateadd(ss,d.gmtOffset,t.timeGMT)的逻辑,并根据时区名称查找时区偏移值。该函数的实现类似于:

CREATE FUNCTION dbo.LocalTimeGet (
    @TimeGMT  datetime,
    @TimeZone varchar(20)
)
RETURNS TABLE
AS
RETURN (
    SELECT  DATEADD(ss, d.gmtOffset, @TimeGMT) AS timeLocal
    FROM    dst AS d
    WHERE   d.zone = @TimeZone
);
GO

这种方法的优点是,当您升级到2008或更高版本时,您可以使用系统功能使这种转换更容易编码,您只需要更改TVF。如果结果集很小,我会考虑在TVF上使用系统标量函数(SQL 2008),即使它实现了相同的系统函数。根据你的评论,听起来系统功能不能满足你的需要,但你仍然可以坚持使用dst表的实现,这个表封装在上面的TVF中。

TVF可能是性能问题,因为优化器假设它们只返回1行。

如果你需要结合封装和性能,那么我会在应用程序代码中进行时区计算。即使你必须将它应用于每个使用它的项目,你只需要在每个项目中(在数据访问层中)实现1x,并将其视为一个通用的实用程序库,如果你将跨项目使用它

答案 1 :(得分:0)

要回答OP的后续问题,SQL Server 2008解决方案将如下所示:

首先,创建永久定义:

CREATE TYPE dbo.tempDataType AS TABLE (
    timeGMT DATETIME,
    dataCol2 int,
    dataCol3 int)
GO

CREATE PROCEDURE ComputeDateWithDST
    @tempData tempDataType READONLY
AS
SELECT dateadd(ss,d.gmtOffset,t.timeGMT) timeLocal, t.*
FROM @tempData t
JOIN dst d ON t.timeGMT BETWEEN d.sTimeGMT AND d.eTimeGMT
WHERE d.zone = 'US-Eastern'
GO

此后,只要您想将子查询(现在已成为单独的查询,不再是CTE)插入存储过程:

DECLARE @tempData tempDataType

INSERT @tempData
-- sample subquery:
SELECT timeGMT, dataCol2, dataCol3
FROM tbl1 t1
JOIN tbl2 t2 ON t1.ID=t2.ID

EXEC ComputeDateWithDST @tempData;
GO

性能可能是一个问题,因为您将单独运行以前的CTE,而不是让SQL Server将其与主查询结合以优化执行计划。