How to determine when SQL Server index statistics were last updated - sql-server

I want to check which index stats that hasn't been updated on tables that has page count of 500 mb and more.
I have this script:
SELECT OBJECT_NAME(object_id) AS [ObjectName]
,[name] AS [StatisticName]
,STATS_DATE([object_id], [stats_id]) AS [StatisticUpdateDate]
FROM sys.stats
order by StatisticUpdateDate desc
Its not giving me all the info I want. Can someone be able to modify my script or show me how to get where I want to be?
Thanks in advance.

Here is a solution for 500 pages even if I think 500Mb is more reasonable finter:
with cte as
(
select p.object_id,
object_name(p.object_id) as obj,
sum(total_pages) as tot_pages
from sys.partitions p join sys.allocation_units au
on au.container_id = p.hobt_id
group by p.object_id
)
SELECT c.* ,
OBJECT_NAME(st.object_id) AS [ObjectName]
,[name] AS [StatisticName]
,STATS_DATE(st.[object_id],
[stats_id]) AS [StatisticUpdateDate]
FROM sys.stats st join cte c
on st.object_id = c.object_id
where tot_pages >= 500;

Related

Does a SELECT COUNT(*) query have to do a full table scan?

Does a query that gets the count of all rows in a table have to do a full table scan or does SQL Server maintain a count of rows somewhere?
SELECT COUNT(*) FROM TABLE_NAME;
The table TABLE_NAME has a primary key, and therefore a clustered index, and looks like so:
CREATE TABLE TABLE_NAME
(
Id int PRIMARY KEY IDENTITY(1, 1),
Name nvarchar(50) NOT NULL
);
I am using Microsoft SQL Server 2014.
The server will always read all records (if there's an index then it will scan the entire index) to count the rows. You can't escape this as long as you are doing SELECT COUNT(*) FROM Table.
If your table has a clustered index, you can change your query to an "under the hood" query to retrieve the count without actually fetching the records with:
SELECT OBJECT_NAME(i.id) [Table_Name], i.rowcnt [Row_Count]
FROM sys.sysindexes i WITH (NOLOCK)
WHERE i.indid in (0,1)
ORDER BY i.rowcnt desc
if you are looking for an approximate count of the records, you can also use the following query:
SELECT
TableName = t.NAME,
SchemaName = s.Name,
[RowCount] = p.rows,
TotalSpaceMB = CONVERT(DECIMAL(18,2), SUM(a.total_pages) * 8 / 1024.0),
UsedSpaceMB = CONVERT(DECIMAL(18,2), SUM(a.used_pages) * 8 / 1024.0),
UnusedSpaceMB = CONVERT(DECIMAL(18,2), (SUM(a.total_pages) - SUM(a.used_pages)) * 8 / 1024.0)
FROM
sys.tables t
INNER JOIN sys.indexes i ON t.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
LEFT OUTER JOIN sys.schemas s ON t.schema_id = s.schema_id
WHERE
t.NAME NOT LIKE 'dt%'
AND t.is_ms_shipped = 0
AND i.OBJECT_ID > 255
GROUP BY
t.Name,
s.Name,
p.Rows
ORDER BY
TotalSpaceMB DESC
This will show non-system tables with their calculated (not exact) row count and the sum of the sizes of their data (with any index they might have), relatively fast without retrieving the records.
When SQL Server performs a query like SELECT COUNT(*), SQL Server will use the narrowest non-clustered index to count the rows. If the table does not have any non-clustered index, it will have to scan the table.
If your table has a clustered index you can get your count even faster.
SELECT COUNT(*) FROM TABLE_NAME;
Does a full table scan.
For optimizations you can refer to this.
you can following way. it is better in performance I guess.
SELECT COUNT(1) FROM TABLE_NAME

SQL Server 2012 Metadata Syntax

Sorry for the dumb question in advance and thanks for your time!
I have two queries
Query A
SELECT sc.name +'.'+ta.name TableName
,SUM(pa.rows) RowCnt
FROM sys.tables ta
INNER JOIN sys.partitions pa
ON pa.object_id = ta.object_id
INNER JOIN sys.schemas sc
ON ta.schema_id=sc.schema_id
WHERE ta.is_ms_shipped = 0 AND pa.index_id IN (1,0)
GROUP BY sc.name,ta.name
ORDER BY sum(pa.rows) DESC;
and
Query B
SELECT ta.name TableName
, ta.modify_date ModDate
, ta.max_column_id_used "Columns"
FROM sys.tables ta
ORDER BY ta.name;
which execute fine.
When I add the lines
, ta.modify_date ModDate
, ta.max_column_id_used "Columns"
I create Query C
SELECT sc.name +'.'+ta.name TableName
,SUM(pa.rows) RowCnt
, ta.modify_date ModDate
, ta.max_column_id_used "Columns"
FROM sys.tables ta
INNER JOIN sys.partitions pa
ON pa.object_id = ta.object_id
INNER JOIN sys.schemas sc
ON ta.schema_id=sc.schema_id
WHERE ta.is_ms_shipped = 0 AND pa.index_id IN (1,0)
GROUP BY sc.name,ta.name
ORDER BY sum(pa.rows) DESC;
I get the error
8120, Level 16, State 1, Line 3
Column 'sys.tables.modify_date' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I understand what aggregate fx are as well as the purpose of GROUP BY.
Do I need to create a subquery to include this information in one query

Stored procedure inner join

My first stored procedure (in sql-server). I'm not able to make it work, it raises a warning in a.Id
After reading, and not really understand much, I'm not even sure if I can use this inner join inside a stored procedure.
select top 1 b.*, a.*
FROM Bids b
INNER JOIN Auctions a
ON b.Auction_Id = a.Id
(NOLOCK) WHERE ( a.Ends IS NOT NULL AND a.Starts IS NOT NULL AND a.Starts < #Now AND a.Ends > #Now)
ORDER BY b.CreationTime DESC
Actually, I'll need just b.* but I assume I have to retrieve all the fields?.
Thanks
Change the locking hint to this:
INNER JOIN Auctions a WITH(NOLOCK)
Full query would be:
select top 1 b.*, a.*
FROM Bids b
JOIN Auctions a WITH(NOLOCK) ON b.Auction_Id = a.Id
WHERE ( a.Ends IS NOT NULL
AND a.Starts IS NOT NULL
AND a.Starts < #Now
AND a.Ends > #Now)
ORDER BY b.CreationTime DESC
Take the (Nolock) out, or put it after the table name if you need it.
You can use Select to select any fields you want from any table in your query.

Finding blocking/locking queries in MS SQL (mssql)

Using sys.dm_os_wait_stats I have identified what I believe is a locking problem
wait type waittime pct running ptc
LCK_M_RS_S 2238.54 22.14 22.14
LCK_M_S 1980.59 19.59 41.73
Is there a way I can find the top blocking/locking queries? I've tried querying sys.dm_db_index_operational_stats without much luck.
You may find this query useful:
SELECT *
FROM sys.dm_exec_requests
WHERE DB_NAME(database_id) = 'YourDBName'
AND blocking_session_id <> 0
To get the query itself use this one:
SELECT text,*
FROM sys.dm_exec_requests
CROSS APPLY sys.dm_exec_sql_text(sql_handle)
WHERE DB_NAME(database_id) = 'YourDBName'
AND blocking_session_id <> 0
I found this query which helped me find my locked table and query causing the issue.
SELECT L.request_session_id AS SPID,
DB_NAME(L.resource_database_id) AS DatabaseName,
O.Name AS LockedObjectName,
P.object_id AS LockedObjectId,
L.resource_type AS LockedResource,
L.request_mode AS LockType,
ST.text AS SqlStatementText,
ES.login_name AS LoginName,
ES.host_name AS HostName,
TST.is_user_transaction as IsUserTransaction,
AT.name as TransactionName,
CN.auth_scheme as AuthenticationMethod
FROM sys.dm_tran_locks L
JOIN sys.partitions P ON P.hobt_id = L.resource_associated_entity_id
JOIN sys.objects O ON O.object_id = P.object_id
JOIN sys.dm_exec_sessions ES ON ES.session_id = L.request_session_id
JOIN sys.dm_tran_session_transactions TST ON ES.session_id = TST.session_id
JOIN sys.dm_tran_active_transactions AT ON TST.transaction_id = AT.transaction_id
JOIN sys.dm_exec_connections CN ON CN.session_id = ES.session_id
CROSS APPLY sys.dm_exec_sql_text(CN.most_recent_sql_handle) AS ST
WHERE resource_database_id = db_id()
ORDER BY L.request_session_id
Use the script: sp_blocker_pss08 or SQL Trace/Profiler and the Blocked Process Report event class.

when were index statistics last updated?

Is there a quick and easy way to list when every index in the database last had their statistics updated? The preferred answer would be a query. Also, is it possible to determine the "quality" of the statistics: FULLSCAN, SAMPLE n, etc.
EDIT
This worked for what I needed, a slight mod to #OrbMan great answer...
SELECT
STATS_DATE(i.object_id, i.index_id) AS LastStatisticsDate
,o.Name AS TableName
,i.name AS IndexName
FROM sys.objects o
INNER JOIN sys.indexes i ON o.object_id = i.object_id
WHERE o.is_ms_shipped=0
ORDER BY 1 DESC
You can do: STATS_DATE ( table_id , index_id )
So:
USE AdventureWorks;
GO
SELECT 'Index Name' = i.name, 'Statistics Date' = STATS_DATE(i.object_id, i.index_id)
FROM sys.objects o
JOIN sys.indexes i ON o.name = 'Address' AND o.object_id = i.object_id;
GO
where Address is the name of the table whose indexes you would like to examine.

Resources