Here's a real life example showing NOT IN really hurts query performance.
Below is the SQL code snippet extracted from a query to generate a report, which is written by a programmer from external vendor (I don't wanna mention which one, although I'm quite disappointed at it's work):
SELECT ... FROM ... WHERE ... AND demand.time_expense_sheet_id NOT IN (SELECT order_id FROM c_order_line) ...
The c_order_line table contains 1 million of rows in our production environment. That query takes 2 minutes to execute. My boss asked me to troubleshoot it, as users complain again and again that the report cannot be loaded and timeout error raised.
First, I used SQL Profiler to trace what queries are really running, and which query is the slowest one.
I pick the slowest query, execute it on testing environment by using Management Studio, with the "Include Actual Execution Plan" enabled. Then I execute the query, and open the returned actual execution plan by SQL Sentry Plan Explorer (a free tools you must love), and identified the top operation was a Index Scan on that huge table c_order_line.
By checking the SQL query, I found that table was being used in a NOT IN clause. So I send these findings to that favorable vendor (just kidding), they replied me that NOT IN checking may be unnecessary (-_-"). Later they sent me the fix, just removed that NOT IN clause, that report now can be generated within 10 seconds. My users feel happy, my boss feel happy :)
2013-09-25
Get SQL Server Database Disk Space Usage
CREATE TABLE #DBInfo
( ServerName VARCHAR(100),
DatabaseName VARCHAR(100),
FileSizeMB INT,
LogicalFileName sysname,
PhysicalFileName NVARCHAR(520),
Status sysname,
Updateability sysname,
RecoveryMode sysname,
FreeSpaceMB INT,
FreeSpacePct VARCHAR(7),
FreeSpacePages INT,
PollDate datetime,
maxsize int,
growth int
)
DECLARE @command VARCHAR(5000)
SELECT @command = 'Use [' + '?' + '] SELECT
@@servername as ServerName,
' + '''' + '?' + '''' + ' AS DatabaseName,
CAST(sysfiles.size/128.0 AS int) AS FileSize,
sysfiles.name AS LogicalFileName, sysfiles.filename AS PhysicalFileName,
CONVERT(sysname,DatabasePropertyEx(''?'',''Status'')) AS Status,
CONVERT(sysname,DatabasePropertyEx(''?'',''Updateability'')) AS Updateability,
CONVERT(sysname,DatabasePropertyEx(''?'',''Recovery'')) AS RecoveryMode,
CAST(sysfiles.size/128.0 - CAST(FILEPROPERTY(sysfiles.name, ' + '''' +
'SpaceUsed' + '''' + ' ) AS int)/128.0 AS int) AS FreeSpaceMB,
CAST(100 * (CAST (((sysfiles.size/128.0 -CAST(FILEPROPERTY(sysfiles.name,
' + '''' + 'SpaceUsed' + '''' + ' ) AS int)/128.0)/(sysfiles.size/128.0))
AS decimal(4,2))) AS varchar(8)) + ' + '''' + '%' + '''' + ' AS FreeSpacePct,
GETDATE() as PollDate, maxsize,
CAST(sysfiles.growth/128.0 AS int) AS growth
FROM dbo.sysfiles'
INSERT INTO #DBInfo
(ServerName,
DatabaseName,
FileSizeMB,
LogicalFileName,
PhysicalFileName,
Status,
Updateability,
RecoveryMode,
FreeSpaceMB,
FreeSpacePct,
PollDate,
maxsize,
growth)
EXEC sp_MSForEachDB @command
SELECT
ServerName,
DatabaseName,
RTRIM(LogicalFileName) AS LogicalFileName,
FileSizeMB,
LEFT(PhysicalFileName, 1) AS Drive,
PhysicalFileName,
Status,
Updateability,
RecoveryMode,
FreeSpaceMB,
FreeSpacePct,
PollDate,
maxsize,
growth
FROM #DBInfo
ORDER BY
ServerName,
DatabaseName
DROP TABLE #DBInfo
EXEC master..xp_fixeddrives
( ServerName VARCHAR(100),
DatabaseName VARCHAR(100),
FileSizeMB INT,
LogicalFileName sysname,
PhysicalFileName NVARCHAR(520),
Status sysname,
Updateability sysname,
RecoveryMode sysname,
FreeSpaceMB INT,
FreeSpacePct VARCHAR(7),
FreeSpacePages INT,
PollDate datetime,
maxsize int,
growth int
)
DECLARE @command VARCHAR(5000)
SELECT @command = 'Use [' + '?' + '] SELECT
@@servername as ServerName,
' + '''' + '?' + '''' + ' AS DatabaseName,
CAST(sysfiles.size/128.0 AS int) AS FileSize,
sysfiles.name AS LogicalFileName, sysfiles.filename AS PhysicalFileName,
CONVERT(sysname,DatabasePropertyEx(''?'',''Status'')) AS Status,
CONVERT(sysname,DatabasePropertyEx(''?'',''Updateability'')) AS Updateability,
CONVERT(sysname,DatabasePropertyEx(''?'',''Recovery'')) AS RecoveryMode,
CAST(sysfiles.size/128.0 - CAST(FILEPROPERTY(sysfiles.name, ' + '''' +
'SpaceUsed' + '''' + ' ) AS int)/128.0 AS int) AS FreeSpaceMB,
CAST(100 * (CAST (((sysfiles.size/128.0 -CAST(FILEPROPERTY(sysfiles.name,
' + '''' + 'SpaceUsed' + '''' + ' ) AS int)/128.0)/(sysfiles.size/128.0))
AS decimal(4,2))) AS varchar(8)) + ' + '''' + '%' + '''' + ' AS FreeSpacePct,
GETDATE() as PollDate, maxsize,
CAST(sysfiles.growth/128.0 AS int) AS growth
FROM dbo.sysfiles'
INSERT INTO #DBInfo
(ServerName,
DatabaseName,
FileSizeMB,
LogicalFileName,
PhysicalFileName,
Status,
Updateability,
RecoveryMode,
FreeSpaceMB,
FreeSpacePct,
PollDate,
maxsize,
growth)
EXEC sp_MSForEachDB @command
SELECT
ServerName,
DatabaseName,
RTRIM(LogicalFileName) AS LogicalFileName,
FileSizeMB,
LEFT(PhysicalFileName, 1) AS Drive,
PhysicalFileName,
Status,
Updateability,
RecoveryMode,
FreeSpaceMB,
FreeSpacePct,
PollDate,
maxsize,
growth
FROM #DBInfo
ORDER BY
ServerName,
DatabaseName
DROP TABLE #DBInfo
EXEC master..xp_fixeddrives
2013-09-23
Get Table or Index name from resource Id returned by blocked process/deadlock report XML
Blocked Process Report and Deadlock Graph XML only report the affected object (table/index) as ID number, either object id, HOBT id, or page id. Below demonstrates how to resolve into object name.
Object ID (OBJECT:db_id:object_id)
SELECT OBJECT_NAME(db_id, object_id);
HOBT ID (KEY:db_id:hobt_id)
SELECT o.name AS TableName, i.name AS IndexName, SCHEMA_NAME(o.schema_id) AS SchemaName FROM sys.partitions p JOIN sys.objects o ON p.OBJECT_ID = o.OBJECT_ID JOIN sys.indexes i ON p.OBJECT_ID = i.OBJECT_ID AND p.index_id = i.index_id WHERE p.hobt_id = hobt_id
Page ID (PAGE:db_id:file_id:page_id) or Row ID (RID:db_id:file_id:page_id:slot)
DBCC TRACEON(3604)
GO
DBCC PAGE(db_id, file_id, page_id)
The object id will be shown in the Metadata: Object_Id = ???
The index id will be shown in the Metadata: IndexId = ???
Then you can check it by OBJECT_NAME function and sysindex dmv.
Ref.: http://support.microsoft.com/kb/224453
Object ID (OBJECT:db_id:object_id)
SELECT OBJECT_NAME(db_id, object_id);
HOBT ID (KEY:db_id:hobt_id)
SELECT o.name AS TableName, i.name AS IndexName, SCHEMA_NAME(o.schema_id) AS SchemaName FROM sys.partitions p JOIN sys.objects o ON p.OBJECT_ID = o.OBJECT_ID JOIN sys.indexes i ON p.OBJECT_ID = i.OBJECT_ID AND p.index_id = i.index_id WHERE p.hobt_id = hobt_id
Page ID (PAGE:db_id:file_id:page_id) or Row ID (RID:db_id:file_id:page_id:slot)
DBCC TRACEON(3604)
GO
DBCC PAGE(db_id, file_id, page_id)
The object id will be shown in the Metadata: Object_Id = ???
The index id will be shown in the Metadata: IndexId = ???
Then you can check it by OBJECT_NAME function and sysindex dmv.
Ref.: http://support.microsoft.com/kb/224453
2013-09-18
2013-09-17
SQL Server specific Performance Monitor (PerfMon) Counters
Processor:
- Process (sqlservr): % Processor Time (desired value: < 80% per core)
- Process (sqlservr): %Privileged Time (desired value: < 30%)
Memory:
- SQL Server:Buffer Manager: Buffer Cache Hit Ratio (desired value: > 90%)
- SQL Server:Buffer Manager: Page Life Expectancy (desired value: > 300 secs)
Disk:
- SQL Server:Latches: Total Latch Wait Time (ms)
- SQL Server:Latches: Latch Waits/sec (desired value: (Total Latch Wait Time) / (Latch Waits/Sec) < 10)
- Process (sqlservr): % Processor Time (desired value: < 80% per core)
- Process (sqlservr): %Privileged Time (desired value: < 30%)
Memory:
- SQL Server:Buffer Manager: Buffer Cache Hit Ratio (desired value: > 90%)
- SQL Server:Buffer Manager: Page Life Expectancy (desired value: > 300 secs)
Disk:
- SQL Server:Latches: Total Latch Wait Time (ms)
- SQL Server:Latches: Latch Waits/sec (desired value: (Total Latch Wait Time) / (Latch Waits/Sec) < 10)
2013-09-11
Subscribe to:
Posts (Atom)