我有一个使用20个表检索数据的存储过程。 程序样本:
CREATE PROCEDURE GetEnquiries
(
@EnquiryDate DATETIME = NULL
)
AS
DECLARE @querySELECT VARCHAR(MAX) = ''
DECLARE @queryWHERE VARCHAR(MAX) = ''
DECLARE @queryExtraColumns VARCHAR(MAX) = ''
DECLARE @queryReturnResults VARCHAR(MAX) = ''
-----------------------------------------------------
--Create temp table
-----------------------------------------------------
SET @querySELECT = '
CREATE TABLE #tempResults
(
EnquiryId INT,
Cost Decimal(18,2),
CustomerName VARCHAR(50),
EnquiryStatus VARCHAR(50),
ContactNumber VARCHAR(50),
NumberOfVisits INT
) '
-----------------------------------------------------
--Insert into temp table
-----------------------------------------------------
SET @querySELECT = '
INSERT INTO #tempResults
(
EnquiryId ,
Cost ,
CustomerName ,
EnquiryStatus ,
ContactNumber
) '
-----------------------------------------------------
--SELECT
-----------------------------------------------------
SET @querySELECT = '
SELECT
e.EnquiryId ,
e.Cost ,
c.CustomerName ,
e.EnquiryStatus ,
c.ContactNumber
FROM Enquiry e
INNER JOIN Customers c ON e.CustomerId = c.CustomerId '
-----------------------------------------------------
-- WHERE
-----------------------------------------------------
IF(@EnquiryDate IS NOT NULL)
BEGIN
SET @queryWHERE = @queryWHERE + ' CONVERT(VARCHAR(10),e.EnquiryDate,23) >= ' + ''''+ CONVERT(VARCHAR(10),@EnquiryDate,23) + ''''
END
--- There are at least 14 parameters used in WHERE operation the above is just one of them
-----------------------------------------------------
-- Count NumberOfVisits
-----------------------------------------------------
SET @queryExtraColumns = '
;WITH NumberOfVisits AS
(
SELECT t.EnquiryId, COUNT(EnquiryId) AS NumberOfVisits
FROM NumberOfVisits v
INNER JOIN #tempResults t ON v.EnquiryId = t.EnquiryId
GROUP BY t.EnquiryId
)
UPDATE #tempResults
SET NumberOfVisits = u.NumberOfVisits
FROM #tempResults t
INNER JOIN NumberOfVisits u ON u.EnquiryId = t.EnquiryId
'
-----------------------------------------------------
-- return the results
-----------------------------------------------------
SET @queryReturnResults = '
SELECT
EnquiryId ,
Cost ,
CustomerName ,
EnquiryStatus ,
ContactNumber ,
NumberOfVisits
FROM #tempResults t
'
-----------------------------------------------------
-- Combine all the strings + DROP the temp table
-----------------------------------------------------
-- PRINT( @querySELECT + ' WHERE ' + @queryWHERE + @queryExtraColumns + @queryReturnResults + ' DROP TABLE #tempResults ')
EXEC( @querySELECT + ' WHERE ' + @queryWHERE + @queryExtraColumns + @queryReturnResults + ' DROP TABLE #tempResults ')
一些事实:
上述程序是我正在处理的存储程序的简单形式。
我正在使用SQL Server 2008
我的实际过程有15个参数,所有这些参数都在WHERE子句中使用。如果为参数提供了值,则该参数包含在WHERE子句中,否则不包含。
至少有10列值来自GROUP BY条件,如上述程序中给出的“NumberOfVisits”。
我在所有主键上都有索引。外键。
我在WHERE子句中使用的所有列都有索引。
我在GROUP BY子句中使用的所有列都有索引。
问题:
Q1:这是按照上述模式创建动态存储过程的最佳做法吗?
Q2:我使用以下命令得到了此过程的输出SQL: - PRINT(@querySELECT +'WHERE'+ @queryWHERE + @queryExtraColumns + @queryReturnResults +'DROP TABLE #tempResults') 当我运行该SQL时,它占用了存储过程所用的相同时间,为什么? SQL不应该花费更少的时间吗?为什么没有区别?
问题3:以上是获取汇总列值(“NumberOfVisits”)的最佳做法吗?
问题4:以上是动态创建WHERE子句的最佳方法吗?
问题5:在上述情况下,我可以通过使用替代方法来避免使用临时表吗?
问题6:如何优化此程序?
如果我的问题不明确或不合适,请原谅我。
感谢您宝贵的时间和帮助
答案 0 :(得分:1)
对于动态报告,最好设置SSRS(SQL Server Reporting Services),它是SQL Server 2005到2008 R2的一部分。然后使用BIDS创建一个数据模型项目来运行Report Builder。报表生成器附带SSRS,是一次性点击应用程序。
在这个时代,尝试通过本土查询跟上动态报告只是不再有效。
答案 1 :(得分:0)
同样的问题也被我发布在另一个网站上。 Grant Fritchey回答了这个问题 你可以看到答案here 我复制了该网站的答案,见下文:
通常,不是构建字符串并执行它,而是可以 受到SQL注入攻击,我建议建立一个字符串 使用参数并使用sp_executesql来执行它,提供 参数。这是更多的工作,但它更安全,而且,它更多 可能会看到改进的计划重用。
不,查询是执行的很长一段时间。建立一些 字符串非常无痛。
就汇总列而言,不,这是额外的通行证。相反,我 对子选择执行连接(通常,有时会破坏它) 分步可以更好地工作)。
不。往上看。 sp_executesql更好。
是的,请将其作为单一选择声明进行计划。临时表就是 持有聚合,可以作为派生表来完成。
优化?更严厉的问题。只要看看你有什么,CONVERT 关于e.EnquiryDate的陈述将导致扫描,无论如何。 如果这是一个datetime列,则需要将其与日期时间进行比较 价值,没有转换。除此之外,我没有看到没有看到 整个查询,执行计划,数据,结构,所有这些。
通常,这些捕获所有类型的查询都是非常有问题的。 而不是这样做,找出将要的常见模式 不可避免地存在,这三列总是进来,这只是一个 当另一个人进来等时进来,然后建立三个或四个 不同的过程考虑到这些模式和使用 这是一个包装器proc来确定它应该去哪些触发器 至。设置它将是一项工作,但不会超过这个,而且它会 表现更好,更容易维护。