在MSSQL 2012上优化这个40多秒的选择查询?

时间:2014-02-10 13:55:59

标签: sql sql-server tsql optimization

我不是DBA,而且我没有优化此查询的想法。运行大约需要40多秒。我可以优化哪些明显的新手错误?

USE [deskcal2014]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE PROC [dbo].[proc_AdminRegisteredCards]
(
   @Take         AS INT,
   @Skip         AS INT,
   @FilterColumn AS NVARCHAR(max),
   @FilterOrder AS NVARCHAR(max))
AS
BEGIN
    SELECT TOP(@Take) 
      ISNULL( ROW_NUMBER() OVER(ORDER BY ta.CreatedOn, ta.ItemId), -1000) AS AdminRegisteredCardsId,
      ta.ItemId,
      ta.CardNumber, 
      ta.FirstName, 
      ta.LastName, 
      ta.Birthday, 
      ta.PostalCode, 
      ta.[Description], 
      ta.CardActivated, 
      ta.ContactInfo, 
      ta.PhoneNumber,
      ta.ReceiveCalendarReminders, 
      ta.ReceiveGeneralMails, 
      ta.ReceivePrefStoreMails, 
      ta.CardStatus, 
      ta.SamoaCardId, 
      ta.CalendarUserId, 
      ta.LiveOpsRegistrantId,
      ta.UseType,
      ta.CreatedOn,
      ta.ModifiedBy, 
      ta.ModifiedOn from (
          SELECT CalendarUser.CalendarUserId as ItemId,
          SamoaCard.CardNumber,
          SamoaCard.FirstName,
          SamoaCard.LastName,
          CalendarUser.Birthday,
          CalendarUser.PostalCode,
          RegisterSourceType.[Description],
          CalendarUserCard.CardActivated,
          CalendarUser.EmailAddress as ContactInfo,
          CalendarUser.PhoneNumber,
          CalendarUser.ReceiveCalendarReminders,
          CalendarUser.ReceiveGeneralMails,
          CalendarUser.ReceivePrefStoreMails,
          CASE WHEN CalendarUserCard.CardDeactivated IS NOT NULL THEN 'Deactivated' ELSE 'Activated' END AS CardStatus,
          SamoaCard.SamoaCardId,
          CalendarUser.CalendarUserId,
          null as LiveOpsRegistrantId,
          SamoaCard.CreatedOn,
          'C' as UseType,
          CalendarUser.ModifiedBy,
          CalendarUser.ModifiedOn     
              FROM (
                  (dbo.CalendarUser CalendarUser 
                  INNER JOIN dbo.RegisterSourceType RegisterSourceType ON (CalendarUser.RegisterType = RegisterSourceType.RegisterType))
                  INNER JOIN dbo.CalendarUserCard CalendarUserCard     ON (CalendarUserCard.CalendarUserId = CalendarUser.CalendarUserId)
              )
              INNER JOIN dbo.SamoaCard SamoaCard ON (CalendarUserCard.SamoaCardId = SamoaCard.SamoaCardId)
              ORDER BY
                  case when @FilterColumn = 'FirstName' and @FilterOrder = 'ASC' 
                    then CalendarUser.Firstname end asc, 
                  case when @FilterColumn = 'FirstName' and @FilterOrder = 'DESC' 
                    then CalendarUser.Firstname end desc, 
                  case when @FilterColumn = 'LastName' and @FilterOrder = 'ASC' 
                    then CalendarUser.Lastname end asc, 
                  case when @FilterColumn = 'LastName' and @FilterOrder = 'DESC' 
                    then CalendarUser.Lastname end desc,
                  case when @FilterColumn = 'CardNumber' and @FilterOrder = 'ASC' 
                    then CalendarUser.CardNumber end asc, 
                  case when @FilterColumn = 'CardNumber' and @FilterOrder = 'DESC' 
                    then CalendarUser.CardNumber end desc,
                  case when @FilterColumn = 'Birthday' and @FilterOrder = 'ASC' 
                    then CalendarUser.Birthday end asc, 
                  case when @FilterColumn = 'Birthday' and @FilterOrder = 'DESC' 
                    then CalendarUser.Birthday end desc,  
                  case when @FilterColumn = 'Description' and @FilterOrder = 'ASC' 
                    then RegisterSourceType.[Description] end asc, 
                  case when @FilterColumn = 'Description' and @FilterOrder = 'DESC' 
                    then RegisterSourceType.[Description] end desc,
                  case when @FilterColumn = 'ContactInfo' and @FilterOrder = 'ASC' 
                    then CalendarUser.EmailAddress end asc, 
                  case when @FilterColumn = 'ContactInfo' and @FilterOrder = 'DESC' 
                    then CalendarUser.EmailAddress end desc,
                  case when @FilterColumn = 'CardActivated' and @FilterOrder = 'ASC' 
                    then CalendarUserCard.CardActivated end asc, 
                  case when @FilterColumn = 'CardActivated' and @FilterOrder = 'DESC' 
                    then CalendarUserCard.CardActivated end desc,
                  case when @FilterColumn = 'PostalCode' and @FilterOrder = 'ASC' 
                    then CalendarUser.PostalCode end asc, 
                  case when @FilterColumn = 'PostalCode' and @FilterOrder = 'DESC' 
                    then CalendarUser.PostalCode     end desc
              OFFSET @Skip ROWS -- skip N rows
              FETCH NEXT @Take ROWS ONLY


    union all
    SELECT TOP(10)
        LiveOpsRegistrant.LiveOpsRegistrantId as ItemId, 
        LiveOpsRegistrant.CardNumber,
        'Registered' as FirstName,
        'Card' as LastName,
        LiveOpsRegistrant.Birthday,
        null as PostalCode,
        'LiveOps' as Description,
        LiveOpsRegistrant.CreatedOn as CardActivated,
        LiveOpsRegistrant.PhoneNumber as ContactInfo,
        LiveOpsRegistrant.PhoneNumber,
        CONVERT(bit,0) as ReceiveCalendarReminders,
        CONVERT(bit,0) as ReceiveGeneralMails,
        CONVERT(bit,0) as ReceivePrefStoreMails,
        'Activated' AS CardStatus,
        SamoaCard.SamoaCardId,
        null as CalendarUserId,
        LiveOpsRegistrant.LiveOpsRegistrantId,
        SamoaCard.CreatedOn,
        'L' as UseType,
        SamoaCard.ModifiedBy,
        SamoaCard.ModifiedOn
    FROM dbo.LiveOpsRegistrant LiveOpsRegistrant
         INNER JOIN dbo.SamoaCard SamoaCard ON (LiveOpsRegistrant.CardNumber = SamoaCard.CardNumber)) ta
END
GO

2 个答案:

答案 0 :(得分:0)

回应已经给出的一些评论:在ORDER BY子句中包含一堆逻辑通常不能很好地工作。 ROW_NUMBER()在具有许多联接和其他复杂性的查询中使用时可能会很糟糕。

临时表可能是你的第一个最佳选择。阅读你的代码,我认为第一个代码是CalendarUser.CalendarUserId,你需要用一堆嵌套的if ... else if语句填充它:

if @FilterColumn = 'FirstName' and @FilterOrder = 'ASC' 
begin
    insert into #CalendarUser 
    select top(@Take) CalendarUserId
    order by Firstname asc
    offset @Skip rows
    fetch next @Take rows only
end
else
begin
    if .....

使用#CalendarUser上的内部联接来填充第二个临时表#DataOut,其中包含您要输出的所有字段,以过滤结果集。使用ROW_NUMBER()排除您正在计算的字段。将UNION ALL保留在此查询之外,将其中的数据作为单独步骤附加到#DataOut表中。

最终输出查询将是

select 
  ISNULL( ROW_NUMBER() OVER(ORDER BY CreatedOn, ItemId), -1000) 
    AS AdminRegisteredCardsId,
  #DataOut.*
from #DataOut

写作不愉快,感觉蛮力,但我相信你会看到戏剧性的表现改善。

答案 1 :(得分:0)

我一直在尝试优化类似的查询,并得出结论,跨连接的排序和过滤对性能有很大的影响 - 我的建议是对使用索引视图进行排序或过滤的所有内容进行非规范化处理看看这会对表现产生什么影响。