有没有办法编写动态内部查询?基本上,我发现自己一遍又一遍地输入类似下面的查询:
;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的输入...但是,我对优化器的理解(无可否认,有限)说这不是真的一个好主意。
答案 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将其与主查询结合以优化执行计划。