Sql server 如何优化INSERT语句
我正在为数据仓库生成[Dim_Calendar]表。我在下面开发了一个查询,执行它需要29秒&插入27k行。我想优化它,如果可能的话。我确实理解,虽然循环对性能没有帮助,但我不知道如何替换它以获得相同的结果 我正在使用SQL Server 2012 BI版Sql server 如何优化INSERT语句,sql-server,tsql,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 A
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
查询速度很慢,因为您正在使用循环一次创建一个值 在处理值序列时,有一个“数字”表,表中的数字从1到您想要的项目数都很有用。通过选择或加入该表,您可以生成序列、确定差距等。Aaron Bertrand写了一篇关于如何创建数字表并使用它创建一组日期的文章 假设您已经有了这样一个数字n,创建日历表非常简单,如下所示:
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;
在您的情况下,选择的零件类似于:
;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
要生成数字表,可以使用以下语句:
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到您想要的项目数都很有用。通过选择或加入该表,您可以生成序列、确定差距等。Aaron Bertrand写了一篇关于如何创建数字表并使用它创建一组日期的文章 假设您已经有了这样一个数字n,创建日历表非常简单,如下所示:
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;
在您的情况下,选择的零件类似于:
;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
要生成数字表,可以使用以下语句:
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)
这也应该很快适用于您:
;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);
这也应该很快适用于您:
;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);
数据显示,这比小数字的数字表慢14倍,大数字的数字表慢35倍。您已经用递归替换了循环,但仍然一次生成一个值,而没有使用递归index@PanagiotisKanavosOP问了一个加快插入速度的方法,这可能不是最快的方法,但在我的开发盒上,它仍然只需要643毫秒就可以完成,这比他的29秒要快得多。根据我的研究,这比数字表中的小数字慢14倍,大数字慢35倍。您已经用递归替换了循环,但仍然一次生成一个值,而没有使用递归index@PanagiotisKanavosOP问了一个加快插入速度的方法,这可能不是最快的方法,但在我的开发盒上,它仍然只花了643毫秒就完成了,这比他的29秒要快得多。这个答案为什么以及如何帮助OP实现目标?请解释你的答案,不仅是为了OP,也是为了进一步阅读。谢谢有关更多信息,请阅读我们的最终目标是删除while循环。如果我们查看实际代码,@start值已经增加,即使这是代码中的关键部分。我们可以借助CTE而不是while循环来加速insert语句。这与Steve Ford发布的递归CTE相同。这个答案为什么以及如何帮助OP实现目标?请解释你的答案,不仅是为了OP,也是为了进一步阅读。谢谢有关更多信息,请阅读我们的最终目标是删除while循环。如果我们查看实际代码,@start值已经增加,即使这是代码中的关键部分。我们可以借助CTE而不是while循环来加速insert语句,这与Steve Ford发布的递归CTE相同。