Most updated and deleted table? - sql

How can we see most deleted and most updated table in sql server 2012 and upper levels.
And how can we see table that makes the most io?
Does these statistics withheld in Sql Server?

Index fragmentation should help - looking at number of rows and fragmentation:
SELECT
B.name AS TableName
,C.name AS IndexName
,C.fill_factor AS IndexFillFactor
,D.rows AS RowsCount
,A.avg_fragmentation_in_percent
,A.page_count
FROM
sys.dm_db_index_physical_stats(DB_ID(),NULL,NULL,NULL,NULL) A
INNER JOIN
sys.objects B
ON
A.object_id = B.object_id
INNER JOIN
sys.indexes C
ON
B.object_id = C.object_id AND A.index_id = C.index_id
INNER JOIN
sys.partitions D
ON
B.object_id = D.object_id AND A.index_id = D.index_id
WHERE
C.index_id > 0
order by
avg_fragmentation_in_percent desc
PFE: Thanks to Neeraj Dwivedi at SQL Central

Related

Getting duplication with inner join and left join

I am trying to fetch following information for the table of the columns :
1) Primary key.
2) IsNullable
3) Is_Identity.
Query :
SELECT c.name 'Column Name',c.is_nullable,c.is_identity,ISNULL(i.is_primary_key, 0) 'Primary Key' FROM sys.columns c INNER JOIN sys.types t ON c.user_type_id = t.user_type_id LEFT OUTER JOIN sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id LEFT OUTER JOIN sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id WHERE c.object_id = OBJECT_ID('[HumanResources].[Employee]')
I am working with Adventure Works database but the problem here is I am getting OrganizationNode column 2 times and I am unable to understand the reason.
For other tables I am not getting this issue but I am only getting this problem for the table [HumanResources].[Employee].
Below is the link to download database bak file :
https://www.dropbox.com/s/nutfat17b73boav/AdvantureWorksSeScript.bak?dl=0
I have also tried distinct but because of that it shuffled up the ordering of the column as you can see in the second output. BirthDate is coming at the top instead of BusinessEntityID.
I would not like to use distinct as my below query is working fine with all other tables but I am not getting why it is giving duplicate columns in case of [HumanResources].[Employee] table or may be I have tested it with few tables and query may not work properly for few other databases.
I am not sure whether my query is correct or not to work in all the scenarios to fetch above 3 things(pk,inullable etc.) which I mention.
Try this
SELECT name,
is_identity,
is_nullable,
is_primary_key
FROM (
SELECT c.name,
c.column_id,
c.is_identity,
c.is_nullable,
ISNULL(i.is_primary_key,0) is_primary_key,
ROW_NUMBER() OVER(PARTITION BY c.name ORDER BY c.name) rn
FROM sys.columns c
JOIN sys.tables t
ON t.object_id = c.object_id
LEFT JOIN sys.index_columns ic
ON c.object_id = ic.object_id AND c.column_id = ic.column_id
LEFT JOIN sys.indexes i
ON i.object_id = ic.object_id AND i.index_id = ic.index_id
WHERE t.name = 'Employee'
)keys
WHERE rn = 1
ORDER BY column_id

Records not shown while selecting from table in mssql

I'm Getting total tables in whole database and its row-count from following query:
SELECT SCHEMA_NAME(A.schema_id) + '.' +
A.Name, SUM(B.rows) AS 'RowCount'
FROM sys.objects A
INNER JOIN sys.partitions B ON A.object_id = B.object_id
WHERE A.type = 'U'
GROUP BY A.schema_id, A.Name
Order By 'RowCount' desc
After that i'm getting below result:
And then finally when i'm trying to fetch records from one of this table
select * from dbo.[xxx_$Retail ICT Header]
It gives 0 rows as output......Any clue?
To get the Row count in each table try below query and after that cross check by running select query.
SELECT s.name+'.'+o.name,
ddps.row_count
FROM sys.indexes AS i
INNER JOIN sys.objects AS o ON i.OBJECT_ID = o.OBJECT_ID
INNER JOIN sys.schemas s on s.schema_id= o.schema_id
INNER JOIN sys.dm_db_partition_stats AS ddps ON i.OBJECT_ID = ddps.OBJECT_ID
AND i.index_id = ddps.index_id
WHERE i.index_id < 2 AND o.is_ms_shipped = 0 ORDER BY o.NAME

Count(*) differs from rows in sys.partitions

I'm using the following query to get information about all tables in a DB:
SELECT
t.NAME AS TableName,
i.name as indexName,
sum(p.rows) as RowCounts,
sum(a.total_pages) as TotalPages,
sum(a.used_pages) as UsedPages,
sum(a.data_pages) as DataPages,
(sum(a.total_pages) * 8) / 1024 as TotalSpaceMB,
(sum(a.used_pages) * 8) / 1024 as UsedSpaceMB,
(sum(a.data_pages) * 8) / 1024 as DataSpaceMB
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
WHERE
t.NAME NOT LIKE 'dt%' AND
i.OBJECT_ID > 255 AND
i.index_id <= 1
GROUP BY
t.NAME, i.object_id, i.index_id, i.name
ORDER BY
object_name(i.object_id)
The problem is that for some tables it reports a different row count than if I do:
select count(*) FROM someTable
Why is that?
Edit:
The first query returns a higher count:
First: 1 240 464
Second: 413 496
The problem is that there's more than one allocation_unit per partition, so the same partition can appear more than once and therefore the sum(p.rows) ends up counting the same partition more than once, so you get a multiple of the right number of rows.
Here's how I solved the problem:
(note that my query isn't identical to yours, I have slightly different columns and used Kb rather than Mb, but the idea is the same)
SELECT
s.Name + '.' + t.name AS table_name,
(select sum(p2.rows)
from sys.indexes i2 inner join sys.partitions p2 ON i2.object_id = p2.OBJECT_ID AND i2.index_id = p2.index_id
where i2.object_id = t.object_id and i2.object_id > 255 and (i2.index_id = 0 or i2.index_id = 1)
) as total_rows,
SUM(CASE WHEN (i.index_id=0) OR (i.index_id=1) THEN a.total_pages * 8 ELSE 0 END) AS data_size_kb,
SUM(CASE WHEN (i.index_id=0) OR (i.index_id=1) THEN a.used_pages * 8 ELSE 0 END) AS data_used_kb,
SUM(CASE WHEN (i.index_id=0) OR (i.index_id=1) THEN 0 ELSE a.total_pages * 8 END) AS index_size_kb,
SUM(CASE WHEN (i.index_id=0) OR (i.index_id=1) THEN 0 ELSE a.used_pages * 8 END) AS index_used_kb,
SUM(a.total_pages) * 8 AS total_size_kb,
SUM(a.used_pages) * 8 AS total_used_kb,
SUM(a.used_pages) * 100 / CASE WHEN SUM(a.total_pages) = 0 THEN 1 ELSE SUM(a.total_pages) END AS percent_full
FROM
sys.tables t
INNER JOIN
sys.schemas s ON s.schema_id = t.schema_id
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
WHERE
t.is_ms_shipped = 0 AND i.OBJECT_ID > 255
GROUP BY
t.object_id, t.Name, s.Name
ORDER BY SUM(a.total_pages) DESC
From the sys.partitions documentation
rows bigint Approximate number of rows in this partition.
(emphasis mine). The system views aren't going to keep a spot-on number of rows in the table. Think of what that would entail and how much overhead it would add to all insert/delete statements. If I were a betting man, I'd say that it's doing something with the count of the number of pages in the clustered index or heap which is a far cheaper operation. That's purely speculative, though.
Have you looked into the help article regarding the sys.allocation_units view? Apparently, the container_id field is a little more than it seems. Try to add this to the where section:
and a.type = 2
In SQL Server 2016, To fix the count(*) and sys.partitions mismatch I performed an Index Rebuild on the Primary Key. Luckily the TABLE only had 2.4 million rows so it didn't take that long as I have standard edition so couldn't do the rebuild ONLINE.
Inner joins will cause unmatched rows to be filtered out. Groups will also affect your row counts as they can combine rows. These two conditions were cause a lower row count for the aggregate query than the simple count(*).
I see specifically you're asking about the sys.partitions table. The likely explanation is that there is not a match for every row in the sys.indexes table given the match condition of i.object_id = p.OBJECT_ID AND i.index_id = p.index_id. Try running this:
Select
count(*)
from
sys.partitions p
LEFT JOIN
sys.indexes i ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
You will then likely see the count you expect. Remove the count function to simply Select * ... to find the unmatched rows.

SQL Server index usage stats

I have run the query below in order to find indexes to delete.
SELECT d.name AS DatabaseName, t.name AS TableName, i.name AS IndexName, ius.*
FROM sys.dm_db_index_usage_stats ius
JOIN sys.databases d ON d.database_id = ius.database_id
JOIN sys.tables t ON t.object_id = ius.object_id
JOIN sys.indexes i ON i.object_id = ius.object_id AND i.index_id =
ius.index_id
ORDER BY user_updates DESC
But the result set that returns is pretty confusing. I am receiving multiple rows for the same indexes with different database_ids and therefore names. Let's say we have an index and its name is IDXName and its is IDXID. This index is under TBL1 inthe DB1 database. But there are multiple rows for this index with same index name and same index id and same table name but different database ids.
I double checked the Microsoft documentation and it confirms that the database id in that view is the database where the index resides. So how come I have the ids of the databases that that index does not exist in?
Yoy didn't linit the query to current database, that's why you're seeing data on indexes from different databases:
SELECT d.name AS DatabaseName, t.name AS TableName, i.name AS IndexName, ius.*
FROM sys.dm_db_index_usage_stats ius
JOIN sys.databases d ON d.database_id = ius.database_id
JOIN sys.tables t ON t.object_id = ius.object_id
JOIN sys.indexes i ON i.object_id = ius.object_id AND i.index_id = ius.index_id
WHERE d.database_id = db_id()
ORDER BY user_updates DESC
If all you need from sys.databases is a database name, there's no need for the join at all:
SELECT db_name() AS DatabaseName, t.name AS TableName, i.name AS IndexName, ius.*
FROM sys.dm_db_index_usage_stats ius
JOIN sys.tables t ON t.object_id = ius.object_id
JOIN sys.indexes i ON i.object_id = ius.object_id AND i.index_id = ius.index_id
WHERE ius.database_id = db_id()
ORDER BY user_updates DESC

Script that provides the row counts and table names

Maybe you easily said how to I provide table names and row counts?
Pseudo SQL:
for "select tablename from system.Tables" into :tablename
execute "select count(*) from ? into ?" using :tablename, :count
return row(:tablename, :count)
end for
Can you tell me show me this script in T-SQL?
If you're on SQL Server 2005 or newer (you unfortunately didn't specify which version of SQL Server you're using), this query should give you that information:
SELECT
TableName = t.NAME,
TableSchema = s.Name,
RowCounts = p.rows
FROM
sys.tables t
INNER JOIN
sys.schemas s ON t.schema_id = s.schema_id
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
WHERE
t.is_ms_shipped = 0
GROUP BY
t.NAME, s.Name, p.Rows
ORDER BY
s.Name, t.Name
This produces an output something like (this is from AdventureWorks):
TableName TableSchema RowCounts
AWBuildVersion dbo 1
DatabaseLog dbo 1597
ErrorLog dbo 0
Department HumanResources 16
Employee HumanResources 290
JobCandidate HumanResources 13
Address Person 19614
AddressType Person 6
... and so on......
SELECT
t.NAME AS TableName, p.[Rows] FROM
sys.tables t INNER JOIN
sys.partitions p ON t.object_id = p.OBJECT_ID GROUP BY
t.NAME, p.[Rows] ORDER BY t.NAME
-- Shows all user tables and row counts for the current database
-- Remove OBJECTPROPERTY function call to include system objects
SELECT o.NAME,
i.rowcnt
FROM sysindexes AS i
INNER JOIN sysobjects AS o ON i.id = o.id
WHERE i.indid < 2 AND OBJECTPROPERTY(o.id, 'IsMSShipped') = 0
ORDER BY o.NAME
exec sp_MSForEachTable 'SELECT ''?'' as TableName, COUNT(*) as Rows FROM ?'
I have adjusted the answer from marc_c with CTE and displaying it with choice of displaying just the schema you are after.
Should work with SQL Serve 2005 and newer.
WITH CountRowsInTables (Table_Name, Table_Schema, Row_Counts) AS
(
SELECT
TableName = t.Name,
TableSchema = s.Name,
RowCounts = p.Rows
FROM
sys.tables t
INNER JOIN
sys.schemas s ON t.schema_id = s.schema_id
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
WHERE
t.is_ms_shipped = 0
GROUP BY
s.Name, t.Name, p.Rows
)
SELECT Table_name, Table_Schema, Row_Counts
FROM CountRowsInTables
WHERE Table_Schema = 'Pick_Schema_to_display';
Try this
-- drop table #tmpspace
create table #tmpspace (
name sysname
, rows int
, reserved varchar(50)
, data varchar(50)
, index_size varchar(50)
, unused varchar(50)
)
dbcc updateusage(0) with NO_INFOMSGS
exec sp_msforeachtable 'insert #tmpspace exec sp_spaceused ''?'''
select * from #tmpspace
order by convert(int, substring(reserved, 1, charindex(' ', reserved))) desc, rows desc, name
Works on sql2000 too.
dbcc updateusage might take some time but results will be 100% actual. Skip it if you need speed over accuracy.
This works for me:
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