view_IndexInformation.sql 64 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169
  1. -- 2012-03-19 Pedro Lopes (Microsoft) [email protected] (http://aka.ms/ezequiel)
  2. --
  3. -- All Databases index info, including duplicate, redundant, rarely used and unused indexes.
  4. --
  5. -- 4/5/2012 Simplified execution by subdividing input queries
  6. -- 4/5/2012 Fixed some collation issues;
  7. -- 4/6/2012 Split in separate listings the unused indexes from rarely used indexes; Split in separate list
  8. -- 6/6/2012 Fixed issue with partition aligned indexes
  9. -- 10/31/2012 Widened search for Redundant Indexes
  10. -- 12/17/2012 Fixed several issues
  11. -- 1/17/2013 Added several index related info
  12. -- 2/1/2013 Fixed issue with Heap identification
  13. -- 2/26/2013 Fixed issue with partition info; Removed alternate keys from search for Unused and Rarely used
  14. -- 4/17/2013 Added more information to duplicate and redundant indexes output, valuable when deciding which
  15. -- 4/19/2013 Fixed issue with potential duplicate index_ids in sys.dm_db_index_operational_stats relating t
  16. -- 5/6/2013 Changed data collection to minimize blocking potential on VLDBs.
  17. -- 5/20/2013 Fixed issue with database names with special characters.
  18. -- 5/29/2013 Fixed issue with large integers in aggregation.
  19. -- 6/20/2013 Added step to avoid entering in loop that generates dump in SQL 2005.
  20. -- 11/10/2013 Added index checks.
  21. -- 2/24/2014 Added info to Unused_IX section.
  22. -- 6/4/2014 Refined search for duplicate and redundant indexes.
  23. -- 11/12/2014 Added SQL 2014 Hash indexes support; changed scan mode to LIMITED; added search for hard coded
  24. -- 11/2/2016 Added support for SQL Server 2016 sys.dm_db_index_operational_stats changes; Added script creation.
  25. /*
  26. NOTE: on SQL Server 2005, be aware that querying sys.dm_db_index_usage_stats when it has large number of rows may lead to performance issues.
  27. URL: http://support.microsoft.com/kb/2003031
  28. */
  29. SET NOCOUNT ON;
  30. DECLARE @UpTime VARCHAR(12), @StartDate DATETIME, @sqlmajorver int, @sqlcmd NVARCHAR(4000), @params NVARCHAR(500)
  31. DECLARE @DatabaseName sysname, @indexName sysname
  32. SELECT @sqlmajorver = CONVERT(int, (@@microsoftversion / 0x1000000) & 0xff);
  33. IF @sqlmajorver = 9
  34. BEGIN
  35. SET @sqlcmd = N'SELECT @StartDateOUT = login_time, @UpTimeOUT = DATEDIFF(mi, login_time, GETDATE()) FROM master..sysprocesses WHERE spid = 1';
  36. END
  37. ELSE
  38. BEGIN
  39. SET @sqlcmd = N'SELECT @StartDateOUT = sqlserver_start_time, @UpTimeOUT = DATEDIFF(mi,sqlserver_start_time,GETDATE()) FROM sys.dm_os_sys_info';
  40. END
  41. SET @params = N'@StartDateOUT DATETIME OUTPUT, @UpTimeOUT VARCHAR(12) OUTPUT';
  42. EXECUTE sp_executesql @sqlcmd, @params, @StartDateOUT=@StartDate OUTPUT, @UpTimeOUT=@UpTime OUTPUT;
  43. SELECT @StartDate AS Collecting_Data_Since, CONVERT(VARCHAR(4),@UpTime/60/24) + 'd ' + CONVERT(VARCHAR(4),@UpTime/60%24) + 'h ' + CONVERT(VARCHAR(4),@UpTime%60) + 'm' AS Collecting_Data_For
  44. RAISERROR (N'Starting...', 10, 1) WITH NOWAIT
  45. DECLARE @dbid int--, @sqlcmd NVARCHAR(4000)
  46. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tblDatabases'))
  47. DROP TABLE #tblDatabases;
  48. IF NOT EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tblDatabases'))
  49. CREATE TABLE #tblDatabases (database_id int PRIMARY KEY, is_done bit)
  50. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tblWorking'))
  51. DROP TABLE #tblWorking;
  52. IF NOT EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tblWorking'))
  53. CREATE TABLE #tblWorking (database_id int, [object_id] int, [object_name] NVARCHAR(255), index_id int, index_name NVARCHAR(255), [schema_name] NVARCHAR(255), partition_number int, is_done bit)
  54. INSERT INTO #tblDatabases
  55. SELECT database_id, 0 FROM sys.databases WHERE is_read_only = 0 AND state = 0 AND database_id > 4 AND is_distributor = 0;
  56. RAISERROR (N'Populating support tables...', 10, 1) WITH NOWAIT
  57. WHILE (SELECT COUNT(*) FROM #tblDatabases WHERE is_done = 0) > 0
  58. BEGIN
  59. SELECT TOP 1 @dbid = database_id FROM #tblDatabases WHERE is_done = 0
  60. SELECT @sqlcmd = 'SELECT ' + CONVERT(NVARCHAR(255), @dbid) + ', si.[object_id], mst.[name], si.index_id, si.name, t.name, sp.partition_number, 0
  61. FROM [' + DB_NAME(@dbid) + '].sys.indexes si
  62. INNER JOIN [' + DB_NAME(@dbid) + '].sys.partitions sp ON si.[object_id] = sp.[object_id] AND si.index_id = sp.index_id
  63. INNER JOIN [' + DB_NAME(@dbid) + '].sys.tables AS mst ON mst.[object_id] = si.[object_id]
  64. INNER JOIN [' + DB_NAME(@dbid) + '].sys.schemas AS t ON t.[schema_id] = mst.[schema_id]
  65. WHERE mst.is_ms_shipped = 0'
  66. INSERT INTO #tblWorking
  67. EXEC sp_executesql @sqlcmd;
  68. UPDATE #tblDatabases
  69. SET is_done = 1
  70. WHERE database_id = @dbid;
  71. END
  72. --------------------------------------------------------
  73. -- Index physical and usage stats
  74. --------------------------------------------------------
  75. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpIPS'))
  76. DROP TABLE #tmpIPS;
  77. IF NOT EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpIPS'))
  78. CREATE TABLE #tmpIPS (
  79. [database_id] int,
  80. [object_id] int,
  81. [index_id] int,
  82. [partition_number] int,
  83. fragmentation DECIMAL(18,3),
  84. [page_count] bigint,
  85. [size_MB] DECIMAL(26,3),
  86. record_count int,
  87. forwarded_record_count int NULL,
  88. CONSTRAINT PK_IPS PRIMARY KEY CLUSTERED(database_id, [object_id], [index_id], [partition_number]))
  89. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpIOS'))
  90. DROP TABLE #tmpIOS;
  91. IF NOT EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpIOS'))
  92. CREATE TABLE #tmpIOS (
  93. [database_id] int,
  94. [object_id] int,
  95. [index_id] int,
  96. [partition_number] int,
  97. range_scan_count bigint NULL,
  98. singleton_lookup_count bigint NULL,
  99. forwarded_fetch_count bigint NULL,
  100. row_lock_count bigint NULL,
  101. row_lock_wait_count bigint NULL,
  102. row_lock_pct NUMERIC(15,2) NULL,
  103. row_lock_wait_in_ms bigint NULL,
  104. [avg_row_lock_waits_in_ms] NUMERIC(15,2) NULL,
  105. page_lock_count bigint NULL,
  106. page_lock_wait_count bigint NULL,
  107. page_lock_pct NUMERIC(15,2) NULL,
  108. page_lock_wait_in_ms bigint NULL,
  109. [avg_page_lock_waits_in_ms] NUMERIC(15,2) NULL,
  110. page_io_latch_wait_in_ms bigint NULL,
  111. [avg_page_io_latch_wait_in_ms] NUMERIC(15,2) NULL
  112. CONSTRAINT PK_IOS PRIMARY KEY CLUSTERED(database_id, [object_id], [index_id], [partition_number]));
  113. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpIUS'))
  114. DROP TABLE #tmpIUS;
  115. IF NOT EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpIUS'))
  116. CREATE TABLE #tmpIUS (
  117. [database_id] int,
  118. [schema_name] VARCHAR(100) COLLATE database_default,
  119. [object_id] int,
  120. [index_id] int,
  121. [Hits] bigint NULL,
  122. [Reads_Ratio] DECIMAL(5,2),
  123. [Writes_Ratio] DECIMAL(5,2),
  124. user_updates bigint,
  125. last_user_seek DATETIME NULL,
  126. last_user_scan DATETIME NULL,
  127. last_user_lookup DATETIME NULL,
  128. last_user_update DATETIME NULL
  129. CONSTRAINT PK_IUS PRIMARY KEY CLUSTERED(database_id, [object_id], [index_id]));
  130. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpIxs'))
  131. DROP TABLE #tmpIxs;
  132. IF NOT EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpIxs'))
  133. CREATE TABLE #tmpIxs (
  134. [database_id] int,
  135. [database_name] VARCHAR(500),
  136. [object_id] int,
  137. [schema_name] VARCHAR(100) COLLATE database_default,
  138. [table_name] VARCHAR(300) COLLATE database_default,
  139. [index_id] int,
  140. [index_name] VARCHAR(300) COLLATE database_default,
  141. [partition_number] int,
  142. [index_type] tinyint,
  143. type_desc NVARCHAR(30),
  144. is_primary_key bit,
  145. is_unique_constraint bit,
  146. is_disabled bit,
  147. fill_factor tinyint,
  148. is_unique bit,
  149. is_padded bit,
  150. has_filter bit,
  151. filter_definition NVARCHAR(max),
  152. KeyCols VARCHAR(4000),
  153. KeyColsOrdered VARCHAR(4000),
  154. IncludedCols VARCHAR(4000) NULL,
  155. IncludedColsOrdered VARCHAR(4000) NULL,
  156. AllColsOrdered VARCHAR(4000) NULL,
  157. [KeyCols_data_length_bytes] int,
  158. CONSTRAINT PK_Ixs PRIMARY KEY CLUSTERED(database_id, [object_id], [index_id], [partition_number]));
  159. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpAgg'))
  160. DROP TABLE #tmpAgg;
  161. IF NOT EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpAgg'))
  162. CREATE TABLE #tmpAgg (
  163. [database_id] int,
  164. [database_name] sysname,
  165. [object_id] int,
  166. [schema_name] VARCHAR(100) COLLATE database_default,
  167. [table_name] VARCHAR(300) COLLATE database_default,
  168. [index_id] int,
  169. [index_name] VARCHAR(300) COLLATE database_default,
  170. [partition_number] int,
  171. fragmentation DECIMAL(18,3),
  172. fill_factor tinyint,
  173. [page_count] bigint,
  174. [size_MB] DECIMAL(26,3),
  175. record_count bigint,
  176. forwarded_record_count bigint NULL,
  177. range_scan_count bigint NULL,
  178. singleton_lookup_count bigint NULL,
  179. forwarded_fetch_count bigint NULL,
  180. row_lock_count bigint NULL,
  181. row_lock_wait_count bigint NULL,
  182. row_lock_pct NUMERIC(15,2) NULL,
  183. row_lock_wait_in_ms bigint NULL,
  184. [avg_row_lock_waits_in_ms] NUMERIC(15,2) NULL,
  185. page_lock_count bigint NULL,
  186. page_lock_wait_count bigint NULL,
  187. page_lock_pct NUMERIC(15,2) NULL,
  188. page_lock_wait_in_ms bigint NULL,
  189. [avg_page_lock_waits_in_ms] NUMERIC(15,2) NULL,
  190. page_io_latch_wait_in_ms bigint NULL,
  191. [avg_page_io_latch_wait_in_ms] NUMERIC(15,2) NULL,
  192. [Hits] bigint NULL,
  193. [Reads_Ratio] DECIMAL(5,2),
  194. [Writes_Ratio] DECIMAL(5,2),
  195. user_updates bigint,
  196. last_user_seek DATETIME NULL,
  197. last_user_scan DATETIME NULL,
  198. last_user_lookup DATETIME NULL,
  199. last_user_update DATETIME NULL,
  200. KeyCols VARCHAR(4000) COLLATE database_default,
  201. KeyColsOrdered VARCHAR(4000) COLLATE database_default,
  202. IncludedCols VARCHAR(4000) COLLATE database_default NULL,
  203. IncludedColsOrdered VARCHAR(4000) COLLATE database_default NULL,
  204. AllColsOrdered VARCHAR(4000) COLLATE database_default NULL,
  205. is_unique bit,
  206. [type] tinyint,
  207. type_desc NVARCHAR(30),
  208. is_primary_key bit,
  209. is_unique_constraint bit,
  210. is_padded bit,
  211. has_filter bit,
  212. filter_definition NVARCHAR(max),
  213. is_disabled bit,
  214. [KeyCols_data_length_bytes] int,
  215. CONSTRAINT PK_tmpAgg PRIMARY KEY CLUSTERED(database_id, [object_id], [index_id], [partition_number]));
  216. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tblCode'))
  217. DROP TABLE #tblCode;
  218. IF NOT EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tblCode'))
  219. CREATE TABLE #tblCode (
  220. [DatabaseName] sysname,
  221. [schemaName] VARCHAR(100),
  222. [objectName] VARCHAR(200),
  223. [indexName] VARCHAR(200),
  224. type_desc NVARCHAR(60));
  225. IF @sqlmajorver >= 12
  226. BEGIN
  227. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpXIS'))
  228. DROP TABLE #tmpXIS;
  229. IF NOT EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpXIS'))
  230. CREATE TABLE #tmpXIS (
  231. [database_id] int,
  232. [object_id] int,
  233. [schema_name] VARCHAR(100) COLLATE database_default,
  234. [table_name] VARCHAR(300) COLLATE database_default,
  235. [index_id] int,
  236. [index_name] VARCHAR(300) COLLATE database_default,
  237. total_bucket_count bigint,
  238. empty_bucket_count bigint,
  239. avg_chain_length bigint,
  240. max_chain_length bigint,
  241. scans_started bigint,
  242. scans_retries bigint,
  243. rows_returned bigint,
  244. rows_touched bigint,
  245. CONSTRAINT PK_tmpXIS PRIMARY KEY CLUSTERED(database_id, [object_id], [index_id]));
  246. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpXNCIS'))
  247. DROP TABLE #tmpXNCIS;
  248. IF NOT EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpXNCIS'))
  249. CREATE TABLE #tmpXNCIS (
  250. [database_id] int,
  251. [object_id] int,
  252. [schema_name] VARCHAR(100) COLLATE database_default,
  253. [table_name] VARCHAR(300) COLLATE database_default,
  254. [index_id] int,
  255. [index_name] VARCHAR(300) COLLATE database_default,
  256. delta_pages bigint,
  257. internal_pages bigint,
  258. leaf_pages bigint,
  259. page_update_count bigint,
  260. page_update_retry_count bigint,
  261. page_consolidation_count bigint,
  262. page_consolidation_retry_count bigint,
  263. page_split_count bigint,
  264. page_split_retry_count bigint,
  265. key_split_count bigint,
  266. key_split_retry_count bigint,
  267. page_merge_count bigint,
  268. page_merge_retry_count bigint,
  269. key_merge_count bigint,
  270. key_merge_retry_count bigint,
  271. scans_started bigint,
  272. scans_retries bigint,
  273. rows_returned bigint,
  274. rows_touched bigint,
  275. CONSTRAINT PK_tmpXNCIS PRIMARY KEY CLUSTERED(database_id, [object_id], [index_id]));
  276. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpAggXTPHash'))
  277. DROP TABLE #tmpAggXTPHash;
  278. IF NOT EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpAggXTPHash'))
  279. CREATE TABLE #tmpAggXTPHash (
  280. [database_id] int,
  281. [database_name] sysname,
  282. [object_id] int,
  283. [schema_name] VARCHAR(100) COLLATE database_default,
  284. [table_name] VARCHAR(300) COLLATE database_default,
  285. [index_id] int,
  286. [index_name] VARCHAR(300) COLLATE database_default,
  287. total_bucket_count bigint,
  288. empty_bucket_count bigint,
  289. avg_chain_length bigint,
  290. max_chain_length bigint,
  291. scans_started bigint,
  292. scans_retries bigint,
  293. rows_returned bigint,
  294. rows_touched bigint,
  295. KeyCols VARCHAR(4000) COLLATE database_default,
  296. KeyColsOrdered VARCHAR(4000) COLLATE database_default,
  297. IncludedCols VARCHAR(4000) COLLATE database_default NULL,
  298. IncludedColsOrdered VARCHAR(4000) COLLATE database_default NULL,
  299. AllColsOrdered VARCHAR(4000) COLLATE database_default NULL,
  300. is_unique bit,
  301. [type] tinyint,
  302. type_desc NVARCHAR(30),
  303. is_primary_key bit,
  304. is_unique_constraint bit,
  305. is_padded bit,
  306. has_filter bit,
  307. filter_definition NVARCHAR(max),
  308. is_disabled bit,
  309. [KeyCols_data_length_bytes] int,
  310. CONSTRAINT PK_tmpAggXTPHash PRIMARY KEY CLUSTERED(database_id, [object_id], [index_id]));
  311. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpAggXTPNC'))
  312. DROP TABLE #tmpAggXTPNC;
  313. IF NOT EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpAggXTPNC'))
  314. CREATE TABLE #tmpAggXTPNC (
  315. [database_id] int,
  316. [database_name] sysname,
  317. [object_id] int,
  318. [schema_name] VARCHAR(100) COLLATE database_default,
  319. [table_name] VARCHAR(300) COLLATE database_default,
  320. [index_id] int,
  321. [index_name] VARCHAR(300) COLLATE database_default,
  322. delta_pages bigint,
  323. internal_pages bigint,
  324. leaf_pages bigint,
  325. page_update_count bigint,
  326. page_update_retry_count bigint,
  327. page_consolidation_count bigint,
  328. page_consolidation_retry_count bigint,
  329. page_split_count bigint,
  330. page_split_retry_count bigint,
  331. key_split_count bigint,
  332. key_split_retry_count bigint,
  333. page_merge_count bigint,
  334. page_merge_retry_count bigint,
  335. key_merge_count bigint,
  336. key_merge_retry_count bigint,
  337. scans_started bigint,
  338. scans_retries bigint,
  339. rows_returned bigint,
  340. rows_touched bigint,
  341. KeyCols VARCHAR(4000) COLLATE database_default,
  342. KeyColsOrdered VARCHAR(4000) COLLATE database_default,
  343. IncludedCols VARCHAR(4000) COLLATE database_default NULL,
  344. IncludedColsOrdered VARCHAR(4000) COLLATE database_default NULL,
  345. AllColsOrdered VARCHAR(4000) COLLATE database_default NULL,
  346. is_unique bit,
  347. [type] tinyint,
  348. type_desc NVARCHAR(30),
  349. is_primary_key bit,
  350. is_unique_constraint bit,
  351. is_padded bit,
  352. has_filter bit,
  353. filter_definition NVARCHAR(max),
  354. is_disabled bit,
  355. [KeyCols_data_length_bytes] int,
  356. CONSTRAINT PK_tmpAggXTPNC PRIMARY KEY CLUSTERED(database_id, [object_id], [index_id]));
  357. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpHashIxs'))
  358. DROP TABLE #tmpHashIxs;
  359. IF NOT EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpHashIxs'))
  360. CREATE TABLE #tmpHashIxs (
  361. [database_id] int,
  362. [database_name] VARCHAR(500),
  363. [object_id] int,
  364. [schema_name] VARCHAR(100) COLLATE database_default,
  365. [table_name] VARCHAR(300) COLLATE database_default,
  366. [index_id] int,
  367. [index_name] VARCHAR(300) COLLATE database_default,
  368. [partition_number] int,
  369. [index_type] tinyint,
  370. type_desc NVARCHAR(30),
  371. is_primary_key bit,
  372. is_unique_constraint bit,
  373. is_disabled bit,
  374. fill_factor tinyint,
  375. is_unique bit,
  376. is_padded bit,
  377. has_filter bit,
  378. filter_definition NVARCHAR(max),
  379. [bucket_count] bigint,
  380. KeyCols VARCHAR(4000),
  381. KeyColsOrdered VARCHAR(4000),
  382. IncludedCols VARCHAR(4000) NULL,
  383. IncludedColsOrdered VARCHAR(4000) NULL,
  384. AllColsOrdered VARCHAR(4000) NULL,
  385. [KeyCols_data_length_bytes] int,
  386. CONSTRAINT PK_HashIxs PRIMARY KEY CLUSTERED(database_id, [object_id], [index_id], [partition_number]));
  387. END;
  388. DECLARE /*@dbid int, */@objectid int, @indexid int, @partition_nr int, @dbname NVARCHAR(255), @oname NVARCHAR(255), @iname NVARCHAR(255), @sname NVARCHAR(255)
  389. RAISERROR (N'Gathering sys.dm_db_index_physical_stats and sys.dm_db_index_operational_stats data...', 10, 1) WITH NOWAIT
  390. WHILE (SELECT COUNT(*) FROM #tblWorking WHERE is_done = 0) > 0
  391. BEGIN
  392. SELECT TOP 1 @dbid = database_id, @objectid = [object_id], @indexid = index_id, @partition_nr = partition_number, @oname = [object_name], @iname = index_name, @sname = [schema_name]
  393. FROM #tblWorking WHERE is_done = 0
  394. INSERT INTO #tmpIPS
  395. SELECT ps.database_id,
  396. ps.[object_id],
  397. ps.index_id,
  398. ps.partition_number,
  399. SUM(ps.avg_fragmentation_in_percent),
  400. SUM(ps.page_count),
  401. CAST((SUM(ps.page_count)*8)/1024 AS DECIMAL(26,3)) AS [size_MB],
  402. SUM(ISNULL(ps.record_count,0)),
  403. SUM(ISNULL(ps.forwarded_record_count,0)) -- for heaps
  404. FROM sys.dm_db_index_physical_stats(@dbid, @objectid, @indexid , @partition_nr, 'SAMPLED') AS ps
  405. WHERE /*ps.index_id > 0 -- ignore heaps
  406. AND */ps.index_level = 0 -- leaf-level nodes only
  407. AND ps.alloc_unit_type_desc = 'IN_ROW_DATA'
  408. GROUP BY ps.database_id, ps.[object_id], ps.index_id, ps.partition_number
  409. OPTION (MAXDOP 2);
  410. -- Avoid entering in loop that generates dump in SQL 2005
  411. IF @sqlmajorver = 9
  412. BEGIN
  413. SET @sqlcmd = (SELECT 'USE [' + DB_NAME(@dbid) + '];
  414. UPDATE STATISTICS ' + QUOTENAME(@sname) + '.' + QUOTENAME(@oname) + CASE WHEN @iname IS NULL THEN '' ELSE ' (' + QUOTENAME(@iname) + ')' END)
  415. EXEC sp_executesql @sqlcmd
  416. END;
  417. SET @sqlcmd = N'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  418. USE [' + DB_NAME(@dbid) + '];
  419. WITH osCTE (database_id, [object_id], index_id, partition_number, range_scan_count, singleton_lookup_count,
  420. forwarded_fetch_count, row_lock_count, row_lock_wait_count, row_lock_wait_in_ms, page_lock_count,
  421. page_lock_wait_count, page_lock_wait_in_ms, page_io_latch_wait_count, page_io_latch_wait_in_ms)
  422. AS (SELECT os.database_id,
  423. os.[object_id],
  424. os.index_id,
  425. os.partition_number,
  426. SUM(os.range_scan_count),
  427. SUM(os.singleton_lookup_count),
  428. SUM(os.forwarded_fetch_count),
  429. SUM(os.row_lock_count),
  430. SUM(os.row_lock_wait_count),
  431. SUM(os.row_lock_wait_in_ms),
  432. SUM(os.page_lock_count),
  433. SUM(os.page_lock_wait_count),
  434. SUM(os.page_lock_wait_in_ms),
  435. SUM(os.page_io_latch_wait_count),
  436. SUM(os.page_io_latch_wait_in_ms)
  437. FROM sys.dm_db_index_operational_stats(' + CONVERT(NVARCHAR(20), @dbid) + ', ' + CONVERT(NVARCHAR(20), @objectid) + ', ' + CONVERT(NVARCHAR(20), @indexid) + ', ' + CONVERT(NVARCHAR(20), @partition_nr) + ') AS os
  438. INNER JOIN sys.objects AS o WITH (NOLOCK) ON os.[object_id] = o.[object_id]
  439. ' + CASE WHEN @sqlmajorver >= 13 THEN 'LEFT JOIN sys.internal_partitions AS ip WITH (NOLOCK) ON os.hobt_id = ip.hobt_id AND ip.internal_object_type IN (2,3)' ELSE '' END + '
  440. WHERE o.[type] = ''U''
  441. GROUP BY os.database_id, os.[object_id], os.index_id, os.partition_number
  442. )
  443. SELECT osCTE.database_id,
  444. osCTE.[object_id],
  445. osCTE.index_id,
  446. osCTE.partition_number,
  447. osCTE.range_scan_count,
  448. osCTE.singleton_lookup_count,
  449. osCTE.forwarded_fetch_count,
  450. osCTE.row_lock_count,
  451. osCTE.row_lock_wait_count,
  452. CAST(100.0 * osCTE.row_lock_wait_count / (1 + osCTE.row_lock_count) AS numeric(15,2)) AS row_lock_pct,
  453. osCTE.row_lock_wait_in_ms,
  454. CAST(1.0 * osCTE.row_lock_wait_in_ms / (1 + osCTE.row_lock_wait_count) AS numeric(15,2)) AS [avg_row_lock_waits_in_ms],
  455. osCTE.page_lock_count,
  456. osCTE.page_lock_wait_count,
  457. CAST(100.0 * osCTE.page_lock_wait_count / (1 + osCTE.page_lock_count) AS numeric(15,2)) AS page_lock_pct,
  458. osCTE.page_lock_wait_in_ms,
  459. CAST(1.0 * osCTE.page_lock_wait_in_ms / (1 + osCTE.page_lock_wait_count) AS numeric(15,2)) AS [avg_page_lock_waits_in_ms],
  460. osCTE.page_io_latch_wait_in_ms,
  461. CAST(1.0 * osCTE.page_io_latch_wait_in_ms / (1 + osCTE.page_io_latch_wait_count) AS numeric(15,2)) AS [avg_page_io_latch_wait_in_ms]
  462. FROM osCTE
  463. --WHERE os.index_id > 0 -- ignore heaps
  464. OPTION (MAXDOP 2);'
  465. INSERT INTO #tmpIOS
  466. EXEC sp_executesql @sqlcmd
  467. UPDATE #tblWorking
  468. SET is_done = 1
  469. WHERE database_id = @dbid AND [object_id] = @objectid AND index_id = @indexid AND partition_number = @partition_nr
  470. END;
  471. RAISERROR (N'Gathering sys.dm_db_index_usage_stats data...', 10, 1) WITH NOWAIT
  472. UPDATE #tblDatabases
  473. SET is_done = 0;
  474. WHILE (SELECT COUNT(*) FROM #tblDatabases WHERE is_done = 0) > 0
  475. BEGIN
  476. SELECT TOP 1 @dbid = database_id FROM #tblDatabases WHERE is_done = 0
  477. SELECT @dbname = DB_NAME(@dbid)
  478. SET @sqlcmd = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  479. USE [' + @dbname + '];
  480. SELECT s.database_id, t.name, s.[object_id], s.index_id,
  481. (s.user_seeks + s.user_scans + s.user_lookups) AS [Hits],
  482. RTRIM(CONVERT(NVARCHAR(20),CAST(CASE WHEN (s.user_seeks + s.user_scans + s.user_lookups) = 0 THEN 0 ELSE CONVERT(REAL, (s.user_seeks + s.user_scans + s.user_lookups)) * 100 /
  483. CASE (s.user_seeks + s.user_scans + s.user_lookups + s.user_updates) WHEN 0 THEN 1 ELSE CONVERT(REAL, (s.user_seeks + s.user_scans + s.user_lookups + s.user_updates)) END END AS DECIMAL(18,2))) COLLATE database_default) AS [Reads_Ratio],
  484. RTRIM(CONVERT(NVARCHAR(20),CAST(CASE WHEN s.user_updates = 0 THEN 0 ELSE CONVERT(REAL, s.user_updates) * 100 /
  485. CASE (s.user_seeks + s.user_scans + s.user_lookups + s.user_updates) WHEN 0 THEN 1 ELSE CONVERT(REAL, (s.user_seeks + s.user_scans + s.user_lookups + s.user_updates)) END END AS DECIMAL(18,2))) COLLATE database_default) AS [Writes_Ratio],
  486. s.user_updates,
  487. MAX(s.last_user_seek) AS last_user_seek,
  488. MAX(s.last_user_scan) AS last_user_scan,
  489. MAX(s.last_user_lookup) AS last_user_lookup,
  490. MAX(s.last_user_update) AS last_user_update
  491. FROM sys.dm_db_index_usage_stats AS s WITH (NOLOCK)
  492. INNER JOIN sys.objects AS o WITH (NOLOCK) ON s.[object_id] = o.[object_id]
  493. INNER JOIN sys.tables AS mst WITH (NOLOCK) ON mst.[object_id] = s.[object_id]
  494. INNER JOIN sys.schemas AS t WITH (NOLOCK) ON t.[schema_id] = mst.[schema_id]
  495. WHERE o.[type] = ''U''
  496. AND s.database_id = ' + CONVERT(NVARCHAR(20), @dbid) + '
  497. --AND s.index_id > 0 -- ignore heaps
  498. GROUP BY s.database_id, t.name, s.[object_id], s.index_id, s.user_seeks, s.user_scans, s.user_lookups, s.user_updates
  499. OPTION (MAXDOP 2)'
  500. INSERT INTO #tmpIUS
  501. EXECUTE sp_executesql @sqlcmd
  502. SET @sqlcmd = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  503. USE [' + @dbname + '];
  504. SELECT ' + CONVERT(NVARCHAR(20), @dbid) + ' AS [database_id], t.name, i.[object_id], i.index_id, 0, 0, 0, NULL, NULL, NULL, NULL, NULL
  505. FROM sys.indexes i WITH (NOLOCK)
  506. INNER JOIN sys.objects o WITH (NOLOCK) ON i.object_id = o.object_id
  507. INNER JOIN sys.tables AS mst WITH (NOLOCK) ON mst.[object_id] = i.[object_id]
  508. INNER JOIN sys.schemas AS t WITH (NOLOCK) ON t.[schema_id] = mst.[schema_id]
  509. WHERE o.[type] = ''U''
  510. AND i.index_id NOT IN (SELECT s.index_id
  511. FROM sys.dm_db_index_usage_stats s WITH (NOLOCK)
  512. WHERE s.object_id = i.object_id
  513. AND i.index_id = s.index_id
  514. AND database_id = ' + CONVERT(NVARCHAR(20), @dbid) + ')
  515. AND i.name IS NOT NULL
  516. AND i.index_id > 1'
  517. INSERT INTO #tmpIUS
  518. EXECUTE sp_executesql @sqlcmd
  519. UPDATE #tblDatabases
  520. SET is_done = 1
  521. WHERE database_id = @dbid;
  522. END;
  523. IF @sqlmajorver >= 12
  524. BEGIN
  525. RAISERROR (N'Gathering sys.dm_db_xtp_hash_index_stats and sys.dm_db_xtp_nonclustered_index_stats data...', 10, 1) WITH NOWAIT
  526. UPDATE #tblDatabases
  527. SET is_done = 0;
  528. WHILE (SELECT COUNT(*) FROM #tblDatabases WHERE is_done = 0) > 0
  529. BEGIN
  530. SELECT TOP 1 @dbid = database_id FROM #tblDatabases WHERE is_done = 0
  531. SELECT @dbname = DB_NAME(@dbid)
  532. SET @sqlcmd = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  533. USE [' + @dbname + '];
  534. SELECT ' + CONVERT(NVARCHAR(20), @dbid) + ' AS [database_id], xis.[object_id], t.name, o.name, xis.index_id, si.name,
  535. xhis.total_bucket_count, xhis.empty_bucket_count, xhis.avg_chain_length, xhis.max_chain_length,
  536. xis.scans_started, xis.scans_retries, xis.rows_returned, xis.rows_touched
  537. FROM sys.dm_db_xtp_hash_index_stats xhis
  538. INNER JOIN sys.dm_db_xtp_index_stats xis ON xis.[object_id] = xhis.[object_id] AND xis.[index_id] = xhis.[index_id]
  539. INNER JOIN sys.indexes AS si WITH (NOLOCK) ON xis.[object_id] = si.[object_id] AND xis.[index_id] = si.[index_id]
  540. INNER JOIN sys.objects AS o WITH (NOLOCK) ON si.[object_id] = o.[object_id]
  541. INNER JOIN sys.tables AS mst WITH (NOLOCK) ON mst.[object_id] = o.[object_id]
  542. INNER JOIN sys.schemas AS t WITH (NOLOCK) ON t.[schema_id] = mst.[schema_id]
  543. WHERE o.[type] = ''U'''
  544. INSERT INTO #tmpXIS
  545. EXECUTE sp_executesql @sqlcmd
  546. SET @sqlcmd = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  547. USE [' + @dbname + '];
  548. SELECT ' + CONVERT(NVARCHAR(20), @dbid) + ' AS [database_id],
  549. xis.[object_id], t.name, o.name, xis.index_id, si.name,
  550. xnis.delta_pages, xnis.internal_pages, xnis.leaf_pages, xnis.page_update_count,
  551. xnis.page_update_retry_count, xnis.page_consolidation_count,
  552. xnis.page_consolidation_retry_count, xnis.page_split_count, xnis.page_split_retry_count,
  553. xnis.key_split_count, xnis.key_split_retry_count, xnis.page_merge_count, xnis.page_merge_retry_count,
  554. xnis.key_merge_count, xnis.key_merge_retry_count,
  555. xis.scans_started, xis.scans_retries, xis.rows_returned, xis.rows_touched
  556. FROM sys.dm_db_xtp_nonclustered_index_stats AS xnis WITH (NOLOCK)
  557. INNER JOIN sys.dm_db_xtp_index_stats AS xis WITH (NOLOCK) ON xis.[object_id] = xnis.[object_id] AND xis.[index_id] = xnis.[index_id]
  558. INNER JOIN sys.indexes AS si WITH (NOLOCK) ON xis.[object_id] = si.[object_id] AND xis.[index_id] = si.[index_id]
  559. INNER JOIN sys.objects AS o WITH (NOLOCK) ON si.[object_id] = o.[object_id]
  560. INNER JOIN sys.tables AS mst WITH (NOLOCK) ON mst.[object_id] = o.[object_id]
  561. INNER JOIN sys.schemas AS t WITH (NOLOCK) ON t.[schema_id] = mst.[schema_id]
  562. WHERE o.[type] = ''U'''
  563. INSERT INTO #tmpXNCIS
  564. EXECUTE sp_executesql @sqlcmd
  565. UPDATE #tblDatabases
  566. SET is_done = 1
  567. WHERE database_id = @dbid;
  568. END
  569. END;
  570. RAISERROR (N'Gathering index column data...', 10, 1) WITH NOWAIT
  571. UPDATE #tblDatabases
  572. SET is_done = 0;
  573. WHILE (SELECT COUNT(*) FROM #tblDatabases WHERE is_done = 0) > 0
  574. BEGIN
  575. SELECT TOP 1 @dbid = database_id FROM #tblDatabases WHERE is_done = 0
  576. SELECT @dbname = DB_NAME(@dbid)
  577. SET @sqlcmd = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  578. USE [' + @dbname + '];
  579. SELECT ' + CONVERT(NVARCHAR(20), @dbid) + ' AS [database_id], ''' + DB_NAME(@dbid) + ''' AS database_name,
  580. mst.[object_id], t.name, mst.[name],
  581. mi.index_id, mi.[name], p.partition_number,
  582. mi.[type], mi.[type_desc], mi.is_primary_key, mi.is_unique_constraint, mi.is_disabled,
  583. mi.fill_factor, mi.is_unique, mi.is_padded, ' + CASE WHEN @sqlmajorver > 9 THEN 'mi.has_filter, mi.filter_definition,' ELSE 'NULL, NULL,' END + '
  584. SUBSTRING((SELECT '','' + ac.name FROM sys.tables AS st
  585. INNER JOIN sys.indexes AS i ON st.[object_id] = i.[object_id]
  586. INNER JOIN sys.index_columns AS ic ON i.[object_id] = ic.[object_id] AND i.[index_id] = ic.[index_id]
  587. INNER JOIN sys.all_columns AS ac ON st.[object_id] = ac.[object_id] AND ic.[column_id] = ac.[column_id]
  588. WHERE mi.[object_id] = i.[object_id] AND mi.index_id = i.index_id AND ic.is_included_column = 0
  589. ORDER BY ic.key_ordinal
  590. FOR XML PATH('''')), 2, 8000) AS KeyCols,
  591. SUBSTRING((SELECT '','' + ac.name FROM sys.tables AS st
  592. INNER JOIN sys.indexes AS i ON st.[object_id] = i.[object_id]
  593. INNER JOIN sys.index_columns AS ic ON i.[object_id] = ic.[object_id] AND i.[index_id] = ic.[index_id]
  594. INNER JOIN sys.all_columns AS ac ON st.[object_id] = ac.[object_id] AND ic.[column_id] = ac.[column_id]
  595. WHERE mi.[object_id] = i.[object_id] AND mi.index_id = i.index_id AND ic.is_included_column = 0
  596. ORDER BY ac.name
  597. FOR XML PATH('''')), 2, 8000) AS KeyColsOrdered,
  598. SUBSTRING((SELECT '','' + ac.name FROM sys.tables AS st
  599. INNER JOIN sys.indexes AS i ON st.[object_id] = i.[object_id]
  600. INNER JOIN sys.index_columns AS ic ON i.[object_id] = ic.[object_id] AND i.[index_id] = ic.[index_id]
  601. INNER JOIN sys.all_columns AS ac ON st.[object_id] = ac.[object_id] AND ic.[column_id] = ac.[column_id]
  602. WHERE mi.[object_id] = i.[object_id] AND mi.index_id = i.index_id AND ic.is_included_column = 1
  603. ORDER BY ic.key_ordinal
  604. FOR XML PATH('''')), 2, 8000) AS IncludedCols,
  605. SUBSTRING((SELECT '','' + ac.name FROM sys.tables AS st
  606. INNER JOIN sys.indexes AS i ON st.[object_id] = i.[object_id]
  607. INNER JOIN sys.index_columns AS ic ON i.[object_id] = ic.[object_id] AND i.[index_id] = ic.[index_id]
  608. INNER JOIN sys.all_columns AS ac ON st.[object_id] = ac.[object_id] AND ic.[column_id] = ac.[column_id]
  609. WHERE mi.[object_id] = i.[object_id] AND mi.index_id = i.index_id AND ic.is_included_column = 1
  610. ORDER BY ac.name
  611. FOR XML PATH('''')), 2, 8000) AS IncludedColsOrdered,
  612. SUBSTRING((SELECT '','' + ac.name FROM sys.tables AS st
  613. INNER JOIN sys.indexes AS i ON st.[object_id] = i.[object_id]
  614. INNER JOIN sys.index_columns AS ic ON i.[object_id] = ic.[object_id] AND i.[index_id] = ic.[index_id]
  615. INNER JOIN sys.all_columns AS ac ON st.[object_id] = ac.[object_id] AND ic.[column_id] = ac.[column_id]
  616. WHERE mi.[object_id] = i.[object_id] AND mi.index_id = i.index_id
  617. ORDER BY ac.name
  618. FOR XML PATH('''')), 2, 8000) AS AllColsOrdered,
  619. (SELECT SUM(CASE sty.name WHEN ''nvarchar'' THEN sc.max_length/2 ELSE sc.max_length END) FROM sys.indexes AS i
  620. INNER JOIN sys.tables AS t ON t.[object_id] = i.[object_id]
  621. INNER JOIN sys.schemas ss ON ss.[schema_id] = t.[schema_id]
  622. INNER JOIN sys.index_columns AS sic ON sic.object_id = mst.object_id AND sic.index_id = mi.index_id
  623. INNER JOIN sys.columns AS sc ON sc.object_id = t.object_id AND sc.column_id = sic.column_id
  624. INNER JOIN sys.types AS sty ON sc.user_type_id = sty.user_type_id
  625. WHERE mi.[object_id] = i.[object_id] AND mi.index_id = i.index_id) AS [KeyCols_data_length_bytes]
  626. FROM sys.indexes AS mi
  627. INNER JOIN sys.tables AS mst ON mst.[object_id] = mi.[object_id]
  628. INNER JOIN sys.schemas AS t ON t.[schema_id] = mst.[schema_id]
  629. INNER JOIN sys.partitions AS p ON p.[object_id] = mi.[object_id] AND p.index_id = mi.index_id
  630. WHERE mi.type IN (0,1,2,5,6) AND mst.is_ms_shipped = 0
  631. ORDER BY mst.name
  632. OPTION (MAXDOP 2);'
  633. INSERT INTO #tmpIxs
  634. EXECUTE sp_executesql @sqlcmd;
  635. IF @sqlmajorver >= 12
  636. BEGIN
  637. SET @sqlcmd = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  638. USE [' + DB_NAME(@dbid) + '];
  639. SELECT ' + CONVERT(NVARCHAR(20), @dbid) + ' AS [database_id], ''' + DB_NAME(@dbid) + ''' AS database_name,
  640. mst.[object_id], t.name, mst.[name],
  641. mi.index_id, mi.[name], p.partition_number,
  642. mi.[type], mi.[type_desc], mi.is_primary_key, mi.is_unique_constraint, mi.is_disabled,
  643. mi.fill_factor, mi.is_unique, mi.is_padded, mi.has_filter, mi.filter_definition,[bucket_count],
  644. SUBSTRING((SELECT '','' + ac.name FROM sys.tables AS st
  645. INNER JOIN sys.hash_indexes AS i ON st.[object_id] = i.[object_id]
  646. INNER JOIN sys.index_columns AS ic ON i.[object_id] = ic.[object_id] AND i.[index_id] = ic.[index_id]
  647. INNER JOIN sys.all_columns AS ac ON st.[object_id] = ac.[object_id] AND ic.[column_id] = ac.[column_id]
  648. WHERE mi.[object_id] = i.[object_id] AND mi.index_id = i.index_id AND ic.is_included_column = 0
  649. ORDER BY ic.key_ordinal
  650. FOR XML PATH('''')), 2, 8000) AS KeyCols,
  651. SUBSTRING((SELECT '','' + ac.name FROM sys.tables AS st
  652. INNER JOIN sys.hash_indexes AS i ON st.[object_id] = i.[object_id]
  653. INNER JOIN sys.index_columns AS ic ON i.[object_id] = ic.[object_id] AND i.[index_id] = ic.[index_id]
  654. INNER JOIN sys.all_columns AS ac ON st.[object_id] = ac.[object_id] AND ic.[column_id] = ac.[column_id]
  655. WHERE mi.[object_id] = i.[object_id] AND mi.index_id = i.index_id AND ic.is_included_column = 0
  656. ORDER BY ac.name
  657. FOR XML PATH('''')), 2, 8000) AS KeyColsOrdered,
  658. SUBSTRING((SELECT '','' + ac.name FROM sys.tables AS st
  659. INNER JOIN sys.hash_indexes AS i ON st.[object_id] = i.[object_id]
  660. INNER JOIN sys.index_columns AS ic ON i.[object_id] = ic.[object_id] AND i.[index_id] = ic.[index_id]
  661. INNER JOIN sys.all_columns AS ac ON st.[object_id] = ac.[object_id] AND ic.[column_id] = ac.[column_id]
  662. WHERE mi.[object_id] = i.[object_id] AND mi.index_id = i.index_id AND ic.is_included_column = 1
  663. ORDER BY ic.key_ordinal
  664. FOR XML PATH('''')), 2, 8000) AS IncludedCols,
  665. SUBSTRING((SELECT '','' + ac.name FROM sys.tables AS st
  666. INNER JOIN sys.hash_indexes AS i ON st.[object_id] = i.[object_id]
  667. INNER JOIN sys.index_columns AS ic ON i.[object_id] = ic.[object_id] AND i.[index_id] = ic.[index_id]
  668. INNER JOIN sys.all_columns AS ac ON st.[object_id] = ac.[object_id] AND ic.[column_id] = ac.[column_id]
  669. WHERE mi.[object_id] = i.[object_id] AND mi.index_id = i.index_id AND ic.is_included_column = 1
  670. ORDER BY ac.name
  671. FOR XML PATH('''')), 2, 8000) AS IncludedColsOrdered,
  672. SUBSTRING((SELECT '','' + ac.name FROM sys.tables AS st
  673. INNER JOIN sys.hash_indexes AS i ON st.[object_id] = i.[object_id]
  674. INNER JOIN sys.index_columns AS ic ON i.[object_id] = ic.[object_id] AND i.[index_id] = ic.[index_id]
  675. INNER JOIN sys.all_columns AS ac ON st.[object_id] = ac.[object_id] AND ic.[column_id] = ac.[column_id]
  676. WHERE mi.[object_id] = i.[object_id] AND mi.index_id = i.index_id
  677. ORDER BY ac.name
  678. FOR XML PATH('''')), 2, 8000) AS AllColsOrdered,
  679. (SELECT SUM(CASE sty.name WHEN ''nvarchar'' THEN sc.max_length/2 ELSE sc.max_length END) FROM sys.hash_indexes AS i
  680. INNER JOIN sys.tables AS t ON t.[object_id] = i.[object_id]
  681. INNER JOIN sys.schemas ss ON ss.[schema_id] = t.[schema_id]
  682. INNER JOIN sys.index_columns AS sic ON sic.object_id = mst.object_id AND sic.index_id = mi.index_id
  683. INNER JOIN sys.columns AS sc ON sc.object_id = t.object_id AND sc.column_id = sic.column_id
  684. INNER JOIN sys.types AS sty ON sc.user_type_id = sty.user_type_id
  685. WHERE mi.[object_id] = i.[object_id] AND mi.index_id = i.index_id) AS [KeyCols_data_length_bytes]
  686. FROM sys.hash_indexes AS mi
  687. INNER JOIN sys.tables AS mst ON mst.[object_id] = mi.[object_id]
  688. INNER JOIN sys.schemas AS t ON t.[schema_id] = mst.[schema_id]
  689. INNER JOIN sys.partitions AS p ON p.[object_id] = mi.[object_id] AND p.index_id = mi.index_id
  690. WHERE mi.type IN (7) AND mst.is_ms_shipped = 0
  691. ORDER BY mst.name
  692. OPTION (MAXDOP 2);'
  693. INSERT INTO #tmpHashIxs
  694. EXECUTE sp_executesql @sqlcmd;
  695. END;
  696. UPDATE #tblDatabases
  697. SET is_done = 1
  698. WHERE database_id = @dbid;
  699. END;
  700. RAISERROR (N'Aggregating data...', 10, 1) WITH NOWAIT
  701. INSERT INTO #tmpAgg
  702. SELECT ISNULL(ps.database_id, si.[database_id]), si.database_name, ISNULL(ps.[object_id], si.[object_id]),
  703. si.[schema_name], si.table_name, si.index_id, si.index_name, ISNULL(ps.partition_number, si.partition_number),
  704. ps.fragmentation, si.fill_factor, ps.page_count, ps.[size_MB], ps.record_count, ps.forwarded_record_count, -- for heaps
  705. os.range_scan_count, os.singleton_lookup_count, os.forwarded_fetch_count, os.row_lock_count,
  706. os.row_lock_wait_count, os.row_lock_pct, os.row_lock_wait_in_ms, os.[avg_row_lock_waits_in_ms],
  707. os.page_lock_count, os.page_lock_wait_count, os.page_lock_pct, os.page_lock_wait_in_ms,
  708. os.[avg_page_lock_waits_in_ms], os.[page_io_latch_wait_in_ms], os.[avg_page_io_latch_wait_in_ms],
  709. s.[Hits], s.[Reads_Ratio], s.[Writes_Ratio], s.user_updates, s.last_user_seek, s.last_user_scan,
  710. s.last_user_lookup, s.last_user_update, si.KeyCols, si.KeyColsOrdered, si.IncludedCols,
  711. si.IncludedColsOrdered, si.AllColsOrdered, si.is_unique, si.[index_type], si.[type_desc],
  712. si.is_primary_key, si.is_unique_constraint, si.is_padded, si.has_filter, si.filter_definition,
  713. si.is_disabled, si.[KeyCols_data_length_bytes]
  714. FROM #tmpIxs AS si
  715. LEFT JOIN #tmpIPS AS ps ON si.database_id = ps.database_id AND si.index_id = ps.index_id AND si.[object_id] = ps.[object_id] AND si.partition_number = ps.partition_number
  716. LEFT JOIN #tmpIOS AS os ON os.database_id = ps.database_id AND os.index_id = ps.index_id AND os.[object_id] = ps.[object_id] AND os.partition_number = ps.partition_number
  717. LEFT JOIN #tmpIUS AS s ON s.database_id = ps.database_id AND s.index_id = ps.index_id and s.[object_id] = ps.[object_id]
  718. --WHERE si.type > 0 -- ignore heaps
  719. ORDER BY database_name, [table_name], fragmentation DESC, index_id
  720. OPTION (MAXDOP 2);
  721. IF @sqlmajorver >= 12
  722. BEGIN
  723. INSERT INTO #tmpAggXTPHash
  724. SELECT ISNULL(ps.database_id, si.[database_id]), si.database_name, ISNULL(ps.[object_id], si.[object_id]),
  725. si.[schema_name], si.table_name, si.index_id, si.index_name, ps.total_bucket_count, ps.empty_bucket_count,
  726. ps.avg_chain_length, ps.max_chain_length, ps.scans_started, ps.scans_retries, ps.rows_returned,
  727. ps.rows_touched, si.KeyCols, si.KeyColsOrdered, si.IncludedCols, si.IncludedColsOrdered,
  728. si.AllColsOrdered, si.is_unique, si.[index_type], si.[type_desc], si.is_primary_key, si.is_unique_constraint,
  729. si.is_padded, si.has_filter, si.filter_definition, si.is_disabled, si.[KeyCols_data_length_bytes]
  730. FROM #tmpHashIxs AS si
  731. LEFT JOIN #tmpXIS AS ps ON si.database_id = ps.database_id AND si.index_id = ps.index_id AND si.[object_id] = ps.[object_id]
  732. ORDER BY database_name, [table_name], index_id
  733. OPTION (MAXDOP 2);
  734. INSERT INTO #tmpAggXTPNC
  735. SELECT ISNULL(ps.database_id, si.[database_id]), si.database_name, ISNULL(ps.[object_id], si.[object_id]),
  736. si.[schema_name], si.table_name, si.index_id, si.index_name, ps.delta_pages, ps.internal_pages,
  737. ps.leaf_pages, ps.page_update_count, ps.page_update_retry_count,
  738. ps.page_consolidation_count, ps.page_consolidation_retry_count, ps.page_split_count,
  739. ps.page_split_retry_count, ps.key_split_count, ps.key_split_retry_count, ps.page_merge_count,
  740. ps.page_merge_retry_count, ps.key_merge_count, ps.key_merge_retry_count,
  741. ps.scans_started, ps.scans_retries, ps.rows_returned, ps.rows_touched,
  742. si.KeyCols, si.KeyColsOrdered, si.IncludedCols, si.IncludedColsOrdered, si.AllColsOrdered,
  743. si.is_unique, si.[index_type], si.[type_desc], si.is_primary_key, si.is_unique_constraint,
  744. si.is_padded, si.has_filter, si.filter_definition, si.is_disabled, si.[KeyCols_data_length_bytes]
  745. FROM #tmpHashIxs AS si
  746. LEFT JOIN #tmpXNCIS AS ps ON si.database_id = ps.database_id AND si.index_id = ps.index_id AND si.[object_id] = ps.[object_id]
  747. ORDER BY database_name, [table_name], index_id
  748. OPTION (MAXDOP 2);
  749. END;
  750. RAISERROR (N'Output index information', 10, 1) WITH NOWAIT
  751. -- All index information
  752. SELECT 'All_IX_Info' AS [Category], [database_id], [database_name], [object_id], [schema_name], [table_name], [index_id], [index_name], [type_desc] AS index_type,
  753. [partition_number], fragmentation, fill_factor, [page_count], [size_MB], record_count, range_scan_count, singleton_lookup_count, row_lock_count, row_lock_wait_count,
  754. row_lock_pct, row_lock_wait_in_ms, [avg_row_lock_waits_in_ms], page_lock_count, page_lock_wait_count,
  755. page_lock_pct, page_lock_wait_in_ms, [avg_page_lock_waits_in_ms], page_io_latch_wait_in_ms, [avg_page_io_latch_wait_in_ms], [Hits],
  756. CONVERT(NVARCHAR,[Reads_Ratio]) COLLATE database_default + '/' + CONVERT(NVARCHAR,[Writes_Ratio]) COLLATE database_default AS [R/W_Ratio],
  757. user_updates, last_user_seek, last_user_scan, last_user_lookup, last_user_update, KeyCols, IncludedCols,
  758. is_unique, is_primary_key, is_unique_constraint, is_disabled, is_padded, has_filter, filter_definition, KeyCols_data_length_bytes
  759. FROM #tmpAgg
  760. WHERE index_id > 0 -- ignore heaps
  761. ORDER BY [database_name], [schema_name], table_name, [page_count] DESC, forwarded_record_count DESC;
  762. -- All XTP index information
  763. IF @sqlmajorver >= 12
  764. BEGIN
  765. SELECT 'All_XTP_HashIX_Info' AS [Category], [database_id], [database_name], [object_id], [schema_name], [table_name], [index_id], [index_name], [type_desc] AS index_type,
  766. total_bucket_count, empty_bucket_count, FLOOR((CAST(empty_bucket_count AS FLOAT)/total_bucket_count) * 100) AS [empty_bucket_pct], avg_chain_length, max_chain_length,
  767. scans_started, scans_retries, rows_returned, rows_touched, KeyCols, IncludedCols, is_unique, is_primary_key, is_unique_constraint, is_disabled, is_padded, has_filter,
  768. filter_definition, KeyCols_data_length_bytes
  769. FROM #tmpAggXTPHash
  770. ORDER BY [database_name], [schema_name], table_name, [total_bucket_count] DESC;
  771. SELECT 'All_XTP_RangeIX_Info' AS [Category], [database_id], [database_name], [object_id], [schema_name], [table_name], [index_id], [index_name], [type_desc] AS index_type,
  772. delta_pages, internal_pages, leaf_pages, page_update_count, page_update_retry_count, page_consolidation_count, page_consolidation_retry_count,
  773. page_split_count, page_split_retry_count, key_split_count, key_split_retry_count, page_merge_count, page_merge_retry_count, key_merge_count, key_merge_retry_count,
  774. scans_started, scans_retries, rows_returned, rows_touched, KeyCols, IncludedCols, is_unique, is_primary_key, is_unique_constraint, is_disabled, is_padded, has_filter,
  775. filter_definition, KeyCols_data_length_bytes
  776. FROM #tmpAggXTPNC
  777. ORDER BY [database_name], [schema_name], table_name, [leaf_pages] DESC;
  778. END;
  779. -- All Heaps information
  780. SELECT 'All_Heaps_Info' AS [Category], [database_id], [database_name], [object_id], [schema_name], [table_name], [index_id], [type_desc] AS index_type,
  781. [partition_number], fragmentation, [page_count], [size_MB], record_count, forwarded_record_count, forwarded_fetch_count,
  782. range_scan_count, singleton_lookup_count, row_lock_count, row_lock_wait_count,
  783. row_lock_pct, row_lock_wait_in_ms, [avg_row_lock_waits_in_ms], page_lock_count, page_lock_wait_count,
  784. page_lock_pct, page_lock_wait_in_ms, [avg_page_lock_waits_in_ms], page_io_latch_wait_in_ms, [avg_page_io_latch_wait_in_ms]
  785. FROM #tmpAgg
  786. WHERE index_id = 0 -- only heaps
  787. ORDER BY [database_name], [schema_name], table_name, [page_count] DESC, forwarded_record_count DESC;
  788. -- Unused indexes that can possibly be dropped or disabled
  789. SELECT 'Unused_IX_With_Updates' AS [Category], [database_id], [database_name], [object_id], [schema_name], [table_name], [index_id], [index_name], [type_desc] AS index_type, [Hits],
  790. CONVERT(NVARCHAR,[Reads_Ratio]) COLLATE database_default + '/' + CONVERT(NVARCHAR,[Writes_Ratio]) COLLATE database_default AS [R/W_Ratio],
  791. [page_count], [size_MB], record_count, user_updates, last_user_seek, last_user_scan,
  792. last_user_lookup, last_user_update, is_unique, is_padded, has_filter, filter_definition
  793. FROM #tmpAgg
  794. WHERE [Hits] = 0
  795. AND last_user_update > 0
  796. AND [type] IN (2,6) -- non-clustered and non-clustered columnstore indexes only
  797. AND is_primary_key = 0 -- no primary keys
  798. AND is_unique_constraint = 0 -- no unique constraints
  799. AND is_unique = 0 -- no alternate keys
  800. UNION ALL
  801. SELECT 'Unused_IX_No_Updates' AS [Category], [database_id], [database_name], [object_id], [schema_name], [table_name], [index_id], [index_name], [type_desc] AS index_type, [Hits],
  802. CONVERT(NVARCHAR,[Reads_Ratio]) COLLATE database_default + '/' + CONVERT(NVARCHAR,[Writes_Ratio]) COLLATE database_default AS [R/W_Ratio],
  803. [page_count], [size_MB], record_count, user_updates, last_user_seek, last_user_scan,
  804. last_user_lookup, last_user_update, is_unique, is_padded, has_filter, filter_definition
  805. FROM #tmpAgg
  806. WHERE [Hits] = 0
  807. AND (last_user_update = 0 OR last_user_update IS NULL)
  808. AND [type] IN (2,6) -- non-clustered and non-clustered columnstore indexes only
  809. AND is_primary_key = 0 -- no primary keys
  810. AND is_unique_constraint = 0 -- no unique constraints
  811. AND is_unique = 0 -- no alternate keys
  812. ORDER BY [table_name], user_updates DESC, [page_count] DESC;
  813. -- Rarely used indexes that can possibly be dropped or disabled
  814. SELECT 'Rarely_Used_IX' AS [Category], [database_id], [database_name], [object_id], [schema_name], [table_name], [index_id], [index_name], [type_desc] AS index_type, [Hits],
  815. CONVERT(NVARCHAR,[Reads_Ratio]) COLLATE database_default + '/' + CONVERT(NVARCHAR,[Writes_Ratio]) COLLATE database_default AS [R/W_Ratio],
  816. [page_count], [size_MB], record_count, user_updates, last_user_seek, last_user_scan,
  817. last_user_lookup, last_user_update, is_unique, is_padded, has_filter, filter_definition
  818. FROM #tmpAgg
  819. WHERE [Hits] > 0 AND [Reads_Ratio] < 5
  820. AND [type] IN (2,6) -- non-clustered and non-clustered columnstore indexes only
  821. AND is_primary_key = 0 -- no primary keys
  822. AND is_unique_constraint = 0 -- no unique constraints
  823. AND is_unique = 0 -- no alternate keys
  824. ORDER BY [database_name], [table_name], [page_count] DESC;
  825. -- Duplicate Indexes
  826. SELECT 'Duplicate_IX' AS [Category], I.[database_id], I.[database_name], I.[object_id], I.[schema_name], I.[table_name], I.[index_id], I.[index_name], I.[type_desc] AS index_type, I.is_primary_key, I.is_unique_constraint, I.is_unique, I.is_padded, I.has_filter, I.filter_definition,
  827. I.[Hits], I.[KeyCols], I.IncludedCols, CASE WHEN I.IncludedColsOrdered IS NULL THEN I.[KeyColsOrdered] ELSE I.[KeyColsOrdered] + ',' + I.IncludedColsOrdered END AS [AllColsOrdered]
  828. FROM #tmpAgg I INNER JOIN #tmpAgg I2
  829. ON I.database_id = I2.database_id AND I.[object_id] = I2.[object_id] AND I.[index_id] <> I2.[index_id]
  830. AND I.[KeyCols] = I2.[KeyCols] AND (I.IncludedCols = I2.IncludedCols OR (I.IncludedCols IS NULL AND I2.IncludedCols IS NULL))
  831. AND ((I.filter_definition = I2.filter_definition) OR (I.filter_definition IS NULL AND I2.filter_definition IS NULL))
  832. WHERE I.[type] IN (1,2,5,6) -- clustered, non-clustered, clustered and non-clustered columnstore indexes only
  833. AND I2.[type] IN (1,2,5,6) -- clustered, non-clustered, clustered and non-clustered columnstore indexes only
  834. GROUP BY I.[database_id], I.[database_name], I.[object_id], I.[schema_name], I.[table_name], I.[schema_name], I.[index_id], I.[index_name], I.[Hits], I.KeyCols, I.IncludedCols, I.[KeyColsOrdered], I.IncludedColsOrdered, I.type_desc, I.[AllColsOrdered], I.is_primary_key, I.is_unique_constraint, I.is_unique, I.is_padded, I.has_filter, I.filter_definition
  835. ORDER BY I.database_name, I.[table_name], I.[index_id];
  836. /*
  837. Note that it is possible that a clustered index (unique or not) is among the duplicate indexes to be dropped,
  838. namely if a non-clustered primary key exists on the table.
  839. In this case, make the appropriate changes in the clustered index (making it unique and/or primary key in this case),
  840. and drop the non-clustered instead.
  841. */
  842. SELECT 'Duplicate_IX_toDrop' AS [Category], I.[database_id], I.[database_name], I.[object_id], I.[schema_name], I.[table_name], I.[index_id], I.[index_name], I.[type_desc] AS index_type, I.is_primary_key, I.is_unique_constraint, I.is_unique, I.is_padded, I.has_filter, I.filter_definition,
  843. I.[Hits], I.[KeyCols], I.IncludedCols, CASE WHEN I.IncludedColsOrdered IS NULL THEN I.[KeyColsOrdered] ELSE I.[KeyColsOrdered] + ',' + I.IncludedColsOrdered END AS [AllColsOrdered]
  844. FROM #tmpAgg I INNER JOIN #tmpAgg I2
  845. ON I.database_id = I2.database_id AND I.[object_id] = I2.[object_id] AND I.[index_id] <> I2.[index_id]
  846. AND I.[KeyCols] = I2.[KeyCols] AND (I.IncludedCols = I2.IncludedCols OR (I.IncludedCols IS NULL AND I2.IncludedCols IS NULL))
  847. AND ((I.filter_definition = I2.filter_definition) OR (I.filter_definition IS NULL AND I2.filter_definition IS NULL))
  848. WHERE I.[type] IN (1,2,5,6) -- clustered, non-clustered, clustered and non-clustered columnstore indexes only
  849. AND I2.[type] IN (1,2,5,6) -- clustered, non-clustered, clustered and non-clustered columnstore indexes only
  850. AND I.[index_id] NOT IN (
  851. SELECT COALESCE((SELECT MIN(tI3.[index_id]) FROM #tmpAgg tI3
  852. WHERE tI3.[database_id] = I.[database_id] AND tI3.[object_id] = I.[object_id]
  853. AND tI3.[KeyCols] = I.[KeyCols] AND (tI3.IncludedCols = I.IncludedCols OR (tI3.IncludedCols IS NULL AND I.IncludedCols IS NULL))
  854. AND (tI3.is_unique = 1 AND tI3.is_primary_key = 1)
  855. GROUP BY tI3.[object_id], tI3.KeyCols, tI3.IncludedCols, tI3.[KeyColsOrdered], tI3.IncludedColsOrdered),
  856. (SELECT MIN(tI3.[index_id]) FROM #tmpAgg tI3
  857. WHERE tI3.[database_id] = I.[database_id] AND tI3.[object_id] = I.[object_id]
  858. AND tI3.[KeyCols] = I.[KeyCols] AND (tI3.IncludedCols = I.IncludedCols OR (tI3.IncludedCols IS NULL AND I.IncludedCols IS NULL))
  859. AND (tI3.is_unique = 1 OR tI3.is_primary_key = 1)
  860. GROUP BY tI3.[object_id], tI3.KeyCols, tI3.IncludedCols, tI3.[KeyColsOrdered], tI3.IncludedColsOrdered),
  861. (SELECT MIN(tI3.[index_id]) FROM #tmpAgg tI3
  862. WHERE tI3.[database_id] = I.[database_id] AND tI3.[object_id] = I.[object_id]
  863. AND tI3.[KeyCols] = I.[KeyCols] AND (tI3.IncludedCols = I.IncludedCols OR (tI3.IncludedCols IS NULL AND I.IncludedCols IS NULL))
  864. GROUP BY tI3.[object_id], tI3.KeyCols, tI3.IncludedCols, tI3.[KeyColsOrdered], tI3.IncludedColsOrdered)
  865. ))
  866. GROUP BY I.[database_id], I.[database_name], I.[object_id], I.[schema_name], I.[table_name], I.[index_id], I.[index_name], I.[Hits], I.KeyCols, I.IncludedCols, I.[KeyColsOrdered], I.IncludedColsOrdered, I.type_desc, I.[AllColsOrdered], I.is_primary_key, I.is_unique_constraint, I.is_unique, I.is_padded, I.has_filter, I.filter_definition
  867. ORDER BY I.database_name, I.[table_name], I.[index_id];
  868. RAISERROR (N'Starting index search in sql modules...', 10, 1) WITH NOWAIT
  869. DECLARE Dup_Stats CURSOR FAST_FORWARD FOR SELECT I.database_name,I.[index_name]
  870. FROM #tmpAgg I INNER JOIN #tmpAgg I2
  871. ON I.database_id = I2.database_id AND I.[object_id] = I2.[object_id] AND I.[index_id] <> I2.[index_id]
  872. AND I.[KeyCols] = I2.[KeyCols] AND (I.IncludedCols = I2.IncludedCols OR (I.IncludedCols IS NULL AND I2.IncludedCols IS NULL))
  873. AND ((I.filter_definition = I2.filter_definition) OR (I.filter_definition IS NULL AND I2.filter_definition IS NULL))
  874. WHERE I.[type] IN (1,2,5,6) -- clustered, non-clustered, clustered and non-clustered columnstore indexes only
  875. AND I2.[type] IN (1,2,5,6) -- clustered, non-clustered, clustered and non-clustered columnstore indexes only
  876. AND I.[index_id] NOT IN (
  877. SELECT COALESCE((SELECT MIN(tI3.[index_id]) FROM #tmpAgg tI3
  878. WHERE tI3.[database_id] = I.[database_id] AND tI3.[object_id] = I.[object_id]
  879. AND tI3.[KeyCols] = I.[KeyCols] AND (tI3.IncludedCols = I.IncludedCols OR (tI3.IncludedCols IS NULL AND I.IncludedCols IS NULL))
  880. AND (tI3.is_unique = 1 AND tI3.is_primary_key = 1)
  881. GROUP BY tI3.[object_id], tI3.KeyCols, tI3.IncludedCols, tI3.[KeyColsOrdered], tI3.IncludedColsOrdered),
  882. (SELECT MIN(tI3.[index_id]) FROM #tmpAgg tI3
  883. WHERE tI3.[database_id] = I.[database_id] AND tI3.[object_id] = I.[object_id]
  884. AND tI3.[KeyCols] = I.[KeyCols] AND (tI3.IncludedCols = I.IncludedCols OR (tI3.IncludedCols IS NULL AND I.IncludedCols IS NULL))
  885. AND (tI3.is_unique = 1 OR tI3.is_primary_key = 1)
  886. GROUP BY tI3.[object_id], tI3.KeyCols, tI3.IncludedCols, tI3.[KeyColsOrdered], tI3.IncludedColsOrdered),
  887. (SELECT MIN(tI3.[index_id]) FROM #tmpAgg tI3
  888. WHERE tI3.[database_id] = I.[database_id] AND tI3.[object_id] = I.[object_id]
  889. AND tI3.[KeyCols] = I.[KeyCols] AND (tI3.IncludedCols = I.IncludedCols OR (tI3.IncludedCols IS NULL AND I.IncludedCols IS NULL))
  890. GROUP BY tI3.[object_id], tI3.KeyCols, tI3.IncludedCols, tI3.[KeyColsOrdered], tI3.IncludedColsOrdered)
  891. ))
  892. GROUP BY I.[database_id], I.[database_name], I.[object_id], I.[schema_name], I.[table_name], I.[index_id], I.[index_name], I.[Hits], I.KeyCols, I.IncludedCols, I.[KeyColsOrdered], I.IncludedColsOrdered, I.type_desc, I.[AllColsOrdered], I.is_primary_key, I.is_unique_constraint, I.is_unique, I.is_padded, I.has_filter, I.filter_definition
  893. ORDER BY I.database_name, I.[table_name], I.[index_id];
  894. OPEN Dup_Stats
  895. FETCH NEXT FROM Dup_Stats INTO @DatabaseName,@indexName
  896. WHILE (@@FETCH_STATUS = 0)
  897. BEGIN
  898. SET @sqlcmd = 'USE [' + @DatabaseName + '];
  899. SELECT ''' + @DatabaseName + ''' AS [database_name], ss.name AS [schema_name], so.name AS [table_name], ''' + @indexName + ''' AS [index_name], so.type_desc
  900. FROM sys.sql_modules sm
  901. INNER JOIN sys.objects so ON sm.[object_id] = so.[object_id]
  902. INNER JOIN sys.schemas ss ON ss.[schema_id] = so.[schema_id]
  903. WHERE sm.[definition] LIKE ''%' + @indexName + '%'''
  904. INSERT INTO #tblCode
  905. EXECUTE sp_executesql @sqlcmd
  906. FETCH NEXT FROM Dup_Stats INTO @DatabaseName,@indexName
  907. END
  908. CLOSE Dup_Stats
  909. DEALLOCATE Dup_Stats
  910. RAISERROR (N'Ended index search in sql modules', 10, 1) WITH NOWAIT
  911. SELECT 'Duplicate_Indexes_HardCoded' AS [Category], [DatabaseName], [schemaName], [objectName] AS [referedIn_objectName],
  912. indexName AS [referenced_indexName], type_desc AS [refered_objectType]
  913. FROM #tblCode
  914. ORDER BY [DatabaseName], [objectName];
  915. -- Redundant Indexes
  916. SELECT 'Redundant_IX' AS [Category], I.[database_id], I.[database_name], I.[object_id], I.[schema_name], I.[table_name], I.[index_id], I.[index_name], I.[type_desc] AS index_type, I.is_unique, I.is_padded, I.has_filter, I.filter_definition,
  917. I.[Hits], I.[KeyCols], I.IncludedCols, CASE WHEN I.IncludedColsOrdered IS NULL THEN I.[KeyColsOrdered] ELSE I.[KeyColsOrdered] + ',' + I.IncludedColsOrdered END AS [AllColsOrdered]
  918. FROM #tmpAgg I INNER JOIN #tmpAgg I2
  919. ON I.[database_id] = I2.[database_id] AND I.[object_id] = I2.[object_id] AND I.[index_id] <> I2.[index_id]
  920. AND (((I.[KeyColsOrdered] <> I2.[KeyColsOrdered] OR I.IncludedColsOrdered <> I2.IncludedColsOrdered)
  921. AND ((CASE WHEN I.IncludedColsOrdered IS NULL THEN I.[KeyColsOrdered] ELSE I.[KeyColsOrdered] + ',' + I.IncludedColsOrdered END) = (CASE WHEN I2.IncludedColsOrdered IS NULL THEN I2.[KeyColsOrdered] ELSE I2.[KeyColsOrdered] + ',' + I2.IncludedColsOrdered END)
  922. OR I.[AllColsOrdered] = I2.[AllColsOrdered]))
  923. OR (I.[KeyColsOrdered] <> I2.[KeyColsOrdered] AND I.IncludedColsOrdered = I2.IncludedColsOrdered)
  924. OR (I.[KeyColsOrdered] = I2.[KeyColsOrdered] AND I.IncludedColsOrdered <> I2.IncludedColsOrdered)
  925. OR ((I.[AllColsOrdered] = I2.[AllColsOrdered] AND I.filter_definition IS NULL AND I2.filter_definition IS NOT NULL) OR (I.[AllColsOrdered] = I2.[AllColsOrdered] AND I.filter_definition IS NOT NULL AND I2.filter_definition IS NULL)))
  926. AND I.[index_id] NOT IN (SELECT I3.[index_id]
  927. FROM #tmpIxs I3 INNER JOIN #tmpIxs I4
  928. ON I3.[database_id] = I4.[database_id] AND I3.[object_id] = I4.[object_id] AND I3.[index_id] <> I4.[index_id]
  929. AND I3.[KeyCols] = I4.[KeyCols] AND (I3.IncludedCols = I4.IncludedCols OR (I3.IncludedCols IS NULL AND I4.IncludedCols IS NULL))
  930. WHERE I3.[database_id] = I.[database_id] AND I3.[object_id] = I.[object_id]
  931. GROUP BY I3.[index_id])
  932. WHERE I.[type] IN (1,2,5,6) -- clustered, non-clustered, clustered and non-clustered columnstore indexes only
  933. AND I2.[type] IN (1,2,5,6) -- clustered, non-clustered, clustered and non-clustered columnstore indexes only
  934. AND I.is_unique_constraint = 0 -- no unique constraints
  935. AND I2.is_unique_constraint = 0 -- no unique constraints
  936. GROUP BY I.[database_id], I.[database_name], I.[object_id], I.[schema_name], I.[table_name], I.[index_id], I.[index_name], I.[Hits], I.KeyCols, I.IncludedCols, I.[KeyColsOrdered], I.IncludedColsOrdered, I.type_desc, I.[AllColsOrdered], I.is_unique, I.is_padded, I.has_filter, I.filter_definition
  937. ORDER BY I.database_name, I.[table_name], I.[AllColsOrdered], I.[index_id];
  938. -- Large IX Keys
  939. SELECT 'Large_Index_Key' AS [Category], I.[database_name], I.[schema_name], I.[table_name], I.[index_id], I.[index_name],
  940. I.KeyCols, [KeyCols_data_length_bytes]
  941. FROM #tmpAgg I
  942. WHERE [KeyCols_data_length_bytes] > 900
  943. ORDER BY I.[database_name], I.[schema_name], I.[table_name], I.[index_id];
  944. -- Low Fill Factor
  945. SELECT 'Low_Fill_Factor' AS [Category], I.[database_name], I.[schema_name], I.[table_name], I.[index_id], I.[index_name],
  946. [fill_factor], I.KeyCols, I.IncludedCols, CASE WHEN I.IncludedCols IS NULL THEN I.[KeyCols] ELSE I.[KeyCols] + ',' + I.IncludedCols END AS [AllColsOrdered]
  947. FROM #tmpAgg I
  948. WHERE [fill_factor] BETWEEN 1 AND 79
  949. ORDER BY I.[database_name], I.[schema_name], I.[table_name], I.[index_id];
  950. --NonUnique Clustered IXs
  951. SELECT 'NonUnique_CIXs' AS [Category], I.[database_name], I.[schema_name], I.[table_name], I.[index_id], I.[index_name], I.[KeyCols]
  952. FROM #tmpAgg I
  953. WHERE [is_unique] = 0
  954. AND I.[index_id] = 1
  955. ORDER BY I.[database_name], I.[schema_name], I.[table_name];
  956. RAISERROR (N'Generating scripts...', 10, 1) WITH NOWAIT
  957. DECLARE @strSQL NVARCHAR(4000)
  958. PRINT CHAR(10) + '/* Generated on ' + CONVERT (VARCHAR, GETDATE()) + ' in ' + @@SERVERNAME + ' */'
  959. IF (SELECT COUNT(*) FROM #tmpAgg WHERE [Hits] = 0 AND last_user_update > 0) > 0
  960. BEGIN
  961. PRINT CHAR(10) + '--############# Existing unused indexes with updates drop statements #############' + CHAR(10)
  962. DECLARE Un_Stats CURSOR FAST_FORWARD FOR SELECT 'USE ' + [database_name] + CHAR(10) + 'GO' + CHAR(10) + 'IF EXISTS (SELECT name FROM sys.indexes WHERE name = N'''+ [index_name] + ''')' + CHAR(10) + 'DROP INDEX ' + QUOTENAME([index_name]) + ' ON ' + QUOTENAME([schema_name]) + '.' + QUOTENAME([table_name]) + ';' + CHAR(10) + 'GO' + CHAR(10)
  963. FROM #tmpAgg
  964. WHERE [Hits] = 0 AND last_user_update > 0
  965. ORDER BY [database_name], [table_name], [Reads_Ratio] DESC;
  966. OPEN Un_Stats
  967. FETCH NEXT FROM Un_Stats INTO @strSQL
  968. WHILE (@@FETCH_STATUS = 0)
  969. BEGIN
  970. PRINT @strSQL
  971. FETCH NEXT FROM Un_Stats INTO @strSQL
  972. END
  973. CLOSE Un_Stats
  974. DEALLOCATE Un_Stats
  975. PRINT CHAR(10) + '--############# Ended unused indexes with updates drop statements #############' + CHAR(10)
  976. END;
  977. IF (SELECT COUNT(*) FROM #tmpAgg WHERE [Hits] = 0 AND (last_user_update = 0 OR last_user_update IS NULL)) > 0
  978. BEGIN
  979. PRINT CHAR(10) + '--############# Existing unused indexes with no updates drop statements #############' + CHAR(10)
  980. DECLARE Un_Stats CURSOR FAST_FORWARD FOR SELECT 'USE ' + [database_name] + CHAR(10) + 'GO' + CHAR(10) + 'IF EXISTS (SELECT name FROM sys.indexes WHERE name = N'''+ [index_name] + ''')' + CHAR(10) + 'DROP INDEX ' + QUOTENAME([index_name]) + ' ON ' + QUOTENAME([schema_name]) + '.' + QUOTENAME([table_name]) + ';' + CHAR(10) + 'GO' + CHAR(10)
  981. FROM #tmpAgg
  982. WHERE [Hits] = 0 AND (last_user_update = 0 OR last_user_update IS NULL)
  983. ORDER BY [database_name], [table_name], [Reads_Ratio] DESC;
  984. OPEN Un_Stats
  985. FETCH NEXT FROM Un_Stats INTO @strSQL
  986. WHILE (@@FETCH_STATUS = 0)
  987. BEGIN
  988. PRINT @strSQL
  989. FETCH NEXT FROM Un_Stats INTO @strSQL
  990. END
  991. CLOSE Un_Stats
  992. DEALLOCATE Un_Stats
  993. PRINT CHAR(10) + '--############# Ended unused indexes with no updates drop statements #############' + CHAR(10)
  994. END;
  995. IF (SELECT COUNT(*) FROM #tmpAgg WHERE [Hits] > 0 AND [Reads_Ratio] < 5) > 0
  996. BEGIN
  997. PRINT CHAR(10) + '/* Generated on ' + CONVERT (VARCHAR, GETDATE()) + ' in ' + @@SERVERNAME + ' */'
  998. PRINT CHAR(10) + '--############# Existing rarely used indexes drop statements #############' + CHAR(10)
  999. DECLARE curRarUsed CURSOR FAST_FORWARD FOR SELECT 'USE ' + [database_name] + CHAR(10) + 'GO' + CHAR(10) + 'IF EXISTS (SELECT name FROM sys.indexes WHERE name = N'''+ [index_name] + ''')' + CHAR(10) + 'DROP INDEX ' + QUOTENAME([index_name]) + ' ON ' + QUOTENAME([schema_name]) + '.' + QUOTENAME([table_name]) + ';' + CHAR(10) + 'GO' + CHAR(10)
  1000. FROM #tmpAgg
  1001. WHERE [Hits] > 0 AND [Reads_Ratio] < 5
  1002. ORDER BY [database_name], [table_name], [Reads_Ratio] DESC
  1003. OPEN curRarUsed
  1004. FETCH NEXT FROM curRarUsed INTO @strSQL
  1005. WHILE (@@FETCH_STATUS = 0)
  1006. BEGIN
  1007. PRINT @strSQL
  1008. FETCH NEXT FROM curRarUsed INTO @strSQL
  1009. END
  1010. CLOSE curRarUsed
  1011. DEALLOCATE curRarUsed
  1012. PRINT '--############# Ended rarely used indexes drop statements #############' + CHAR(10)
  1013. END;
  1014. PRINT CHAR(10) + '/* Generated on ' + CONVERT (VARCHAR, GETDATE()) + ' in ' + @@SERVERNAME + ' */'
  1015. PRINT CHAR(10) + '/*
  1016. NOTE: It is possible that a clustered index (unique or not) is among the duplicate indexes to be dropped, namely if a non-clustered primary key exists on the table.
  1017. In this case, make the appropriate changes in the clustered index (making it unique and/or primary key in this case), and drop the non-clustered instead.
  1018. */'
  1019. PRINT CHAR(10) + '--############# Existing Duplicate indexes drop statements #############' + CHAR(10)
  1020. DECLARE Dup_Stats CURSOR FAST_FORWARD FOR SELECT 'USE ' + I.[database_name] + CHAR(10) + 'GO' + CHAR(10) + 'IF EXISTS (SELECT name FROM sys.indexes WHERE name = N'''+ I.[index_name] + ''')' + CHAR(10) + 'DROP INDEX ' + QUOTENAME(I.[index_name]) + ' ON ' + QUOTENAME(I.[schema_name]) + '.' + QUOTENAME(I.[table_name]) + ';' + CHAR(10) + 'GO' + CHAR(10)
  1021. FROM #tmpAgg I INNER JOIN #tmpAgg I2
  1022. ON I.database_id = I2.database_id AND I.[object_id] = I2.[object_id] AND I.[index_id] <> I2.[index_id]
  1023. AND I.[KeyCols] = I2.[KeyCols] AND (I.IncludedCols = I2.IncludedCols OR (I.IncludedCols IS NULL AND I2.IncludedCols IS NULL))
  1024. AND ((I.filter_definition = I2.filter_definition) OR (I.filter_definition IS NULL AND I2.filter_definition IS NULL))
  1025. WHERE I.[type] IN (1,2,5,6) -- clustered, non-clustered, clustered and non-clustered columnstore indexes only
  1026. AND I2.[type] IN (1,2,5,6) -- clustered, non-clustered, clustered and non-clustered columnstore indexes only
  1027. AND I.[index_id] NOT IN (
  1028. SELECT COALESCE((SELECT MIN(tI3.[index_id]) FROM #tmpAgg tI3
  1029. WHERE tI3.[database_id] = I.[database_id] AND tI3.[object_id] = I.[object_id]
  1030. AND tI3.[KeyCols] = I.[KeyCols] AND (tI3.IncludedCols = I.IncludedCols OR (tI3.IncludedCols IS NULL AND I.IncludedCols IS NULL))
  1031. AND (tI3.is_unique = 1 AND tI3.is_primary_key = 1)
  1032. GROUP BY tI3.[object_id], tI3.KeyCols, tI3.IncludedCols, tI3.[KeyColsOrdered], tI3.IncludedColsOrdered),
  1033. (SELECT MIN(tI3.[index_id]) FROM #tmpAgg tI3
  1034. WHERE tI3.[database_id] = I.[database_id] AND tI3.[object_id] = I.[object_id]
  1035. AND tI3.[KeyCols] = I.[KeyCols] AND (tI3.IncludedCols = I.IncludedCols OR (tI3.IncludedCols IS NULL AND I.IncludedCols IS NULL))
  1036. AND (tI3.is_unique = 1 OR tI3.is_primary_key = 1)
  1037. GROUP BY tI3.[object_id], tI3.KeyCols, tI3.IncludedCols, tI3.[KeyColsOrdered], tI3.IncludedColsOrdered),
  1038. (SELECT MIN(tI3.[index_id]) FROM #tmpAgg tI3
  1039. WHERE tI3.[database_id] = I.[database_id] AND tI3.[object_id] = I.[object_id]
  1040. AND tI3.[KeyCols] = I.[KeyCols] AND (tI3.IncludedCols = I.IncludedCols OR (tI3.IncludedCols IS NULL AND I.IncludedCols IS NULL))
  1041. GROUP BY tI3.[object_id], tI3.KeyCols, tI3.IncludedCols, tI3.[KeyColsOrdered], tI3.IncludedColsOrdered)
  1042. ))
  1043. GROUP BY I.[database_id], I.[database_name], I.[object_id], I.[schema_name], I.[table_name], I.[index_id], I.[index_name], I.[Hits], I.KeyCols, I.IncludedCols, I.[KeyColsOrdered], I.IncludedColsOrdered, I.type_desc, I.[AllColsOrdered], I.is_primary_key, I.is_unique_constraint, I.is_unique, I.is_padded, I.has_filter, I.filter_definition
  1044. ORDER BY I.database_name, I.[table_name], I.[index_id];
  1045. OPEN Dup_Stats
  1046. FETCH NEXT FROM Dup_Stats INTO @strSQL
  1047. WHILE (@@FETCH_STATUS = 0)
  1048. BEGIN
  1049. PRINT @strSQL
  1050. FETCH NEXT FROM Dup_Stats INTO @strSQL
  1051. END
  1052. CLOSE Dup_Stats
  1053. DEALLOCATE Dup_Stats
  1054. PRINT '--############# Ended Duplicate indexes drop statements #############' + CHAR(10)
  1055. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpIPS'))
  1056. DROP TABLE #tmpIPS;
  1057. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpIOS'))
  1058. DROP TABLE #tmpIOS;
  1059. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpIUS'))
  1060. DROP TABLE #tmpIUS;
  1061. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpXIS'))
  1062. DROP TABLE #tmpXIS;
  1063. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpXNCIS'))
  1064. DROP TABLE #tmpXNCIS;
  1065. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpIxs'))
  1066. DROP TABLE #tmpIxs;
  1067. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpHashIxs'))
  1068. DROP TABLE #tmpHashIxs;
  1069. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpAgg'))
  1070. DROP TABLE #tmpAgg;
  1071. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpAggXTPHash'))
  1072. DROP TABLE #tmpAggXTPHash;
  1073. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tmpAggXTPNC'))
  1074. DROP TABLE #tmpAggXTPNC;
  1075. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tblWorking'))
  1076. DROP TABLE #tblWorking;
  1077. IF EXISTS (SELECT [object_id] FROM tempdb.sys.objects (NOLOCK) WHERE [object_id] = OBJECT_ID('tempdb.dbo.#tblCode'))
  1078. DROP TABLE #tblCode;
  1079. GO