日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

Microsoft SQL Server 2008技术内幕:T-SQL查询---------查询优化

發布時間:2024/10/12 数据库 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Microsoft SQL Server 2008技术内幕:T-SQL查询---------查询优化 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

示例數據庫:

-- Sample Data for this Chapter -- Listing 4-1: Creation Script for Sample Database and Tables SET NOCOUNT ON; USE master; IF DB_ID('Performance') IS NULLCREATE DATABASE Performance; GO USE Performance; GO-- Creating and Populating the Nums Auxiliary Table SET NOCOUNT ON; IF OBJECT_ID('dbo.Nums', 'U') IS NOT NULLDROP TABLE dbo.Nums; CREATE TABLE dbo.Nums(n INT NOT NULL PRIMARY KEY);DECLARE @max AS INT, @rc AS INT; SET @max = 1000000; SET @rc = 1;INSERT INTO dbo.Nums(n) VALUES(1); WHILE @rc * 2 <= @max BEGININSERT INTO dbo.Nums(n) SELECT n + @rc FROM dbo.Nums;SET @rc = @rc * 2; ENDINSERT INTO dbo.Nums(n)SELECT n + @rc FROM dbo.Nums WHERE n + @rc <= @max; GO-- Drop Data Tables if Exist IF OBJECT_ID('dbo.EmpOrders', 'V') IS NOT NULLDROP VIEW dbo.EmpOrders; GO IF OBJECT_ID('dbo.Orders', 'U') IS NOT NULLDROP TABLE dbo.Orders; GO IF OBJECT_ID('dbo.Customers', 'U') IS NOT NULLDROP TABLE dbo.Customers; GO IF OBJECT_ID('dbo.Employees', 'U') IS NOT NULLDROP TABLE dbo.Employees; GO IF OBJECT_ID('dbo.Shippers', 'U') IS NOT NULLDROP TABLE dbo.Shippers; GO-- Data Distribution Settings DECLARE@numorders AS INT,@numcusts AS INT,@numemps AS INT,@numshippers AS INT,@numyears AS INT,@startdate AS DATETIME;SELECT@numorders = 1000000,@numcusts = 20000,@numemps = 500,@numshippers = 5,@numyears = 4,@startdate = '20050101';-- Creating and Populating the Customers Table CREATE TABLE dbo.Customers (custid CHAR(11) NOT NULL,custname NVARCHAR(50) NOT NULL );INSERT INTO dbo.Customers(custid, custname)SELECT'C' + RIGHT('000000000' + CAST(n AS VARCHAR(10)), 10) AS custid,N'Cust_' + CAST(n AS VARCHAR(10)) AS custnameFROM dbo.NumsWHERE n <= @numcusts;ALTER TABLE dbo.Customers ADDCONSTRAINT PK_Customers PRIMARY KEY(custid);-- Creating and Populating the Employees Table CREATE TABLE dbo.Employees (empid INT NOT NULL,firstname NVARCHAR(25) NOT NULL,lastname NVARCHAR(25) NOT NULL );INSERT INTO dbo.Employees(empid, firstname, lastname)SELECT n AS empid,N'Fname_' + CAST(n AS NVARCHAR(10)) AS firstname,N'Lname_' + CAST(n AS NVARCHAR(10)) AS lastnameFROM dbo.NumsWHERE n <= @numemps;ALTER TABLE dbo.Employees ADDCONSTRAINT PK_Employees PRIMARY KEY(empid);-- Creating and Populating the Shippers Table CREATE TABLE dbo.Shippers (shipperid VARCHAR(5) NOT NULL,shippername NVARCHAR(50) NOT NULL );INSERT INTO dbo.Shippers(shipperid, shippername)SELECT shipperid, N'Shipper_' + shipperid AS shippernameFROM (SELECT CHAR(ASCII('A') - 2 + 2 * n) AS shipperidFROM dbo.NumsWHERE n <= @numshippers) AS D;ALTER TABLE dbo.Shippers ADDCONSTRAINT PK_Shippers PRIMARY KEY(shipperid);-- Creating and Populating the Orders Table CREATE TABLE dbo.Orders (orderid INT NOT NULL,custid CHAR(11) NOT NULL,empid INT NOT NULL,shipperid VARCHAR(5) NOT NULL,orderdate DATETIME NOT NULL,filler CHAR(155) NOT NULL DEFAULT('a') );INSERT INTO dbo.Orders(orderid, custid, empid, shipperid, orderdate)SELECT n AS orderid,'C' + RIGHT('000000000'+ CAST(1 + ABS(CHECKSUM(NEWID())) % @numcustsAS VARCHAR(10)), 10) AS custid,1 + ABS(CHECKSUM(NEWID())) % @numemps AS empid,CHAR(ASCII('A') - 2+ 2 * (1 + ABS(CHECKSUM(NEWID())) % @numshippers)) AS shipperid,DATEADD(day, n / (@numorders / (@numyears * 365.25)), @startdate)-- late arrival with earlier date- CASE WHEN n % 10 = 0THEN 1 + ABS(CHECKSUM(NEWID())) % 30ELSE 0 END AS orderdateFROM dbo.NumsWHERE n <= @numordersORDER BY CHECKSUM(NEWID());CREATE CLUSTERED INDEX idx_cl_od ON dbo.Orders(orderdate);CREATE NONCLUSTERED INDEX idx_nc_sid_od_i_cidON dbo.Orders(shipperid, orderdate)INCLUDE(custid);CREATE UNIQUE INDEX idx_unc_od_oid_i_cid_eidON dbo.Orders(orderdate, orderid)INCLUDE(custid, empid);ALTER TABLE dbo.Orders ADDCONSTRAINT PK_Orders PRIMARY KEY NONCLUSTERED(orderid),CONSTRAINT FK_Orders_CustomersFOREIGN KEY(custid) REFERENCES dbo.Customers(custid),CONSTRAINT FK_Orders_EmployeesFOREIGN KEY(empid) REFERENCES dbo.Employees(empid),CONSTRAINT FK_Orders_ShippersFOREIGN KEY(shipperid) REFERENCES dbo.Shippers(shipperid); GO

?快速建成1--100000數值表的語句

DECLARE @max AS INT, @rc AS INT;
SET @max = 1000000;
SET @rc = 1;

INSERT INTO dbo.Nums(n) VALUES(1);
WHILE @rc * 2 <= @max
BEGIN
? INSERT INTO dbo.Nums(n) SELECT n + @rc FROM dbo.Nums;
? SET @rc = @rc * 2;
END

INSERT INTO dbo.Nums(n)
? SELECT n + @rc FROM dbo.Nums WHERE n + @rc <= @max;
GO

生成隨機custId,empId,shipperid 的語句

1+ABS(CHECKSUM(NEWID())%@NUMBERMAX? 生成 1--->@NUMBERMAX內隨機整數

INSERT INTO dbo.Orders(orderid, custid, empid, shipperid, orderdate)
? SELECT n AS orderid,
??? 'C' + RIGHT('000000000'
??????????? + CAST(
??????????????? 1 + ABS(CHECKSUM(NEWID())) % @numcusts
??????????????? AS VARCHAR(10)), 10) AS custid,
??? 1 + ABS(CHECKSUM(NEWID())) % @numemps AS empid,
??? CHAR(ASCII('A') - 2
?????????? + 2 * (1 + ABS(CHECKSUM(NEWID())) % @numshippers)) AS shipperid,
????? DATEADD(day, n / (@numorders / (@numyears * 365.25)), @startdate)
??????? -- late arrival with earlier date

   ---在這個例子中是生成隨機數是四年,四年有只有一個閏年,平均每年365.25天,這樣跟據訂單號可以算出每個訂單大約放在哪一天,每個訂單號為10倍的時候作了一些處理,+-31天,所以10倍的訂單號有可能出現在上個月
??????? - CASE WHEN n % 10 = 0
??????????? THEN 1 + ABS(CHECKSUM(NEWID())) % 30
??????????? ELSE 0
????????? END AS orderdate
? FROM dbo.Nums
? WHERE n <= @numorders
? ORDER BY CHECKSUM(NEWID());

?優化方法論

返回系統里的等待信息

?

返回系統的等待信息 1 SELECT 2 wait_type, 3 waiting_tasks_count, 4 wait_time_ms, 5 max_wait_time_ms, 6 signal_wait_time_ms 7 FROM sys.dm_os_wait_stats 8 ORDER BY wait_type;

?

signal_wait_time_ms 表示從線程收到資源可用信號開始到得到CPU時間,開始資源為止經歷的時間,如果這個屬性很高,就表明CPU有問題.

分離重量級的等待

分離重量級的等待 -- Isolate top waits WITH Waits AS (SELECTwait_type,wait_time_ms / 1000. AS wait_time_s,100. * wait_time_ms / SUM(wait_time_ms) OVER() AS pct,ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS rn,100. * signal_wait_time_ms / wait_time_ms as signal_pctFROM sys.dm_os_wait_statsWHERE wait_time_ms > 0AND wait_type NOT LIKE N'%SLEEP%'AND wait_type NOT LIKE N'%IDLE%'AND wait_type NOT LIKE N'%QUEUE%' AND wait_type NOT IN( N'CLR_AUTO_EVENT', N'REQUEST_FOR_DEADLOCK_SEARCH', N'SQLTRACE_BUFFER_FLUSH'/* filter out additional irrelevant waits */ ) ) SELECTW1.wait_type, CAST(W1.wait_time_s AS NUMERIC(12, 2)) AS wait_time_s,CAST(W1.pct AS NUMERIC(5, 2)) AS pct,CAST(SUM(W2.pct) AS NUMERIC(5, 2)) AS running_pct,CAST(W1.signal_pct AS NUMERIC(5, 2)) AS signal_pct FROM Waits AS W1JOIN Waits AS W2ON W2.rn <= W1.rn GROUP BY W1.rn, W1.wait_type, W1.wait_time_s, W1.pct, W1.signal_pct HAVING SUM(W2.pct) - W1.pct < 80 -- percentage thresholdOR W1.rn <= 5 ORDER BY W1.rn; GO

這個語句返回累計等待時間(從最大的等待時間的等待類型向下加)占總等待時間達80%的等待類型 或 等待時間為前5位的等待類型

收集等待信息

這個可以使用SQL SERVER中的功能 Server Actity 來實現

也可以用一張表來不間斷的收集當前的累計等待時間,通過計算差值來算出間隔內的等待時間(Interval Wait Time)

創建WaitStats收集表 USE Performance; IF OBJECT_ID('dbo.WaitStats', 'U') IS NOT NULL DROP TABLE dbo.WaitStats;CREATE TABLE dbo.WaitStats (dt DATETIME NOT NULL DEFAULT (CURRENT_TIMESTAMP),wait_type NVARCHAR(60) NOT NULL,waiting_tasks_count BIGINT NOT NULL,wait_time_ms BIGINT NOT NULL,max_wait_time_ms BIGINT NOT NULL,signal_wait_time_ms BIGINT NOT NULL ); 定義一個時間間隔的任務(比如1小時),運行下面代碼,填充收集表 -- Load waitstats data on regular intervals INSERT INTO Performance.dbo.WaitStats(wait_type, waiting_tasks_count, wait_time_ms,max_wait_time_ms, signal_wait_time_ms)SELECTwait_type, waiting_tasks_count, wait_time_ms,max_wait_time_ms, signal_wait_time_msFROM sys.dm_os_wait_statsWHERE wait_type NOT IN (N'MISCELLANEOUS'); 創建下列函數,處理數據收集表,返回間隔等待信息 -- Creation script for IntervalWaits function IF OBJECT_ID('dbo.IntervalWaits', 'IF') IS NOT NULLDROP FUNCTION dbo.IntervalWaits; GOCREATE FUNCTION dbo.IntervalWaits(@fromdt AS DATETIME, @todt AS DATETIME) RETURNS TABLE ASRETURNWITH Waits AS(SELECT dt, wait_type, wait_time_ms,ROW_NUMBER() OVER(PARTITION BY wait_typeORDER BY dt) AS rnFROM dbo.WaitStats)SELECT Prv.wait_type, Prv.dt AS start_time,CAST((Cur.wait_time_ms - Prv.wait_time_ms)/ 1000. AS NUMERIC(12, 2)) AS interval_wait_sFROM Waits AS CurJOIN Waits AS PrvON Cur.wait_type = Prv.wait_typeAND Cur.rn = Prv.rn + 1AND Prv.dt >= @fromdtAND Prv.dt < DATEADD(day, 1, @todt) GO

可以使用簡單的查詢來分析間隔等待時間,比如下面的查詢,返回間隔內的新增等待時間,且按區間內的總新增時間倒序

使用收集表,按區間內所有間隔新增時間總和倒序顯示 SELECT wait_type, start_time, interval_wait_s FROM dbo.IntervalWaits('20090212', '20090213') AS F ORDER BY SUM(interval_wait_s) OVER(PARTITION BY wait_type) DESC,wait_type, start_time; GO

也可以將函數返回的表放在Excel中,通過Excel生成數據透視圖來查看新增等待時間信息

提供給Excel的數據視圖 -- Prepare view for pivot table IF OBJECT_ID('dbo.IntervalWaitsSample', 'V') IS NOT NULLDROP VIEW dbo.IntervalWaitsSample; GOCREATE VIEW dbo.IntervalWaitsSample ASSELECT wait_type, start_time, interval_wait_s FROM dbo.IntervalWaits('20090212', '20090213') AS F; GO

?細化到數據庫/文件級別

分析數據庫IO -- Analyze DB IO WITH DBIO AS (SELECTDB_NAME(IVFS.database_id) AS db,MF.type_desc,SUM(IVFS.num_of_bytes_read + IVFS.num_of_bytes_written) AS io_bytes,SUM(IVFS.io_stall) AS io_stall_msFROM sys.dm_io_virtual_file_stats(NULL, NULL) AS IVFSJOIN sys.master_files AS MFON IVFS.database_id = MF.database_idAND IVFS.file_id = MF.file_idGROUP BY DB_NAME(IVFS.database_id), MF.type_desc ) SELECT db, type_desc, CAST(1. * io_bytes / (1024 * 1024) AS NUMERIC(12, 2)) AS io_mb,CAST(io_stall_ms / 1000. AS NUMERIC(12, 2)) AS io_stall_s,CAST(100. * io_stall_ms / SUM(io_stall_ms) OVER()AS NUMERIC(10, 2)) AS io_stall_pct,ROW_NUMBER() OVER(ORDER BY io_stall_ms DESC) AS rn FROM DBIO ORDER BY io_stall_ms DESC;

細化到進程級別

直接使用SQL SERVER PROFILER GUI會有一些性能問題,因為輸出會同時到目標文件和客戶端,可以由GUI生成TSQL?腳本如下

針對生產環鏡中的工作負荷跟蹤有時需要幾小時,有的卻要幾天.

SQL TRACE跟蹤腳本過程 --------------------------------------------------------------------- -- Trace Performance Workload ---------------------------------------------------------------------SET NOCOUNT ON; USE master; GOIF OBJECT_ID('dbo.PerfworkloadTraceStart', 'P') IS NOT NULLDROP PROC dbo.PerfworkloadTraceStart; GOCREATE PROC dbo.PerfworkloadTraceStart@dbid AS INT,@tracefile AS NVARCHAR(245),@traceid AS INT OUTPUT AS-- Create a Queue DECLARE @rc AS INT; DECLARE @maxfilesize AS BIGINT;SET @maxfilesize = 5;EXEC @rc = sp_trace_create @traceid OUTPUT, 0, @tracefile, @maxfilesize, NULL IF (@rc != 0) GOTO error;-- Set the events DECLARE @on AS BIT; SET @on = 1;-- RPC:Completed exec sp_trace_setevent @traceid, 10, 15, @on; exec sp_trace_setevent @traceid, 10, 8, @on; exec sp_trace_setevent @traceid, 10, 16, @on; exec sp_trace_setevent @traceid, 10, 48, @on; exec sp_trace_setevent @traceid, 10, 1, @on; exec sp_trace_setevent @traceid, 10, 17, @on; exec sp_trace_setevent @traceid, 10, 10, @on; exec sp_trace_setevent @traceid, 10, 18, @on; exec sp_trace_setevent @traceid, 10, 11, @on; exec sp_trace_setevent @traceid, 10, 12, @on; exec sp_trace_setevent @traceid, 10, 13, @on; exec sp_trace_setevent @traceid, 10, 6, @on; exec sp_trace_setevent @traceid, 10, 14, @on;-- SP:Completed exec sp_trace_setevent @traceid, 43, 15, @on; exec sp_trace_setevent @traceid, 43, 8, @on; exec sp_trace_setevent @traceid, 43, 48, @on; exec sp_trace_setevent @traceid, 43, 1, @on; exec sp_trace_setevent @traceid, 43, 10, @on; exec sp_trace_setevent @traceid, 43, 11, @on; exec sp_trace_setevent @traceid, 43, 12, @on; exec sp_trace_setevent @traceid, 43, 13, @on; exec sp_trace_setevent @traceid, 43, 6, @on; exec sp_trace_setevent @traceid, 43, 14, @on;-- SP:StmtCompleted exec sp_trace_setevent @traceid, 45, 8, @on; exec sp_trace_setevent @traceid, 45, 16, @on; exec sp_trace_setevent @traceid, 45, 48, @on; exec sp_trace_setevent @traceid, 45, 1, @on; exec sp_trace_setevent @traceid, 45, 17, @on; exec sp_trace_setevent @traceid, 45, 10, @on; exec sp_trace_setevent @traceid, 45, 18, @on; exec sp_trace_setevent @traceid, 45, 11, @on; exec sp_trace_setevent @traceid, 45, 12, @on; exec sp_trace_setevent @traceid, 45, 13, @on; exec sp_trace_setevent @traceid, 45, 6, @on; exec sp_trace_setevent @traceid, 45, 14, @on; exec sp_trace_setevent @traceid, 45, 15, @on;-- SQL:BatchCompleted exec sp_trace_setevent @traceid, 12, 15, @on; exec sp_trace_setevent @traceid, 12, 8, @on; exec sp_trace_setevent @traceid, 12, 16, @on; exec sp_trace_setevent @traceid, 12, 48, @on; exec sp_trace_setevent @traceid, 12, 1, @on; exec sp_trace_setevent @traceid, 12, 17, @on; exec sp_trace_setevent @traceid, 12, 6, @on; exec sp_trace_setevent @traceid, 12, 10, @on; exec sp_trace_setevent @traceid, 12, 14, @on; exec sp_trace_setevent @traceid, 12, 18, @on; exec sp_trace_setevent @traceid, 12, 11, @on; exec sp_trace_setevent @traceid, 12, 12, @on; exec sp_trace_setevent @traceid, 12, 13, @on;-- SQL:StmtCompleted exec sp_trace_setevent @traceid, 41, 15, @on; exec sp_trace_setevent @traceid, 41, 8, @on; exec sp_trace_setevent @traceid, 41, 16, @on; exec sp_trace_setevent @traceid, 41, 48, @on; exec sp_trace_setevent @traceid, 41, 1, @on; exec sp_trace_setevent @traceid, 41, 17, @on; exec sp_trace_setevent @traceid, 41, 10, @on; exec sp_trace_setevent @traceid, 41, 18, @on; exec sp_trace_setevent @traceid, 41, 11, @on; exec sp_trace_setevent @traceid, 41, 12, @on; exec sp_trace_setevent @traceid, 41, 13, @on; exec sp_trace_setevent @traceid, 41, 6, @on; exec sp_trace_setevent @traceid, 41, 14, @on;-- Set the Filters-- Application name filter EXEC sp_trace_setfilter @traceid, 10, 0, 7, N'SQL Server Profiler%'; -- Database ID filter EXEC sp_trace_setfilter @traceid, 3, 0, 0, @dbid;-- Set the trace status to start EXEC sp_trace_setstatus @traceid, 1;-- Print trace id and file name for future references PRINT 'Trace ID: ' + CAST(@traceid AS VARCHAR(10))+ ', Trace File: ''' + @tracefile + '.trc''';GOTO finish;error: PRINT 'Error Code: ' + CAST(@rc AS VARCHAR(10));finish: GO

啟動跟蹤

View Code -- Start the trace DECLARE @dbid AS INT, @traceid AS INT; SET @dbid = DB_ID('Performance');EXEC master.dbo.PerfworkloadTraceStart@dbid = @dbid,@tracefile = 'c:\temp\Perfworkload 20090212',@traceid = @traceid OUTPUT; GO

停止跟蹤

停止和關閉跟蹤 -- Stop the trace (assuming trace id was 2) EXEC sp_trace_setstatus 2, 0; EXEC sp_trace_setstatus 2, 2; GO

分析跟跟數據,使用fn_trace_gettable函數把跟蹤文件加載到表

加載跟蹤文件進表 -- Load trace data to table SET NOCOUNT ON; USE Performance; IF OBJECT_ID('dbo.Workload', 'U') IS NOT NULL DROP TABLE dbo.Workload; GOSELECT CAST(TextData AS NVARCHAR(MAX)) AS tsql_code,Duration AS duration INTO dbo.Workload FROM sys.fn_trace_gettable('c:\temp\Perfworkload 20090212.trc', NULL) AS T WHERE Duration > 0AND EventClass IN(41, 45); GO

分析跟蹤表

分析跟蹤表 -- Aggregate trace data by query SELECTtsql_code,SUM(duration) AS total_duration FROM dbo.Workload GROUP BY tsql_code;

?緩存中的查詢計劃,和上面的SQL TRACE方法不同的是,這里不包含查詢計劃不在緩存中的查詢(例如查詢或過程使用的RECOMPILE項時)

DMV查詢統計 -- Query Statistics SELECT TOP (5)MAX(query) AS sample_query,SUM(execution_count) AS cnt,SUM(total_worker_time) AS cpu,SUM(total_physical_reads) AS reads,SUM(total_logical_reads) AS logical_reads,SUM(total_elapsed_time) AS duration FROM (SELECT QS.*,SUBSTRING(ST.text, (QS.statement_start_offset/2) + 1,((CASE statement_end_offset WHEN -1 THEN DATALENGTH(ST.text)ELSE QS.statement_end_offset END - QS.statement_start_offset)/2) + 1) AS queryFROM sys.dm_exec_query_stats AS QSCROSS APPLY sys.dm_exec_sql_text(QS.sql_handle) AS STCROSS APPLY sys.dm_exec_plan_attributes(QS.plan_handle) AS PAWHERE PA.attribute = 'dbid'AND PA.value = DB_ID('Performance')) AS D GROUP BY query_hash ORDER BY duration DESC;

物理讀: 由硬盤上的塊讀到緩存中,每讀一個塊就是一個物理讀

邏輯讀: 從緩存中每讀一行就是一次邏輯讀

所以要測量查詢的I/O信息需要先清空緩存

要查看查詢的運行時間還要先清空緩存中的執行計劃

查看查詢IO信息 -- First clear cache DBCC DROPCLEANBUFFERS;-- Then run SET STATISTICS IO ON;SELECT orderid, custid, empid, shipperid, orderdate, filler FROM dbo.Orders WHERE orderdate >= '20080101'AND orderdate < '20080201'; GOSET STATISTICS IO OFF; GO 查看查詢的運行時間 --------------------------------------------------------------------- -- Measuring Runtime of Queries ----------------------------------------------------------------------- STATISTICS TIME-- First clear cache DBCC DROPCLEANBUFFERS; DBCC FREEPROCCACHE;-- Then run SET STATISTICS TIME ON;SELECT orderid, custid, empid, shipperid, orderdate, filler FROM dbo.Orders WHERE orderdate >= '20080101'AND orderdate < '20080201';SET STATISTICS TIME OFF; GO

?

?

轉載于:https://www.cnblogs.com/Gravin-Gu/archive/2013/01/30/2883710.html

總結

以上是生活随笔為你收集整理的Microsoft SQL Server 2008技术内幕:T-SQL查询---------查询优化的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。