如何优化INSERT语句

时间:2014-10-30 09:25:17

标签: sql-server tsql

我正在为数据仓库生成[Dim_Calendar]表。我在下面开发了一个查询,需要29秒才能执行&插入27k行。如果可能的话我想优化它。我确实理解虽然循环没有帮助性能,我不知道如何替换它以实现相同的结果。

我正在使用SQL Server 2012 BI版。

IF EXISTS(SELECT * FROM sys.indexes WHERE name='PK_Dim_Calendar_1' AND object_id = OBJECT_ID('Dim_Calendar'))
BEGIN
    ALTER TABLE [dbo].[Dim_Calendar] DROP CONSTRAINT [PK_Dim_Calendar_1]
END

SET DATEFIRST 1--Sets Monday as 1st day of the week.
DECLARE @today DATETIME = ( SELECT  GETDATE())
DECLARE @start DATETIME = DATEADD(dd, 1, (SELECT Max(date) FROM Dim_Calendar))

IF @start IS NULL
BEGIN
    INSERT INTO [dbo].[Dim_Calendar]
    VALUES (19000101, '1900-01-01', 'Monday',   1   ,'Unknown', 1, 'January', 1900, 1)
    SET @start = '1940-01-01'
END


DECLARE @end DATETIME   = (SELECT DATEFROMPARTS(YEAR(@today), 12, 31))


WHILE @start <= @end
    BEGIN

        INSERT INTO [dbo].[Dim_Calendar]
            SELECT
                YEAR(@start) * 10000 + MONTH(@start) * 100 + DAY(@start)
                ,@start
                ,DATENAME(dw, @start)
                ,DATEPART(wk, @start)
                ,'w/c ' + CONVERT(char(8), DATEADD(dd, 1 - DATEPART(dw, @start), @start), 3)
                ,DATEPART(mm, @start)
                ,DATENAME(MONTH, @start)
                ,YEAR(@start)
                ,DATEPART(QQ, @start)
        SET @start = DATEADD(dd, 1, @start)
    END


ALTER TABLE [dbo].[Dim_Calendar] ADD  CONSTRAINT [PK_Dim_Calendar_1] PRIMARY KEY CLUSTERED 
(
    [FullDateID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO

3 个答案:

答案 0 :(得分:2)

查询很慢,因为您使用循环一次创建一个值。

在处理价值序列时,有一个数字&#39;表格,数字从1到您想要的多少项目。通过选择或连接该表,您可以生成序列,识别间隙等.Aaron Bertrand写了series of articles关于如何创建Numbers表并使用它来创建一组日期。

假设您已经有这样的Numbers(n),创建一个Calendar表就像这样简单:

DECLARE @start DATE = '2005-07-01'; 
DECLARE @end DATE = DATEADD(DAY, -1, DATEADD(YEAR, 30, @start));

DECLARE @days int = DATEDIFF(DAY, @start, @end) + 1

SELECT TOP (@days) 
  d = CONVERT(DATE, DATEADD(DAY, n-1, @start))
INTO dbo.Calendar
FROM dbo.Numbers ORDER BY n;

在您的情况下,SELECT部分将类似于:

;WITH Dates (d)
AS (
    SELECT TOP (@days) 
        d = CONVERT(DATE, DATEADD(DAY, n-1, @start))
    FROM dbo.Numbers 
    ORDER BY d)
 SELECT
    YEAR(d) * 10000 + MONTH(d) * 100 + DAY(d)
    ,d
    ,DATENAME(dw, d)    ,DATEPART(wk, d)
    ,'w/c ' + CONVERT(char(8), DATEADD(dd, 1 - DATEPART(dw, d), d), 3)
    ,DATEPART(mm, d)
    ,DATENAME(MONTH, d)
    ,YEAR(d)
    ,DATEPART(QQ, d)
from Dates

要生成Numbers表,您可以使用以下语句:

SELECT TOP (1000000) n = CONVERT(INT, ROW_NUMBER() OVER (ORDER BY s1.[object_id]))
INTO dbo.Numbers
FROM sys.all_objects AS s1 CROSS JOIN sys.all_objects AS s2
OPTION (MAXDOP 1);

CREATE UNIQUE CLUSTERED INDEX n ON dbo.Numbers(n)

答案 1 :(得分:2)

这应该适合你(快速):

;WITH [dates] 
AS 
   (SELECT @start AS [date] 
    UNION ALL 
    SELECT DATEADD(d, 1, [date]) AS [date] 
    FROM [dates] 
    WHERE [date] < @end) 

SELECT 
      YEAR([date]) * 10000 + MONTH([date]) * 100 + DAY([date])
                ,[date]
                ,DATENAME(dw, [date])
                ,DATEPART(wk, [date])
                ,'w/c ' + CONVERT(char(8), DATEADD(dd, 1 - DATEPART(dw, [date]), [date]), 3)
                ,DATEPART(mm, [date])
                ,DATENAME(MONTH, [date])
                ,YEAR([date])
                ,DATEPART(QQ, [date]) 
FROM [dates] 
OPTION (MAXRECURSION 32747);

答案 2 :(得分:0)

Try this

IF EXISTS(SELECT *
          FROM   sys.indexes
          WHERE  name = 'PK_Dim_Calendar_1'
                 AND object_id = OBJECT_ID('Dim_Calendar'))
  BEGIN
      ALTER TABLE [dbo].[Dim_Calendar]
        DROP CONSTRAINT [PK_Dim_Calendar_1]
  END

SET DATEFIRST 1--Sets Monday as 1st day of the week.
DECLARE @today DATETIME = (SELECT GETDATE())
DECLARE @start DATETIME = DATEADD(dd, 1, (SELECT Max(date)
                  FROM   Dim_Calendar))

IF @start IS NULL
  BEGIN
      INSERT INTO [dbo].[Dim_Calendar]
      VALUES      (19000101,
                   '1900-01-01',
                   'Monday',
                   1,
                   'Unknown',
                   1,
                   'January',
                   1900,
                   1)

      SET @start = '1940-01-01'
  END

DECLARE @end DATETIME = (SELECT DATEFROMPARTS(YEAR(@today), 12, 31));

WITH T(start)
     AS (SELECT @start
         UNION ALL
         SELECT start + 1
         FROM   T
         WHERE  T.start < @end)
INSERT INTO [dbo].[Dim_Calendar]
SELECT YEAR(start) * 10000 + MONTH(start) * 100 + DAY(start),
       start,
       DATENAME(dw, start),
       DATEPART(wk, start),
       'w/c '
       + CONVERT(CHAR(8), DATEADD(dd, 1 - DATEPART(dw, start), start), 3),
       DATEPART(mm, start),
       DATENAME(MONTH, start),
       YEAR(start),
       DATEPART(QQ, start)
FROM   T
OPTION (MAXRECURSION 0);

ALTER TABLE [dbo].[Dim_Calendar]
  ADD CONSTRAINT [PK_Dim_Calendar_1] PRIMARY KEY CLUSTERED ( [FullDateID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

GO