I have below query which is a fairly simple query and as per the execution plan it is also using appropriate non-clustered index seek for both the tables.
declare #P0 int,#P1 int
SELECT PTD_TEST_RESULT_TYPE, PTD_READING_TEXT FROM TRN_PT_TESTS_HEAD,
TRN_PT_TESTS_DET WHERE PTH_ENC_ID = #P0 AND PTD_PTH_ID = PTH_ID AND
PTD_READING_TEXT IS NOT NULL AND PTD_READING_TEXT <> '' AND PTD_TEST_ID = #P1
AND
PTD_TEST_SET_NO = 0 ORDER BY PTD_ID
However, I can see sometimes the query takes 13sec to execute, while checking into sys.dm_exec_requests table I can see it is waiting on wait_resource '60:1:1971533'
On further debugging I found 60:1:1971533 is actually Database_Id : FileId : PageNumber
i.e.
database_id=60
data_file_id = 1
page_number = 1971533
Further I found the page_number is actually a PK_PTH_ID for TRN_PT_TESTS_HEAD table
I used below query to find this
SELECT sc.name as schema_name, so.name as object_name, si.name as index_name
FROM sys.objects as so
JOIN sys.indexes as si on so.object_id = si.object_id
JOIN sys.schemas AS sc on so.schema_id = sc.schema_id
WHERE so.object_id = 1602104748
and si.index_id = 1
My question is, If the query is using appropriate non-clustered index seek for TRN_PT_TESTS_HEAD table then why the query is waiting on PK_PTH_ID clustered index of same table.
Do anyone have some idea on this?
Related
I have to get a list of all the stored procedures that use an index from a table. My task is to delete the indexes that are not being used in the database or that are not overloaded with indexes.
Well first I get all indexes from a table:
DECLARE #tableName AS VARCHAR(50) = 'tbl_INVOICES';
SELECT
IND.name as IndexName
FROM sys.indexes IND
INNER JOIN sys.index_columns IND_COL on IND.index_id = IND_COL.index_id and IND.object_id = IND_COL.object_id
WHERE IND.object_id = OBJECT_ID(#tableName)
AND is_included_column = 0
ORDER BY
IndexName
with each one of these indexes that I obtain, I have to do another search, the expected result is a list of the procedures stored with the name of the index that they are using
SELECT
OBJ.name AS [object_name],
IND.name AS index_name,
IND.type_desc,
IND_USA_STATS.user_seeks,
IND_USA_STATS.user_scans,
IND_USA_STATS.user_lookups,
IND_USA_STATS.user_updates,
OBJ.type,
IND.type,
IND_USA_STATS.index_id
FROM
sys.indexes IND
INNER JOIN
sys.objects OBJ ON IND.[object_id] = OBJ.[object_id]
LEFT JOIN
sys.dm_db_index_usage_stats IND_USA_STATS ON IND.[object_id] = IND_USA_STATS.[object_id] AND
IND.index_id = IND_USA_STATS.index_id AND
IND_USA_STATS.database_id = DB_ID(#tableName)
WHERE
OBJ.type IN ('P')
ORDER BY
index_name
Well, it took a long time and I can't get the list, this was the last attempt I made and it doesn't give me results, the question here is how do I get the list of stored procedures that uses a determined index?
In article about sys.indexes there is a phrase that this view
Contains a row per index or heap of a tabular object, such as a table,
view, or table-valued function.
I was interested to find a size of such an index.
So I created function with index:
create function fIndexSize()
returns #res table
(
object_id int not null
, name varchar(128) not null
, primary key (object_id)
)
as
begin
insert into #res
select object_id, name
from sys.objects
where object_id > 255
return
end
Here we can see the name of new index:
There is also a record in sys.indexes:
Usually I get sizes of indexes using this query:
select
o.schema_id
, o.object_id
, o.name
, o.type_desc
, sum (a.total_pages) * 8.00 / 1024 / 1024 as TotalSpaceGB
from sys.objects o
inner join sys.indexes i on o.object_id = i.object_id
inner join sys.partitions p on i.object_id = p.object_id and i.index_id = p.index_id
inner join sys.allocation_units a on p.partition_id = a.container_id
where (o.name = 'fIndexSize' or i.name like 'PK__fIndexSi%')
group by o.schema_id, o.object_id, o.name, o.type_desc
But this time nothing was returned.
Can anyone give me advice how to find size of such an index?
Yes you can find the size of this index, but you should consider it's living only for a time of a batch and you should look for it in tempdb (as it is table variable):
create function fIndexSize()
returns #res table
(
object_id_xxxx int not null
, name varchar(128) not null
, primary key (object_id_xxxx)
)
as
begin
insert into #res
select object_id, name
from sys.objects
where object_id > 255
return
end;
go
select i.name,
c.name,
8 * SUM(au.used_pages) as size_kb
from tempdb.sys.indexes i
join tempdb.sys.columns c
on i.object_id = c.object_id
join tempdb.sys.partitions as p
on p.object_id = i.object_id and p.index_id = i.index_id
join tempdb.sys.allocation_units as au
on au.container_id = p.partition_id
where c.name = 'object_id_xxxx'
group by i.name,
c.name
I left the column name here only to show that the index found is what we are looking for, and I chose the column name with xxxx for distinguish it well
The result of a table-valued function is not stored in a permanent table in the database. It is generated on the fly during the query execution.
Yes, you have a row in sys.indexes which tells you index properties, like type (clustered or not), is_primary_key, is_unique, etc.
But, there are no corresponding rows in sys.partitions and in sys.allocation_units. That's why your query returns nothing. If you replace inner joins with left joins, you'd see one row with NULL as TotalSpaceGB.
So, documentation is correct. Documentation doesn't say that table-valued functions will have rows in sys.allocation_units.
Each invocation of the function may return different number of rows. This set of rows doesn't exist before the query runs and it doesn't exist after the query finishes.
Even during the function execution sys.partitions and sys.allocation_units are empty for this index (PK__fIndexSi...).
When I looked at the actual execution plan of the query
select * from fIndexSize()
I could see that optimiser creates a temp table behind the scenes. Well, it has to store the rows somewhere and they are stored in TempDB.
So, you should run your select from sys.allocation_units using tempdb.
At first I used SQL Sentry Plan Explorer to see the name of the temporary table:
Then I ran your query against TempDB:
I want to check whether all the tables in my Database has clusterd index on which type of column(eg:int) and whether the clustered index is on a single/multipe columns. I was not able to figure out which DMvs or Views to retreive this information. Please help.
below sql will give you list of the clusterd indexes and table name with few more detail. you can modified this to get to you results.
SELECT 'ClusteredIndexName' = SI.Name,
'TableName' = SO.Name,
'ColumCount' = IK.IndexColumnCount,
'IsUnique' = CASE WHEN SI.is_unique = 0 THEN 'N' ELSE 'Y' END
,SI.type_desc
FROM SYS.INDEXES SI
JOIN SYS.OBJECTS SO -- Joining on SYS.OBJECTS to get the TableName
ON SO.OBJECT_ID = SI.Object_ID
JOIN ( -- Joining on a Derived view to work out how many columns exist on the clustered index
SELECT 'IndexColumnCount' = MAX(KEY_ORDINAL), OBJECT_ID, index_id
FROM SYS.INDEX_COLUMNS
GROUP BY OBJECT_ID, index_id
) AS IK
ON IK.object_id = SI.Object_ID
AND IK.index_id = SI.index_id
WHERE SI.type_desc = 'CLUSTERED' and
SI.OBJECT_ID NOT IN (SELECT OBJECT_ID
FROM SYS.ALL_OBJECTS
WHERE TYPE = 'S') -- Not system tables
AND SO.Type = 'U'
AND SO.is_ms_shipped = 0
Sql Server Management Studio lets you see the definition of all indexes on a table. Find the table in the Tables folder, expand, and expand Indexes. Select Properties on a particular index to view columns and other properties.
SELECT * FROM sys.indexes (I think, its been a while since I used SQL Server).
I'm trying to get the last time a table was updated by the users:
Declare #Collect Table (Name Varchar(100),last_user_update datetime)
Insert into #Collect
EXEC sp_MSForEachTable 'SELECT ''?'' as TableName,
last_user_update
FROM sys.dm_db_index_usage_stats
WHERE database_id = DB_ID(''SP3D_DB_RESEARCH_MDB'') AND OBJECT_ID = OBJECT_ID(''?'')'
SELECT * FROM #Collect ORDER BY last_user_update DESC
The problem is that in the results, some tables are appearing 3 times (please see the image bellow)
Since it appears that all tables duplicated have the same last updated time. Is there any way to group the results by the table name?
If the values are indeed the same, you can just add DISTINCT to the query, and have it return unique results
SELECT DISTINCT ''?'' as TableName, last_user_update ...
If you want to group after the fact, and only the last update interests you, you can do
SELECT TableName, max(last_user_update) as last_update
FROM #Collect
GROUP BY TableName
ORDER BY 2 DESC
Tables can have multiple indexes. The dynamic management view sys.dm_db_index_usage_stats will have separate entries for each index.
If you want to see the index name for each one, try this:
SELECT
o.name as TableName,
i.name as IndexName,
istats.last_user_update
from sys.dm_db_index_usage_stats istats
inner join sys.objects o
on o.object_id = istats.object_id
inner join sys.indexes i
on i.index_id = istats.index_id
and i.object_id = istats.object_id
order by
o.name,
i.name
Or, if you don't care about that and just want the last update time, you can group by the table name:
SELECT
o.name as TableName,
max(istats.last_user_update)
from sys.dm_db_index_usage_stats istats
inner join sys.objects o
on o.object_id = istats.object_id
group by
o.name
You can do an insert directly into your table with this query:
declare #Collect table (Name varchar(100),last_user_update datetime)
insert into #Collect
select
o.name as TableName,
istats.last_user_update
from sys.dm_db_index_usage_stats istats
inner join sys.objects o
on o.object_id = istats.object_id
inner join sys.indexes i
on i.index_id = istats.index_id
and i.object_id = istats.object_id
where database_id = db_id('SP3D_DB_RESEARCH_MDB')
Also, I'm not sure what your goal is, but please understand that this view only has entries for indexes that have activity on them. If an index is unused, it is not in this view. The first access creates a row in the view. The real interesting stuff on this view is the seek and scan information.
See this note from MSDN:
When an index is used, a row is added to sys.dm_db_index_usage_stats
if a row does not already exist for the index. When the row is added,
its counters are initially set to zero.
If your goal is to enumerate all the indexes and then show the last update date for all of them, you'll need to join to sys.indexes and then left join to sys.dm_db_index_usage_stats.
Is there a way in SQL Server 2008 to find the table with the most rows in the database?
This will get you close:
SELECT
[TableName] = so.name,
[RowCount] = MAX(si.rows)
FROM
sysobjects so,
sysindexes si
WHERE
so.xtype = 'U'
AND
si.id = OBJECT_ID(so.name)
GROUP BY
so.name
ORDER BY
2 DESC
Here's basically the same T-SQL that Chris Ballance provided, but using the new Object Catalog Views instead of the compatability views:
SELECT SchemaName = schemas.[name],
TableName = tables.[name],
IndexName = indexes.[name],
IndexType =
CASE indexes.type
WHEN 0 THEN 'Heap'
WHEN 1 THEN 'Clustered'
END,
IndexPartitionCount = partition_info.PartitionCount,
IndexTotalRows = partition_info.TotalRows
FROM sys.tables
JOIN sys.indexes
ON tables.object_id = indexes.object_id
AND indexes.type IN ( 0, 1 )
JOIN ( SELECT object_id, index_id, PartitionCount = COUNT(*), TotalRows = SUM(rows)
FROM sys.partitions
GROUP BY object_id, index_id
) partition_info
ON indexes.object_id = partition_info.object_id
AND indexes.index_id = partition_info.index_id
JOIN sys.schemas ON tables.schema_id = schemas.schema_id
ORDER BY SchemaName, TableName;
I just customize my SSMS 2008 to show the following additional columns
for tables
- Row Count
- Data Space Used (KB)
for databases
- Primary Data Location
- Last Backup Date
- Created Date
....
Works quicker for me most of the time without opening a query, I just click on the column header to go ASC or DESC