How to use sp_MSforeachdb - sql

I have a code that returns all indexes with a fragmentation % greater than 30
Iwant this code to run through all my databases and add the resultset to a table I have called IndexesToRebuild
is there a way I can use the sp_MSforeachdb to run this query thoughout all databases and insert the resultset to the IndexesToRebuild table
here is the code I have so far
if(not exists(select 1 from Utility..dtlIndexesToRebuild))
begin
insert into utility..dtlIndexesToRebuild
select
DB_NAME(),
dbschemas.[name],
dbtables.[name],
dbindexes.[name],
indexstats.avg_fragmentation_in_percent
from
sys.dm_db_index_physical_stats (DB_ID(), null, null, null, null) as indexstats
inner join sys.tables dbtables on dbtables.[object_id] = indexstats.[object_id]
inner join sys.schemas dbschemas on dbtables.[schema_id] = dbschemas.[schema_id]
inner join sys.indexes as dbindexes on dbindexes.[object_id] = indexstats.[object_id]
and indexstats.index_id = dbindexes.index_id
where
indexstats.database_id = DB_ID()
and avg_fragmentation_in_percent > 30
end

sp_msforeachdb has some "features". For something as simple as this, it'll likely be easier to simply leverage some dynamic SQL:
USE master;
GO
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET #SQL = STUFF((SELECT #CRLF + #CRLF +
N'USE ' + QUOTENAME([name]) + N';' + #CRLF +
N'INSERT INTO utility.dbo.dtlIndexesToRebuild (DatabaseName, SchemaName, TableName, IndexName, Fragmentation)' + #CRLF + --Guessed names of your columns
N'SELECT DB_NAME(),' + #CRLF +
N' dbschemas.[name],' + #CRLF +
N' dbtables.[name],' + #CRLF +
N' dbindexes.[name],' + #CRLF +
N' indexstats.avg_fragmentation_in_percent' + #CRLF +
N'FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, NULL) indexstats' + #CRLF +
N' INNER JOIN sys.tables dbtables ON dbtables.[object_id] = indexstats.[object_id]' + #CRLF +
N' INNER JOIN sys.schemas dbschemas ON dbtables.[schema_id] = dbschemas.[schema_id]' + #CRLF +
N' INNER JOIN sys.indexes dbindexes ON dbindexes.[object_id] = indexstats.[object_id]' + #CRLF +
N' AND indexstats.index_id = dbindexes.index_id' + #CRLF +
N'WHERE indexstats.database_id = DB_ID()' + #CRLF +
N' AND avg_fragmentation_in_percent > 30;'
FROM sys.databases d
WHERE d.database_id > 4
--AND d.[name] != N'utility' --Don't know if you want to skip this
FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,4,N'');
--PRINT #SQL; --Your best friend. Use SELECT for over 4,000 characters.
EXEC sys.sp_executesql #SQL;
Your best friend will help you debug any errors, but I've assumed the statement you supplied is valid.

Related

Search for a value in all column and all tables of a database

I want to find the column name that contain the value "Commerciale", but i do not know the column name or the table so I need to search in the whole database. How can i do that with a query?
I'm using SQL SERVER
If you are looking for columns where the name is Commerciale then you can simply use the sys objects:
SELECT s.[name] AS SchemaName,
t.[name] AS TableName,
c.[name] AS ColumnName
FROM sys.schemas s
JOIN sys.tables t ON s.schema_id = t.schema_id
JOIN sys.columns c ON t.object_id = c.object_id
WHERE c.[name] = N'Commerciale';
If, however, you need to search the contents of the values in the rows, you'll need to use dynamic SQL. This will return a dataset for every table in your database which has at least 1 string type column, and will return any rows where the value of one of those columns has the value 'Commerciale'. If it needs to contain the value, change the WHERE to use a LIKE in it's clauses instead (note the query will be horrifically slow with that):
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET #SQL = STUFF((SELECT #CRLF +
N'SELECT N' + QUOTENAME(s.[name],'''') + N' AS SchemaName,' + #CRLF +
N' N' + QUOTENAME(t.[name],'''') + N' AS TableName,' + #CRLF +
N' *' + #CRLF +
N'FROM ' + QUOTENAME(s.[name]) + N'.' + QUOTENAME(t.[name]) + #CRLF +
N'WHERE ' +
STUFF((SELECT #CRLF +
N' AND ' + QUOTENAME(c.[name]) + N' = ''Commerciale'''
FROM sys.columns c
JOIN sys.types ct ON c.system_type_id = ct.system_type_id
WHERE c.object_id = t.object_id
AND ct.[name] IN (N'char',N'varchar',N'nchar',N'nvarchar')
FOR XML PATH(''),TYPE).value('(./text())[1]','nvarchar(MAX)'),1,8,N'') + N';'
FROM sys.schemas s
JOIN sys.tables t ON s.schema_id = t.schema_id
FOR XML PATH(''),TYPE).value('(./text())[1]','nvarchar(MAX)'),1,2,N'');
--PRINT #SQL; --YOu best friend
EXEC sp_executesql #SQL;
This won't tell you what column has the value, you'll need to use your own eyes to do that, but I wasn't entertaining writing a dynamic table dynamic pivot.
you can use system tables :
SELECT
c.name ColumnName
, t.name TableName
FROM sys.columns AS c
JOIN sys.tables AS t
ON c.object_id = t.object_id
WHERE c.name like '%Commerciale%'

Get data from all tables ending with a specific string

I'm looking for a query that gets the nextval column from a lot of different tables ending with "_seq".
I know how to get the names of the tables...
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME LIKE '%seq'
But i couldn't find how to get the values from all those tables at once...
All those tables have the same column.
You can only achieve this with dynamic SQL. As you're using SQL Sevrer 2012, you'll need to use the "old" FOR XML PATH method to create the "delimited" UNION ALL query. I've also added the name of the schema and table into the query's dataset, as I assume this would be valuable information:
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET #SQL = STUFF((SELECT N'UNION ALL' + #CRLF +
N'SELECT N' + QUOTENAME(s.[name],'''') + N' AS SchemaName,' + #CRLF +
N' N' + QUOTENAME(t.[name],'''') + N' AS TableName,' + #CRLF +
N' NextVal' + #CRLF +
N'FROM ' + QUOTENAME(s.[name]) + N'.' + QUOTENAME(t.[name])
FROM sys.schemas s
JOIN sys.tables t ON s.schema_id = t.schema_id
JOIN sys.columns c ON t.object_id = c.object_id
WHERE t.[name] LIKE '%[_]seq'
AND c.[name] = N'NextVal'
FOR XML PATH(''),TYPE).value('.','nvarchar(MAX)'),1,11,N'') + N';'
--PRINT #SQL; --Your best friend.
EXEC sys.sp_executesql #SQL;
As there's no data to test with, you'll need to use your "best friend" to debug if it doesn't work.

Loop through different tables for count

I have a table that has thousands of records. Usually 100k-200k records. There is a field in this table (MATCH_ID). I have to get count of this ID from 100 different tables whose field(TABLE_ID) value matches with MATCH_ID.
I think implementing this using cursors would be a bad idea here. How can I achieve this with better performance.
This makes a couple of a couple of assumptions, but you could create a dynamic UNION ALL query; which'll be far faster than a CURSOR:
DECLARE #MatchID int; --Guessed data type; will need a value
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET #SQL = STUFF((SELECT #CRLF +
N'UNION ALL' + #CRLF +
N'SELECT N' + QUOTENAME(s.[name],'''') + N' AS SchemaName,' + #CRLF +
N' N' + QUOTENAME(t.[name],'''') + N' AS TableName,' + #CRLF +
N' COUNT(*) AS RowCount' + #CRLF +
N'FROM ' + QUOTENAME(s.[name]) + N'.' + QUOTENAME(t.[name]) + #CRLF +
N'WHERE MATCH_ID = #MatchID'
FROM sys.schemas s
JOIN sys.tables t ON s.schema_id = t.schema_id
JOIN sys.columns c ON t.object_id = c.object_id
WHERE c.[name] = N'MATCH_ID'
ORDER BY t.[name]
FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,13,N'') + N';';
--SELECT #SQL; --Your best friend
EXEC sp_executesql #SQL, N'#MatchID int', #MatchID;
As I can't test this, use your "best friend" if you get any syntax errors, or you don't quite get the results you expect.

Select tables where any data exists

I have acces to huge MSSQL DB. This DB have many tables, but huge amount of it is empty. How do I query DB schema to select table names, where any rows exists? (I'd like to create ERD only from these tables that haves some data, when I achieve this). I did not found any related questions.
A quick but approximate query you can use is the following one, just check the RowCount column:
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
[RowCount] DESC
If you want the real count, you will have to issue a SELECT that returns a script of multiple SELECT with COUNT(*) and probably a bunch of UNION ALL. It might take long to finish if you are concurrently accessing the tables or if they are very big.
If you do a real count then you can use a dynamic script to do this. Note that, as #Ezlo mentions, this will be (a lot) slower than the estimated counts using the sys.partitions object:
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET #SQL = STUFF((SELECT #CRLF +
N'UNION ALL' + #CRLF +
N'SELECT N' + QUOTENAME(s.[name],'''') + N' AS SchemaName,' + #CRLF +
N' N' + QUOTENAME(t.[name],'''') + N' AS TableName,' + #CRLF +
N' COUNT(*) AS TotalRows' + #CRLF +
N'FROM ' + QUOTENAME(s.[name]) + N'.' + QUOTENAME(t.[name])
FROM sys.schemas s
JOIN sys.tables t ON s.schema_id = t.schema_id
FOR XML PATH(''),TYPE).value('.','nvarchar(MAX)'),1,11,N'') + N';'
--SELECT #SQL; --To see the SQL if you want
EXEC sp_executesql #SQL;

Drop Several Tables at Once

I have some tables that I need to drop on a regular basis. The names of the tables sometimes change but the table names always begin with 'db_comp_temp'. Is it possible to write some SQL that will in effect do something like:
DROP TABLE db_comp_temp*
Thanks in advance,
No, there is no wildcard support in DDL.
DECLARE #sql nvarchar(max) = N'';
SELECT #sql += N'DROP TABLE ' + QUOTENAME(s.name)
+ '.' + QUOTENAME(t.name) + ';
' FROM sys.tables AS t
INNER JOIN sys.schemas AS s
ON t.[schema_id] = s.[schema_id]
WHERE t.name LIKE N'db[_]comp[_]temp%';
PRINT #sql;
-- EXEC sys.sp_executesql #sql;
Or:
DECLARE #sql nvarchar(max) = N'';
SELECT #sql += N'
,' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name)
FROM sys.tables AS t
INNER JOIN sys.schemas AS s
ON t.[schema_id] = s.[schema_id]
WHERE t.name LIKE N'db[_]comp[_]temp%';
SET #sql = N'DROP TABLE ' + STUFF(#sql, 1, 1, '');
PRINT #sql;
-- EXEC sys.sp_executesql #sql;
You could also do it with FOR XML PATH but I don't think it's necessary when you're not grouping the result into another outer query.