sql 2008 查询性能优化笔记
索引:
set statistics io on
select p.productID,p.name,p.Weight,p.StandardCost from production.product p
where p.productID = 738 create index testPro_In on production.product(StandardCost)
/*大量的行返回使得非聚集索引,要用覆盖索引
不需要增加索引中列的数量或索引键的大小,因为所包含的列只保存在索引的叶子级别上。
include最好在以下情况使用:
1.不希望增加索引键的大小,但是仍然希望有一个覆盖索引
2.你打算索引一种不能被索引的数据类型(除了文本、ntext和图像)
3.你已经超过了一个索引的关键字列的最大数量
*/
SELECT TOP 1000
[CardNumber]
,[ExpMonth]
,[ExpYear]
FROM [AdventureWorks].[Sales].[CreditCard] cc
where cc.[ExpMonth] between 6 and 9
and cc.[ExpYear] = 2008
order by cc.[ExpMonth] drop index [Sales].[CreditCard].ix_test
--如果无include([CardNumber]),则逻辑读取 189次,有则逻辑读取 21 次
--注意创建索引的列的顺序先[ExpMonth],后[ExpYear]
create nonclustered index ix_test on [AdventureWorks].[Sales].[CreditCard](
[ExpMonth],[ExpYear]
)include([CardNumber]) SELECT
[CardNumber]
,[ExpMonth]
,[ExpYear]
FROM [AdventureWorks].[Sales].[CreditCard] cc
where cc.[CardNumber]=''
/*覆盖索引*/
select PostalCode from Person.Address where StateProvinceID=42 --drop index Person.Address. create nonclustered index IX_Address_StateProvinceID on Person.Address(StateProvinceID asc)
include(PostalCode)
with(drop_existing=on)
/*
索引交叉
可以再一个表上开发多个索引,然后使用一个连接算法来在两个子集中得到索引交叉
*/
--没有使用列SalesPersonID上的非聚集索引
select * from Sales.SalesOrderHeader
where SalesPersonID=276
and OrderDate between '4/1/2002' and '7/1/2002'
--OrderDate上的非聚集索引
create nonclustered index ix_testsa on Sales.SalesOrderHeader(OrderDate)
--在创建OrderDate上的非聚集索引后,sql使用2个非聚集索引进行索引查找(非扫描) /*
过滤索引
*/
--表 'SalesOrderHeader'。扫描计数 1,逻辑读取 686 次,物理读取 0 次
select OrderDate,ShipDate from Sales.SalesOrderHeader
where PurchaseOrderNumber like 'PO5%'
and SalesPersonID is not null
--表 'SalesOrderHeader'。扫描计数 1,逻辑读取 5 次,物理读取 0 次,
--create nonclustered index ix_test on Sales.SalesOrderHeader(PurchaseOrderNumber,SalesPersonID)
--include(OrderDate,ShipDate)
drop index Sales.SalesOrderHeader.ix_test create nonclustered index ix_test
on Sales.SalesOrderHeader(PurchaseOrderNumber,SalesPersonID)
include(OrderDate,ShipDate)
where PurchaseOrderNumber is not null
and SalesPersonID is not null dbcc show_statistics('[Sales].[SalesOrderDetail]',IX_SalesOrderDetail_ProductID2)
--drop index [Sales].[SalesOrderDetail].[IX_SalesOrderDetail_ProductID2] CREATE NONCLUSTERED INDEX [IX_SalesOrderDetail_ProductID2]
ON [Sales].[SalesOrderDetail] ([ProductID])
include([UnitPrice],[ModifiedDate]) set statistics io on
select p.PurchaseOrderID,p.VendorID,p.OrderDate
from Purchasing.PurchaseOrderHeader p
where p.VendorID = 85
and p.OrderDate < '12/5/2003' create nonclustered index ix_test on Purchasing.PurchaseOrderHeader
(OrderDate)
--开启更新统计
alter database AdventureWorks set auto_update_statistics on
/*
密度=1/列中不同值的数量
密度越低,越适合非聚集索引
select 1.0/count(distinct c1) from t1
*/
select 1.0/count(distinct VendorID) from Purchasing.PurchaseOrderHeader select * from sys.dm_db_index_physical_stats(db_id('CI-DNP02'),object_id('t1'),null,null,'sampled')
/*
avg_fragmentation_in_percent--表示索引和堆的逻辑平均碎片百分比,如是堆表且模式为samplyed,
则该值为NULL。如大于40%,可能要重建索引。
fragment_count--表示碎片的数量,或者组成索引和堆的单独页面组数。
page_count--组成统计的索引或数据页面数量的计数。
*/
select * from sys.dm_exec_cached_plans
/*
EXEC sp_executesql的运用: 此示例存储过程将动态生成并执行 INSERT 语句,以便向正确的表中插入新订单。
此示例使用订货日期生成应包含数据的表的名称,然后将此名称并入 INSERT 语句中
在该过程中使用 sp_executesql 比使用 EXECUTE 执行字符串更有效。
使用 sp_executesql 时,只生成 12 个版本的 INSERT 字符串,每个月的表对应 1 个字符串。
使用 EXECUTE 时,因为参数值不同,每个 INSERT 字符串均是唯一的。
尽管两种方法生成的批处理数相同,但由于 sp_executesql 生成的 INSERT 字符串类似,
因此,查询优化器更有可能重复使用执行计划。
*/
CREATE PROCEDURE InsertSales @PrmOrderID INT, @PrmCustomerID INT,
@PrmOrderDate DATETIME, @PrmDeliveryDate DATETIME
AS
DECLARE @InsertString NVARCHAR(500)
DECLARE @OrderMonth INT -- Build the INSERT statement.
SET @InsertString = 'INSERT INTO ' +
/* Build the name of the table. */
SUBSTRING( DATENAME(mm, @PrmOrderDate), 1, 3) +
CAST(DATEPART(yy, @PrmOrderDate) AS CHAR(4) ) +
'Sales' +
/* Build a VALUES clause. */
' VALUES (@InsOrderID, @InsCustID, @InsOrdDate,' +
' @InsOrdMonth, @InsDelDate)' /* Set the value to use for the order month because
functions are not allowed in the sp_executesql parameter
list. */
SET @OrderMonth = DATEPART(mm, @PrmOrderDate) EXEC sp_executesql @InsertString,
N'@InsOrderID INT, @InsCustID INT, @InsOrdDate DATETIME,
@InsOrdMonth INT, @InsDelDate DATETIME',
@PrmOrderID, @PrmCustomerID, @PrmOrderDate,
@OrderMonth, @PrmDeliveryDate GO
DECLARE @SQLString nvarchar(500);
DECLARE @ParmDefinition nvarchar(500);
DECLARE @SalesOrderNumber nvarchar(25);
DECLARE @IntVariable int;
SET @SQLString = N'SELECT @SalesOrderOUT = MAX(SalesOrderNumber)
FROM Sales.SalesOrderHeader
WHERE CustomerID = @CustomerID';
SET @ParmDefinition = N'@CustomerID int,
@SalesOrderOUT nvarchar(25) OUTPUT';
SET @IntVariable = 22276;
EXECUTE sp_executesql
@SQLString
,@ParmDefinition
,@CustomerID = @IntVariable
,@SalesOrderOUT = @SalesOrderNumber OUTPUT;
-- This SELECT statement returns the value of the OUTPUT parameter.
SELECT @SalesOrderNumber;
-- This SELECT statement uses the value of the OUTPUT parameter in
-- the WHERE clause.
SELECT OrderDate, TotalDue
FROM Sales.SalesOrderHeader
WHERE SalesOrderNumber = @SalesOrderNumber;
/*
引起pro重新编译的情况:
1.由于常规表引起的重新编译
2.由于局部临时表引起的重新编译
*/
IF(SELECT OBJECT_ID('dbo.p1')) IS NOT NULL
DROP PROC dbo.p1;
GO
CREATE PROC dbo.p1
AS
CREATE TABLE dbo.p1_t1(c1 INT); --Ensure table doesn't exist
SELECT * FROM dbo.p1_t1; --Causes recompilation,因为该pro中的表以前不存在,则
--计划不会包含引用该表的select语句的处理策略,因此为了执行select语句,该pro必须重新编译
DROP TABLE dbo.p1_t1;
GO EXEC dbo.p1; --First execution
EXEC dbo.p1; --Second execution --
IF(SELECT OBJECT_ID('dbo.p1')) IS NOT NULL
DROP PROC dbo.p1;
GO
CREATE PROC dbo.p1
AS
CREATE TABLE #p1_t1(c1 INT); --指定本地临时表
SELECT * FROM #p1_t1; --第一次执行时导致重新编译
DROP TABLE #p1_t1;--可选的,因为局部临时表在pro执行结束时自动卸载,这里是为良好的习惯卸载临时表
GO EXEC dbo.p1; --First execution
EXEC dbo.p1; --Second execution,注意,第二次执行时,没有被重新编译 /*
避免引起pro重新编译的情况:
1.不要交替使用ddl和dml语句
2.统计变化引起的
3.使用表变量,表变量特点:1.没有事物日志开销,2.没有锁开销,3.没有回滚开销,4.因为没统计,所以数据量不能大。
4.避免在pro中修改set选项
*/
IF (SELECT OBJECT_ID('dbo.spTempTable')
) IS NOT NULL
DROP PROC dbo.spTempTable
GO
CREATE PROC dbo.spTempTable
AS
CREATE TABLE #MyTempTable (ID INT, Dsc NVARCHAR(50))
INSERT INTO #MyTempTable (
ID,
Dsc
) SELECT ProductModelId, [Name]
FROM Production.ProductModel AS pm; --Needs 1st recompilation
SELECT * FROM #MyTempTable AS mtt; --Needs 2nd recompile
CREATE CLUSTERED INDEX iTest ON #MyTempTable (ID);
SELECT *
FROM #MyTempTable AS mtt; --Needs 3rd recompilation
CREATE TABLE #t2 (c1 INT);
SELECT *
FROM #t2;
--Needs 4th recompilation
GO EXEC spTempTable --First execution,一次执行该pro,被编译4次
/*
1.如果可以,就用between替换in/or,可进一步使用>=,<=的组合代替between
2.使用like替换substring
*/
SELECT d.Name
FROM HumanResources.Department AS d
WHERE SUBSTRING(d.[Name], 1, 1) = 'F' SELECT d.Name
FROM HumanResources.Department AS d
WHERE d.[Name] LIKE 'F%' --查找月份的方案:
SELECT soh.SalesOrderID
,soh.OrderDate
FROM Sales.SalesOrderHeader AS soh
JOIN Sales.SalesOrderDetail AS sod
ON soh.SalesOrderID = sod.SalesOrderID
--WHERE soh.OrderDate >= '2002-04-01' AND soh.OrderDate < '2002-05-01' --good action
WHERE DATEPART(yy, soh.OrderDate) = 2002
AND DATEPART(mm, soh.OrderDate) = 4--bad action SELECT p.FirstName
FROM Person.Person AS p
WHERE p.FirstName < 'B'
OR p.Firstname >= 'C'; SELECT p.MiddleName
FROM Person.Person AS p
WHERE p.MiddleName < 'B'
OR p.MiddleName >= 'C'
OR p.MiddleName IS NULL; CREATE INDEX IX_Test1 ON Person.Person (MiddleName);
CREATE INDEX IX_test2 ON Person.Person (FirstName); DROP INDEX person.Person.ix_test2
DROP INDEX Person.Person.IX_Test1
/*
使用exists代替count(*)验证数据存在
*/
DECLARE @n INT
SELECT @n = COUNT(*)
FROM Sales.SalesOrderDetail AS sod
WHERE sod.OrderQty = 1
IF @n > 0
PRINT 'Record Exists' IF EXISTS ( SELECT sod.*
FROM Sales.SalesOrderDetail AS sod
WHERE sod.OrderQty = 1 )
PRINT 'Record Exists' /*
union 从最终的结果集删除重复并且在每个查询上有效的运行distinct,如允许有重复,则用union all 代替
*/
/*
执行批或存储过程时,在批或存储过程中的每个查询执行之后,服务器报告所影响的行数。
可如下解决:
set nocount on
<sql queries>
set nocount off
*/
锁:
begin tran
delete from dbo.DatabaseLog where DatabaseLogID = 20
select * from sys.dm_tran_locks
rollback select object_name(N'') /*
下面的select语句获取的(s)锁不会保存到事务结束。在默认隔离级别read_committed 之下,
数据被select语句读出后(s)锁立即被释放。
*/
begin tran
select * from Production.Product where productID = 1
--其他查询
commit
/*
更新(u)模式,update:先(u)锁,后(x)锁。update执行中,只可(select)读,(u)锁阻止其他的update
insert,delete 执行开始就获取(x)锁。insert,delete执行中,所有的action都被阻止。
*/
BEGIN TRAN
--1. Read data to be modified using (S)lock instead of (U)lock.
-- Retain the (S)lock using REPEATABLEREAD locking hint,使用REPEATABLEREAD锁提示保持(s)锁
-- since the original (U)lock is retained until the conversion
-- to (X)lock.此后,原来的(u)锁被保持到转换为(x)锁
SELECT *
FROM t1 WITH (REPEATABLEREAD)
WHERE c1 = 1; --Allow another equivalent update action to start concurrently
--允许另一个等价的更新操作并发开始
WAITFOR DELAY '00:00:10'; --2. Modify the data by acquiring(获取) (X)lock
UPDATE t1 WITH (XLOCK)
SET c2 = GETDATE()
WHERE c1 = 1;
COMMIT /*
当一个select语句需要在获取数据后锁住该行数据,防被修改,可使用如下:
*/
begin tran
select * from Production.Product where productID = 1 with(updlock)--(u)锁,也可with(xlock)
--其他查询
commit
sql 2008 查询性能优化笔记的更多相关文章
- SQL SERVER 查询性能优化——分析事务与锁(五)
SQL SERVER 查询性能优化——分析事务与锁(一) SQL SERVER 查询性能优化——分析事务与锁(二) SQL SERVER 查询性能优化——分析事务与锁(三) 上接SQL SERVER ...
- SQL Server 查询性能优化 相关文章
来自: SQL Server 查询性能优化——堆表.碎片与索引(一) SQL Server 查询性能优化——堆表.碎片与索引(二) SQL Server 查询性能优化——覆盖索引(一) SQL Ser ...
- Sql Server查询性能优化之走出索引的误区
据了解绝大多数开发人员对于索引的理解都是一知半解,局限于大多数日常工作没有机会.也什么没有必要去关心.了解索引,实在哪天某个查询太慢了找到查询条件建个索引就ok,哪天又有个查询慢了,再建立个索引就是, ...
- SQL Server查询性能优化——堆表、碎片与索引(二)
本文是对 SQL Server查询性能优化——堆表.碎片与索引(一)的一些总结. 第一:先对 SQL Server查询性能优化——堆表.碎片与索引(一)中的例一的SET STATISTICS IO之 ...
- SQL Server查询性能优化——覆盖索引(二)
在SQL Server 查询性能优化——覆盖索引(一)中讲了覆盖索引的一些理论. 本文将具体讲一下使用不同索引对查询性能的影响. 下面通过实例,来查看不同的索引结构,如聚集索引.非聚集索引.组合索引等 ...
- SET STATISTICS IO和SET STATISTICS TIME 在SQL Server查询性能优化中的作用
近段时间以来,一直在探究SQL Server查询性能的问题,当然也漫无目的的查找了很多资料,也从网上的大神们的文章中学到了很多,在这里,向各位大神致敬.正是受大神们无私奉献精神的影响,所以小弟也作为回 ...
- SQl语句查询性能优化
[摘要]本文从DBMS的查询优化器对SQL查询语句进行性能优化的角度出发,结合数据库理论,从查询表达式及其多种查询条件组合对数据库查询性能优化进行分析,总结出多种提高数据库查询性能优化策略,介绍索引的 ...
- Sql Server查询性能优化之不可小觑的书签查找
小小程序猿SQL Server认知的成长 1.没毕业或工作没多久,只知道有数据库.SQL这么个东东,浑然分不清SQL和Sql Server Oracle.MySql的关系,通常认为SQL就是SQL S ...
- SQL Server 查询性能优化——覆盖索引
覆盖索引又可以称为索引覆盖. 解释一: 就是select的数据列只用从索引中就能够取得,不必从数据表中读取,换句话说查询列要被所使用的索引覆盖. 解释二: 索引是高效找到行的一个方法,当能通过检索索引 ...
随机推荐
- es6 Proxy
proxy在语言层面去操作一个对象 var user={}; user.fname='Bob'; user.lname="Wood"; user.fullName= functio ...
- noip2012
题解: 闲着无聊做了一遍noip2012 我觉得出题出的好奇怪啊... 为什么两道倍增两道二分答案??? 两天第一题: 第一天第一题傻逼普及组题没什么好说的了 第二天第一题你会扩欧就秒了 两天第二题: ...
- Python_datetime模块
datetime模块重新封装了time模块,提供更多的接口,提供的类有: date,表示日期的类 time,表示时间的类 datetime,表示日期和时间的类 timedelta,表示时间间隔,即两个 ...
- Codeforces Gym100783H 最短路 其他
原文链接https://www.cnblogs.com/zhouzhendong/p/CF-Gym100783H.html 题目传送门 - CF-Gym100783H 题意 给定一个 $n$ 个节点 ...
- HDU3488 Tour KM
原文链接http://www.cnblogs.com/zhouzhendong/p/8284304.html 题目传送门 - HDU3488 题意概括 给一个n的点m条边的有向图. 然后让你把这个图分 ...
- radio按钮单选效果
必须有name,并且是同一值,判断效果可用value值确定
- win 10 slmgr.vbs -xpr 无法运行,被豆麦笔记打开解决方法
win 10 slmgr.vbs -xpr 无法运行,被豆麦笔记打开解决方法 删除这个豆麦笔记 如果之前已经在 控制面板 程序中卸载过,那么是找不到的,我们先运行 slmgr.vbs -xpr,这个时 ...
- mysql的基础增删改查(一)
修改,操作表:1.建表:create table MyClass(id int(4) not null primary key auto_increment,name char(20) not nul ...
- php防攻击
客户端脚本植入 XSS跨站脚本攻击(跨站脚本攻击,输入(传入)自动执行恶意的HTML代码,如盗取用户Cookie.破坏页面结构.重定向到其它网站):过滤<,>&,"等特殊 ...
- Java BigDecimal详解,提供了丰富的四舍五入规则
java.math.BigDecimal类提供用于算术,刻度操作,舍入,比较,哈希算法和格式转换操作. toString()方法提供BigDecimal的规范表示.它使用户可以完全控制舍入行为. 提供 ...