给定一个(简化的)存储过程,例如:
CREATE PROCEDURE WeeklyProc(@endDate DATE)
AS
BEGIN
DECLARE @startDate DATE = DATEADD(DAY, -6, @endDate)
SELECT
-- Stuff
FROM Sale
WHERE SaleDate BETWEEN @startDate AND @endDate
END
Run Code Online (Sandbox Code Playgroud)
如果Sale
表很大,SELECT
可能需要很长时间才能执行,显然是因为优化器由于局部变量而无法优化。我们测试运行SELECT
带有变量的部件,然后硬编码日期,执行时间从约 9 分钟变为约 1 秒。
我们有许多基于“固定”日期范围(周、月、8 周等)进行查询的存储过程,因此输入参数只是 @endDate,@startDate 是在过程中计算的。
问题是,在 WHERE 子句中避免变量以免损害优化器的最佳做法是什么?
我们提出的可能性如下所示。是否有任何这些最佳实践,或者还有其他方法吗?
使用包装程序将变量转换为参数。
参数不会像局部变量那样影响优化器。
CREATE PROCEDURE WeeklyProc(@endDate DATE)
AS
BEGIN
DECLARE @startDate DATE = DATEADD(DAY, -6, @endDate)
EXECUTE DateRangeProc @startDate, @endDate
END
CREATE PROCEDURE DateRangeProc(@startDate DATE, @endDate DATE)
AS
BEGIN
SELECT
-- Stuff
FROM Sale
WHERE SaleDate …
Run Code Online (Sandbox Code Playgroud) 在上一个问题中如何合并数据集而不包括冗余行?我询问了在导入过程中过滤冗余历史数据的问题,但@DavidSpillett 正确回答说我无法做我想做的事情。
我现在不想在导入过程中过滤表,而是想在表上创建一个视图,该视图仅返回价格发生变化的记录。
这是为解决这个问题而改写的原始场景:
我们有一个物品的历史价格表。该表包含多个日期记录相同价格的行。我想在此数据上创建一个视图,该视图仅显示价格随时间的变化,因此如果价格从 A 更改为 BI 想要查看它,但如果它从 B“更改”为 B 那么我不想看到它.
例子:如果昨天的价格是$1,今天的价格是$1,并且没有其他价格变化,那么今天的价格可以从昨天的价格推断出来,所以我只需要昨天的记录。
示例(http://sqlfiddle.com/#!3/c95ff/1):
Table data:
Effective Product Kind Price
2013-04-23T00:23:00 1234 1 1.00
2013-04-24T00:24:00 1234 1 1.00 -- redundant, implied by record 1
2013-04-25T00:25:00 1234 1 1.50
2013-04-26T00:26:00 1234 1 2.00
2013-04-27T00:27:00 1234 1 2.00 -- redundant, implied by record 4
2013-04-28T00:28:00 1234 1 1.00 -- not redundant, price changed back to 1.00
Expected view data:
Effective Product Kind Price
2013-04-23T00:23:00 1234 1 …
Run Code Online (Sandbox Code Playgroud) 我们有几个独立的数据库,它们的数据和代码是相同的,不是在数据库之间访问,而是在每个数据库中数据意味着相同的事情,代码做同样的事情。
例子是:
出于维护和支持的原因,我认为对于错误代码、过程、函数和类型之类的东西来说,在所有数据库中拥有一个“单一事实点”是有意义的,而不是每个数据库中都有一个不同的事实。
目前,每个数据库都有自己的所有副本,包括源存储库,我们独立维护它们。这很不理想,因为在 A 中修复一个过程而忘记将它放在 B 中太容易了,或者在 A 中添加错误代码而在 B 中添加相同的错误代码但它们意味着不同的东西,等等。
数据库不会同时更新,它们不一定驻留在相同的硬件上。在某些情况下,它们可以相互读取数据(如果存在另一个)。
是否有一种标准方法可以为跨多个数据库使用的数据/代码提供单一事实点?
我需要调用另一个数据库中的存储过程并检查返回值。其他数据库的名称可能会有所不同,因此我尝试使用 sp_executesql,但我一直无法找到如何从该过程中获取返回值。
这是我所拥有的简化版本:
DECLARE @errorLogId INT
DECLARE @otherDbName NVARCHAR(MAX) = 'Foo' -- Get the database name from somewhere
DECLARE @sql NVARCHAR(MAX)
SET @sql = @otherDbName + '.dbo.SomeProc'
-- I want @errorLogId to be the return value from SomeProc, this didn't work.
EXECUTE @errorLogId = sp_executesql @sql
IF @errorLogId <> 0
RAISERROR('SomeProc failed, ErrorLogId = %d.', 16, 1, @errorLogId) WITH SETERROR
Run Code Online (Sandbox Code Playgroud)
如果 SomeProc 中发生错误,它会被捕获并将条目写入错误表(在另一个数据库中),然后 SomeProc 返回所写入记录的错误表 ID(标识)。否则 SomeProc 返回 0。
我知道 SomeProc 失败,因为错误被写入另一个数据库中的错误日志,但在本地数据库中 @errorLogId 为 0,这被视为成功。我认为返回值会“通过”sp_executesql,但我认为返回值实际上来自 sp_executesql 而不是 SomeProc(即,无论 SomeProc 成功还是失败,sp_executesql …