为什么这个sql脚本如此之慢以及如何使它快速闪电?

时间:2014-03-11 19:59:42

标签: sql sql-server optimization sql-server-2008-r2

这是我的sql脚本

CREATE TABLE dbo.calendario (
    datacal DATETIME NOT NULL PRIMARY KEY,
    horautil BIT NOT NULL DEFAULT 1
);

-- DELETE FROM dbo.calendario;

DECLARE @dtmin DATETIME, @dtmax DATETIME, @dtnext DATETIME;

SELECT
    @dtmin = '2014-03-11 00:00:00'
    , @dtmax = '2030-12-31 23:50:00'
    , @dtnext = @dtmin;

WHILE (@dtnext <= @dtmax) BEGIN
    INSERT INTO dbo.calendario(datacal) VALUES (@dtnext);
    SET @dtnext = DATEADD(MINUTE, 10, @dtnext);
END;

基本上,我想创建一个日期间隔为10分钟的表格。循环插入了很多记录,但我认为执行它会很快。这需要几分钟......

我正在使用sql server 2008 r2。

感谢任何帮助。

3 个答案:

答案 0 :(得分:8)

你应该避免循环等,并尝试接近这个基于集合。 (谷歌搜索“RBAR SQL”)

无论如何,这在我的笔记本电脑上以1秒的速度运行:

DROP TABLE dbo.calendario 
GO

CREATE TABLE dbo.calendario (
    datacal DATETIME NOT NULL PRIMARY KEY,
    horautil BIT NOT NULL DEFAULT 1
);

-- DELETE FROM dbo.calendario;

DECLARE @dtmin DATETIME, @dtmax DATETIME, @intervals int

SELECT @dtmin = '2014-03-11 00:00:00'
     , @dtmax = '2030-12-31 23:50:00'


SELECT @intervals = DateDiff(minute, @dtmin, @dtmax) / 10

;WITH 
  L0   AS(SELECT 1 AS c UNION ALL SELECT 1),
  L1   AS(SELECT 1 AS c FROM L0 AS A, L0 AS B),
  L2   AS(SELECT 1 AS c FROM L1 AS A, L1 AS B),
  L3   AS(SELECT 1 AS c FROM L2 AS A, L2 AS B),
  L4   AS(SELECT 1 AS c FROM L3 AS A, L3 AS B),
  L5   AS(SELECT 1 AS c FROM L4 AS A, L4 AS B),
  L6   AS(SELECT 1 AS c FROM L5 AS A, L5 AS B),
  Nums AS(SELECT ROW_NUMBER() OVER(ORDER BY c) AS n FROM L6)

INSERT INTO dbo.calendario(datacal)
SELECT DateAdd(minute, 10 * (n - 1), @dtmin)
  FROM Nums
 WHERE n BETWEEN 1 AND @intervals + 1

-- SELECT * FROM dbo.calendario ORDER BY datacal

答案 1 :(得分:1)

此代码在我的机器上需要23秒(并且大部分都在排序中)

DECLARE @DateMin AS datetime = '2014-03-11 00:00:00';
DECLARE @DateMax AS datetime = '2030-12-31 23:50:00';

DECLARE @Test AS Table (
   datacal DATETIME NOT NULL PRIMARY KEY
);

WITH Counter AS (
    SELECT ROW_NUMBER() OVER (ORDER BY a.object_id) -1 AS Count
    FROM sys.all_objects AS a
         CROSS JOIN sys.all_objects AS b
)
INSERT INTO @Test (datacal)
SELECT DATEADD(minute, 10 * Count, @DateMin)
FROM Counter
WHERE DATEADD(minute, 10 * Count, @DateMin) <= @DateMax

答案 2 :(得分:1)

已经有一些有趣的答案。这是另一个基于集合的选项,它也使用公共表表达式。

DECLARE @StartDate DATETIME = '2014-03-11 00:00:00';
DECLARE @EndDate DATETIME = '2030-12-31 00:00:00';

--CTE of days in the Start/End date range.
WITH DaysTable AS
(
    SELECT @StartDate AS CalendarDate
    UNION ALL
    SELECT DATEADD(dd, 1, CalendarDate)
    FROM DaysTable
    WHERE CalendarDate < @EndDate
) 
--CTE of 10 min intervals for one day.
,MinsTable AS
(
    SELECT CAST(0 AS DATETIME) MinutesDate
    UNION ALL
    SELECT DATEADD(MINUTE, 10, mt.MinutesDate)
    FROM MinsTable mt
    WHERE MinutesDate < DATEADD(MINUTE, -10, CAST(0 AS DATETIME) + 1)
) 
SELECT DATEADD(MINUTE, DATEDIFF(MINUTE, CAST(0 AS DATETIME), mt.MinutesDate), dt.CalendarDate) Every10Min
INTO #TestTable
FROM MinsTable mt
--Cross join for all 10 min intervals of all days in date range.
CROSS JOIN DaysTable dt
ORDER BY DATEADD(MINUTE, DATEDIFF(MINUTE, CAST(0 AS DATETIME), mt.MinutesDate), dt.CalendarDate)
OPTION (MAXRECURSION 32767)

SELECT *
FROM #TestTable