Temp Table是最佳解决方案吗?

时间:2017-10-31 19:52:33

标签: sql sql-server stored-procedures temp-tables

我正在构建一个存储过程,从多个不同的表中提取大量不同的数据,我需要一种方法从销售表中提取销售信息,然后对该销售数据执行各种汇总。在下面的示例中,我使用临时表来完成此操作,但有人建议可能有更好的方法。有没有更有效的方法来完成我在这里做的事情?

 SELECT * INTO #TempSales FROM [Sales] WHERE ClientId = @ClientId
    SELECT 
        [Customer].[CustomerID],
        [Customer].[AccountBalAmountOpen],
        [Customer].[AccountAgeAmountDays0],
        [Customer].[AccountAgeAmountDays30],
        [Customer].[AccountAgeAmountDays60],
        [Customer].[AccountAgeAmountDays90],
        [Customer].[AccountAgeAmountDaysOver90],
        (SELECT SUM(SalesAmount) FROM #TempSales WHERE CustomerId = [Customer].[CustomerID] AND Origin = 385) AS ServiceLifeTimeSales,
        (SELECT SUM(SalesAmount) FROM #TempSales WHERE CustomerId = [Customer].[CustomerID] AND Origin = 385 AND MONTH(SaleDate) = MONTH(GETDATE()) AND YEAR(SaleDate) = YEAR(GETDATE())) AS ServiceMonthToDateSales,
        (SELECT SUM(SalesAmount) FROM #TempSales WHERE CustomerId = [Customer].[CustomerID] AND Origin = 385 AND YEAR(SaleDate) = YEAR(GETDATE())) AS ServiceYearToDateSales,
        (SELECT SUM(SalesAmount) FROM #TempSales WHERE CustomerId = [Customer].[CustomerID] AND Origin = 385 AND YEAR(SaleDate) = (YEAR(GETDATE()) - 1)) AS ServicePreviousYearSales,
    (SELECT SUM(SalesAmount) FROM #TempSales WHERE CustomerId = [Customer].[CustomerID] AND Origin = 460) AS PartsLifeTimeSales,
        (SELECT SUM(SalesAmount) FROM #TempSales WHERE CustomerId = [Customer].[CustomerID] AND Origin = 460 AND MONTH(SaleDate) = MONTH(GETDATE()) AND YEAR(SaleDate) = YEAR(GETDATE())) AS PartsMonthToDateSales,
        (SELECT SUM(SalesAmount) FROM #TempSales WHERE CustomerId = [Customer].[CustomerID] AND Origin = 460 AND YEAR(SaleDate) = YEAR(GETDATE())) AS PartsYearToDateSales,
        (SELECT SUM(SalesAmount) FROM #TempSales WHERE CustomerId = [Customer].[CustomerID] AND Origin = 460 AND YEAR(SaleDate) = (YEAR(GETDATE()) - 1)) AS PartsPreviousYearSales,
        [Orders].[CustomerId] AS ParentCustomerId,
        [Orders].[OrderId],
        [Orders].[OrderStatus],
        [Orders].[UnitId],
        [Orders].[FleetId],
        [Orders].[CreatedDate] AS OrderCreatedDate,
        [Orders].[OrderType],
        [OrderParts].[OrderId] AS ParentOrderId,
        [OrderParts].[PartId],
        [OrderParts].[PartDescription],
        [OrderParts].[QuantityShip],
        [OrderParts].[QuantityBackOrder],
        [OrderParts].[CreatedDate] AS PartCreatedDate
      FROM [Customer]
      LEFT JOIN [Orders]
      ON [Orders].[CustomerId] = [Customer].[CustomerID]
      LEFT JOIN [OrderParts]
      ON [OrderParts].[OrderId] = [Orders].[OrderId]
      WHERE [Customer].[ClientID] = @ClientId
DROP TABLE #TempSales

1 个答案:

答案 0 :(得分:0)

虽然临时表可用于存储将在其他查询中重用的复杂查询的中间结果,但我并不认为您在计算时需要它,因为您正在创建临时表:

SELECT * INTO #TempSales FROM [Sales] WHERE ClientId = @ClientId

您没有加入任何内容,也没有进行计算或使用聚合函数,因此如果表格已正确编入索引(至少ClientIdCustomerIdOrigin和{{1 }},您的查询应该执行得很好甚至更快,因为使用SalesDate创建的表除了您手动添加之外没有任何索引。

但是你可能正在使用临时表来提高速度,因为你有这样的条件:

SELECT INTO

当您将过滤器列包含在函数中时make the query non-sargable。因此,查询优化器无法正确使用AND YEAR(SaleDate) = YEAR(GETDATE()) AND MONTH(SaleDate) = MONTH(GETDATE()) AND YEAR(SaleDate) = YEAR(GETDATE()) 上的任何现有索引,并且由于时态表的行数较少,因此执行完全扫描所需的时间少于SaleDate

您可以使用不对过滤器列应用任何函数的条件来修复它(尽管您可以对常量值使用函数):

Sales

或者如果您使用的是早于SQL Server 2012的SQL版本:

declare @thisYearStart as datetime, @nextYearStart as datetime,
  @thisMonthStart as datetime, @nextMonthStart as datetime

set @thisYearStart=DATEFROMPARTS(YEAR(GETDATE()),1,1)
set @nextYearStart=DATEADD(year,1,@thisYearStart)
set @thisMonthStart=DATEFROMPARTS(YEAR(GETDATE()),MONTH(GETDATE()),1)
set @nextMonthStart=DATEADD(month,1,@thisMonthStart)

然后只使用:

set @thisYearStart=CAST(CAST(YEAR(GETDATE()) as char(4))+'0101' AS datetime) -- ISO format
set @nextYearStart=DATEADD(year,1,@thisYearStart)
set @thisMonthStart=DATEADD(month,MONTH(GETDATE())-1,@thisYearStart)
set @nextMonthStart=DATEADD(month,1,@thisMonthStart)

请注意,您不需要使用变量,您可以直接在AND SaleDate>=@thisYearStart AND SaleDate<@nextYearStart AND SaleDate>=@thisMonthStart AND SaleDate<@nextMonthStart 条件下使用DATEFROMPARTS(...),但由于您已经使用它们创建了存储过程,因此他们将使用查询更具可读性。