误用的数据库视图 (SQL Server) - 如何优化

Posted

技术标签:

【中文标题】误用的数据库视图 (SQL Server) - 如何优化【英文标题】:Misused database view (SQL Server) - how to optimize 【发布时间】:2018-01-11 00:33:32 【问题描述】:

我需要一些关于如何优化 SQL 查询的建议,因为我不是 SQL 专家。

我的一个客户有一些在 SQL Server 上运行的 SQL 代码,他们希望它运行得更快。以前跑得快,现在不行了。

查询每天使用相同的大视图 6 次来提取数据,这是导致它非常慢的主要原因,运行时间超过 25 分钟。

主要问题似乎是视图被调用了很多次,运行之间的差异很小。

是否可以重写查询,使其只调用一次视图,然后从中提取一周的数据,是否可以在不使用临时表的情况下完成?

-- Imput: @justWeekEnding - the date for the end of week report
-- Input: @customerid - the unique customer ID for whom the report is generated
-- Expected output: Sales report, showing profit margin for the selected customer per shop per day-of-week
--
-- dbo.charting_salesdata: View producing a sales report across all stores per date per customer
-- dbo.stores: Table containing store info
-- dbo.receipts: Table containing all receipts
-- dbo.storecategories: Table containing all store categories

 DECLARE @justWeekEnding AS date;
 SET @justWeekEnding = CONVERT(date, @weekEnding, 101);

 SELECT 
     Query.StoreName AS [Store Name], Query.POS as [POS Name], 
     Query.saturday, Query.sunday, Query.monday, Query.tuesday, 
     Query.wednesday, Query.thursday, Query.friday
 FROM
     (SELECT     
          dbo.stores.NAME AS [StoreName],
          receipts.POS AS [POS],
          (SELECT COUNT(percentage) 
           FROM dbo.charting_salesdata WITH (nolock)
           WHERE dbo.charting_salesdata.receiptid = dbo.receipts.receiptid
             AND dateofsale = Dateadd(d, -6, @justWeekEnding)
           GROUP BY receiptid, dateofsale) AS saturday,
          (SELECT COUNT(percentage)
           FROM dbo.charting_salesdata WITH (nolock)
           WHERE dbo.charting_salesdata.receiptid = dbo.receipts.receiptid
             AND dateofsale = Dateadd(d, -5, @justWeekEnding)
           GROUP BY receiptid, dateofsale) AS sunday,
          (SELECT COUNT(percentage)
           FROM dbo.charting_salesdata WITH (nolock)
           WHERE dbo.charting_salesdata.receiptid = dbo.receipts.receiptid
             AND dateofsale = Dateadd(d, -4, @justWeekEnding)
           GROUP BY receiptid, dateofsale) AS monday,
          (SELECT COUNT(percentage)
           FROM dbo.charting_salesdata WITH (nolock)
           WHERE dbo.charting_salesdata.receiptid = dbo.receipts.receiptid
             AND dateofsale = Dateadd(d, -3, @justWeekEnding)
           GROUP BY receiptid, dateofsale) AS tuesday,
          (SELECT COUNT(percentage)
           FROM dbo.charting_salesdata WITH (nolock)
           WHERE dbo.charting_salesdata.receiptid = dbo.receipts.receiptid
             AND dateofsale = Dateadd(d, -2, @justWeekEnding)
           GROUP BY receiptid, dateofsale) AS wednesday,
          (SELECT COUNT(percentage)
           FROM dbo.charting_salesdata WITH (nolock)
           WHERE dbo.charting_salesdata.receiptid = dbo.receipts.receiptid
             AND dateofsale = Dateadd(d, -1, @justWeekEnding)
           GROUP BY receiptid, dateofsale) AS thursday,
          (SELECT COUNT(percentage)
           FROM dbo.charting_salesdata WITH (nolock)
           WHERE dbo.charting_salesdata.receiptid = dbo.receipts.receiptid
             AND dateofsale = @justWeekEnding
           GROUP BY receiptid, dateofsale) AS friday,
          (SELECT COUNT(percentage)
           FROM dbo.charting_salesdata WITH (nolock)
           WHERE dbo.charting_salesdata.receiptid = dbo.receipts.receiptid
             AND dateofsale BETWEEN Dateadd(d, -6, @justWeekEnding) AND @justWeekEnding) AS TOTAL
       FROM       
           receipts WITH (nolock)
       INNER JOIN 
           dbo.stores WITH (nolock) ON dbo.receipts.storeid = dbo.stores.storeid
       INNER JOIN 
           dbo.storecategories WITH (nolock) ON dbo.stores.storecategoryid = dbo.storecategories.storecategoryid
       WHERE      
           1=1
           AND customerid = @customerid
           AND dbo.receipts.isdeleted = 0) AS Query
WHERE 
    Query.TOTAL > 0
ORDER BY   
    Query.StoreName, Query.TargetName

【问题讨论】:

设置Bad Habits to kick - putting NOLOCK everywhere - 不推荐在任何地方使用它 - 恰恰相反! 感谢您的建议。我继承的 SQL 到处都有它们……我将开始摆脱它们。到处使用它们的原因是什么? 【参考方案1】:

是的,这是可能的。

按 dateofsale、receiptid、dateofsale 对所有内容进行分组,并应用日期过滤器,然后 PIVOT 结果(将行(一周中的 7 天)旋转到列)

https://technet.microsoft.com/en-us/library/ms177410(v=sql.105).aspx

【讨论】:

这听起来是一个很好的解决方案,正是我所需要的。非常感谢。【参考方案2】:

使用@justweekEnding 日期过滤器为按日期分组的列 dateofsale、receiped、dateofsale 的视图创建 CTE(通用类型表达式)。

然后您可以创建一个查询,该查询将其他表与 CTE 连接起来并执行 Pivot 以获取所有列,例如周六、周日、周一

【讨论】:

感谢您的意见。我以前没有使用过 CTE,我会研究一下。

以上是关于误用的数据库视图 (SQL Server) - 如何优化的主要内容,如果未能解决你的问题,请参考以下文章

sql server 2008怎么修改视图中的字段属性!!例如修改字段名和字段类型!

如何为每个表计算由 SQL Server 中未指定数量的表共享的列中的不同值?

模拟实现SQL Server字段列显示的数据类型

用于全文搜索的 SQL Server 索引视图

配置SQL Server 2000选项

SQL Server - 如果两个特定字段都为空,则从视图中排除记录