上图显示了一个SQL命令的执行过程,为了能使每个语句能高效的执行,我们应该尽量在关系层来完成所有的操作。举个很简单的例子:
SELECT TOP 10 P.Name,P.Color,PSC.Name AS SubcategoryName,PC.Name AS CategoryName,D.DocumentSummary,
PP.LargePhoto,SUM(LineTotal) LineTotal
FROM Production.Product P
JOIN Production.ProductSubcategory PSC
ON P.ProductSubcategoryID = PSC.ProductSubcategoryID
JOIN Production.ProductCategory PC
ON PSC.ProductCategoryID = PC.ProductCategoryID
JOIN Production.ProductProductPhoto PPP
ON P.ProductID = PPP.ProductID
JOIN Production.ProductPhoto PP
ON PP.ProductPhotoID = PPP.ProductPhotoID
JOIN Sales.SalesOrderDetail OD
ON OD.ProductID = P.ProductID
LEFT JOIN Production.ProductDocument PD
ON PD.ProductID = P.ProductID
LEFT JOIN Production.Document D
ON D.DocumentID = PD.DocumentID
GROUP BY P.Name,P.Color,PSC.Name,PC.Name,D.DocumentSummary,
PP.LargePhoto
ORDER BY LineTotal DESC
此语句违反了尽量在关系层操作的宗旨,是把所有的结果取出后,再根据LineTotal进行排序。此语句因为排序后只取前10条结果,但是排序所针对的结果集是整个的查询结果。因此所操作的内存是很大的,因此我们应该减少排序结果集的大小。下面的查询首先在一个子查询中把前10个订单查出后,再和其它的表进行关系,减少了排序操作的内存量。两者的查询开销比例为99:1,可见我们在编写分页过程时首先要根据排序字段筛选出要关联的字段键值后再进行相关表的连接。
SELECT P.Name,P.Color,PSC.Name AS SubcategoryName,PC.Name AS CategoryName,D.DocumentSummary,
PP.LargePhoto,OD.LineTotal
FROM Production.Product P
JOIN Production.ProductSubcategory PSC
ON P.ProductSubcategoryID = PSC.ProductSubcategoryID
JOIN Production.ProductCategory PC
ON PSC.ProductCategoryID = PC.ProductCategoryID
JOIN Production.ProductProductPhoto PPP
ON P.ProductID = PPP.ProductID
JOIN Production.ProductPhoto PP
ON PP.ProductPhotoID = PPP.ProductPhotoID
JOIN
(
SELECT TOP 10 ProductID,SUM(LineTotal) LineTotal
FROM Sales.SalesOrderDetail
GROUP BY ProductID
ORDER BY LineTotal DESC
) OD
ON OD.ProductID = P.ProductID
LEFT JOIN Production.ProductDocument PD
ON PD.ProductID = P.ProductID
LEFT JOIN Production.Document D
ON D.DocumentID = PD.DocumentID
另外,在分页过程中会有一个记录总数,有的过程中会执行两次查询,一次返回记录数,一次查询根据筛选条件及当前需要返回其中哪些记录再进行一次查询。如果筛选条件不够精确,进行表扫描就很很耗时。因此不如一次查询把结果放入一个表变量中,此表变量中有个自增字段,根据当前第几页再筛选当前页的记录。使用@@rowcount得到总记录数。
关于@@rowcount的使用:对一个表,如果此记录存在就更新,如果不存在就插入记录。过程中可能会先使用if exists(select * from table where key='aa') update table
Else insert into table.
上面的查询其实多了一次操作。如果直接update后根据@@rowcount为0时。就直接插入,则避免了判断存在的查询。Update table if @@rowcount=0 insert into
在对链接数据库操作时,应该尽量减少操作的次数,这和从程序中发送命令到数据库一个道理。你在本机直接操作远程机的表时,所有的筛选条件都不会直接应用到远程机。而是把结果返回到本机后,再对所有远程表的数据进行筛选。因此,你只能调用远程机的存储过程才能高效的执行相关的命令。
很多过程只返回一条记录,然后作为一个结果集返回给程序,程序里不管使用datareader还是adapter,都是在操作一个结果集。这个结果集中包括一些元数据的信息,这些元数据就造成了额外的字节传送。因此,这样的过程中直接使用输出参数能减少传送的字节数。