为什么这个 select 这么慢 (SqlServer)?
Why is this select so slow (SqlServer)?
我们有 3 个嵌套选择,每个都创建临时 table。外面的两个走得很快。但是内部的(下面有时需要大约 1/4 秒来执行。它正在创建一个包含 7 行的 table,每行包含一个日期:
declare @StartDate datetime
declare @EndDate datetime
select @StartDate = cast(@Weeks_Loop_TheDate as date), @EndDate = cast((@Weeks_Loop_TheDate + 6) as date)
declare @temp3 table
(
TheDate datetime
)
while (@StartDate<=@EndDate)
begin
insert into @temp3
values (@StartDate )
select @StartDate=DATEADD(dd,1,@StartDate)
end
select * from @temp3
参数是用 DateTime 变量设置的,因此转换不应该很重要。并且填充应该是微不足道且快速的。那么你知道为什么它这么慢吗?
有更好的方法吗?我们需要取回该范围内 7 个日期的结果集。
谢谢 - 戴夫
这不行吗?与设置操作相比,Loops/cursors 在 SQL 服务器中速度较慢。
DECLARE @StartDate DATE = '2017-05-03';
SELECT DATEADD(DAY, RowNr, @StartDate)
FROM (SELECT ROW_NUMBER () OVER (ORDER BY object_id) - 1 AS RowNr FROM sys.objects) AS T
WHERE T.RowNr < 7;
子查询会生成一个从0到n的数字序列(你在数据库中的对象数量,它总是会超过7,如果不是,你可以直接CROSS JOIN
进去)。
然后只需使用 DATEADD
将这些生成的数字相加即可。
最后限制您要在 WHERE
子句中添加的天数。
如果您打算经常使用它,您可以将它包装在一个内联 Table 值函数中。
CREATE FUNCTION dbo.DateTable (
@p1 DATE,
@p2 INT)
RETURNS TABLE
AS RETURN
SELECT DATEADD(DAY, RowNr, @p1) AS TheDate
FROM (SELECT ROW_NUMBER () OVER (ORDER BY object_id) - 1 AS RowNr FROM sys.objects) AS T
WHERE T.RowNr < @p2;
GO
然后这样查询:
SELECT *
FROM dbo.DateTable ('2017-05-03', 7);
两种情况下的结果:
+------------+
| TheDate |
+------------+
| 2017-05-03 |
| 2017-05-04 |
| 2017-05-05 |
| 2017-05-06 |
| 2017-05-07 |
| 2017-05-08 |
| 2017-05-09 |
+------------+
另一个有用的工具是 Numbers
table。它可以像那样创建(来源:http://dataeducation.com/you-require-a-numbers-table/):
CREATE TABLE Numbers
(
Number INT NOT NULL,
CONSTRAINT PK_Numbers
PRIMARY KEY CLUSTERED (Number)
WITH FILLFACTOR = 100
)
INSERT INTO Numbers
SELECT
(a.Number * 256) + b.Number AS Number
FROM
(
SELECT number
FROM master..spt_values
WHERE
type = 'P'
AND number <= 255
) a (Number),
(
SELECT number
FROM master..spt_values
WHERE
type = 'P'
AND number <= 255
) b (Number)
GO
那么你就不必使用ROW_NUMBER()
,你的函数可以如下:
ALTER FUNCTION dbo.DateTable (
@p1 DATE,
@p2 INT)
RETURNS TABLE
AS RETURN
SELECT DATEADD(DAY, Number, @p1) AS TheDate
FROM Numbers
WHERE Number < @p2;
GO
这将非常有效,Numbers
table 可以在需要一系列数字来进行某种计算的许多其他场景中重复使用。
您的脚本 运行 不应超过一毫秒。肯定存在需要调查的服务器问题。
也就是说,此操作可以作为更有效的基于集合的操作而不是循环来完成。下面的示例使用 CTE 生成数字规则。实用程序 numbers table 有助于像这样基于集合的处理,因此我建议您创建一个带有数字序列(以 number 作为主键)的永久性 table 以进一步提高性能。
DECLARE @StartDate date = @Weeks_Loop_TheDate;
WITH numbers(n) AS (
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 0)) - 1 FROM (VALUES(0),(0),(0),(0),(0),(0),(0)) AS a(b)
)
SELECT DATEADD(day, n, @StartDate)
FROM numbers
ORDER BY n;
不要使用诸如@temp 之类的变量,而是使用临时变量 table (#)。使用@temp 时,查询分析器没有很好地优化。
我们有 3 个嵌套选择,每个都创建临时 table。外面的两个走得很快。但是内部的(下面有时需要大约 1/4 秒来执行。它正在创建一个包含 7 行的 table,每行包含一个日期:
declare @StartDate datetime
declare @EndDate datetime
select @StartDate = cast(@Weeks_Loop_TheDate as date), @EndDate = cast((@Weeks_Loop_TheDate + 6) as date)
declare @temp3 table
(
TheDate datetime
)
while (@StartDate<=@EndDate)
begin
insert into @temp3
values (@StartDate )
select @StartDate=DATEADD(dd,1,@StartDate)
end
select * from @temp3
参数是用 DateTime 变量设置的,因此转换不应该很重要。并且填充应该是微不足道且快速的。那么你知道为什么它这么慢吗?
有更好的方法吗?我们需要取回该范围内 7 个日期的结果集。
谢谢 - 戴夫
这不行吗?与设置操作相比,Loops/cursors 在 SQL 服务器中速度较慢。
DECLARE @StartDate DATE = '2017-05-03';
SELECT DATEADD(DAY, RowNr, @StartDate)
FROM (SELECT ROW_NUMBER () OVER (ORDER BY object_id) - 1 AS RowNr FROM sys.objects) AS T
WHERE T.RowNr < 7;
子查询会生成一个从0到n的数字序列(你在数据库中的对象数量,它总是会超过7,如果不是,你可以直接CROSS JOIN
进去)。
然后只需使用 DATEADD
将这些生成的数字相加即可。
最后限制您要在 WHERE
子句中添加的天数。
如果您打算经常使用它,您可以将它包装在一个内联 Table 值函数中。
CREATE FUNCTION dbo.DateTable (
@p1 DATE,
@p2 INT)
RETURNS TABLE
AS RETURN
SELECT DATEADD(DAY, RowNr, @p1) AS TheDate
FROM (SELECT ROW_NUMBER () OVER (ORDER BY object_id) - 1 AS RowNr FROM sys.objects) AS T
WHERE T.RowNr < @p2;
GO
然后这样查询:
SELECT *
FROM dbo.DateTable ('2017-05-03', 7);
两种情况下的结果:
+------------+
| TheDate |
+------------+
| 2017-05-03 |
| 2017-05-04 |
| 2017-05-05 |
| 2017-05-06 |
| 2017-05-07 |
| 2017-05-08 |
| 2017-05-09 |
+------------+
另一个有用的工具是 Numbers
table。它可以像那样创建(来源:http://dataeducation.com/you-require-a-numbers-table/):
CREATE TABLE Numbers
(
Number INT NOT NULL,
CONSTRAINT PK_Numbers
PRIMARY KEY CLUSTERED (Number)
WITH FILLFACTOR = 100
)
INSERT INTO Numbers
SELECT
(a.Number * 256) + b.Number AS Number
FROM
(
SELECT number
FROM master..spt_values
WHERE
type = 'P'
AND number <= 255
) a (Number),
(
SELECT number
FROM master..spt_values
WHERE
type = 'P'
AND number <= 255
) b (Number)
GO
那么你就不必使用ROW_NUMBER()
,你的函数可以如下:
ALTER FUNCTION dbo.DateTable (
@p1 DATE,
@p2 INT)
RETURNS TABLE
AS RETURN
SELECT DATEADD(DAY, Number, @p1) AS TheDate
FROM Numbers
WHERE Number < @p2;
GO
这将非常有效,Numbers
table 可以在需要一系列数字来进行某种计算的许多其他场景中重复使用。
您的脚本 运行 不应超过一毫秒。肯定存在需要调查的服务器问题。
也就是说,此操作可以作为更有效的基于集合的操作而不是循环来完成。下面的示例使用 CTE 生成数字规则。实用程序 numbers table 有助于像这样基于集合的处理,因此我建议您创建一个带有数字序列(以 number 作为主键)的永久性 table 以进一步提高性能。
DECLARE @StartDate date = @Weeks_Loop_TheDate;
WITH numbers(n) AS (
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 0)) - 1 FROM (VALUES(0),(0),(0),(0),(0),(0),(0)) AS a(b)
)
SELECT DATEADD(day, n, @StartDate)
FROM numbers
ORDER BY n;
不要使用诸如@temp 之类的变量,而是使用临时变量 table (#)。使用@temp 时,查询分析器没有很好地优化。