为什么选择这么慢(SqlServer)?

时间:2017-06-24 17:59:13

标签: sql sql-server temp-tables

我们有3个嵌套选择,每个选择都创建临时表。外面两个走得很快。但是内部的(下面有时需要大约1/4秒来执行。它创建了一个包含7行的表,每个行都有一个日期:

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个日期。

谢谢 - 戴夫

3 个答案:

答案 0 :(得分:1)

这不会起作用吗?与set操作相比,SQL Server中的循环/游标速度很慢。

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子句中添加的天数。

如果您经常使用它,可以将其包含在内联表值函数中。

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表。它可以像这样创建(来源: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表可以在许多其他需要一系列数字进行某种计算的场景中重复使用。

答案 1 :(得分:1)

运行脚本不应该花费超过一毫秒的时间。必须存在需要调查的服务器问题。

也就是说,此操作可以作为更有效的基于集合的操作而不是循环来完成。以下示例使用CTE生成数字序列。实用程序编号表有助于这样的基于集合的处理,因此我建议您创建一个包含一系列数字(以数字作为主键)的永久表,以进一步提高性能。

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;

答案 2 :(得分:0)

而不是使用诸如@temp之类的变量而是使用临时表(#)。使用@temp时,查询分析器无法很好地进行优化。