I need the run the following query on many databases, I have over 100+ databases, but I don't want to pull up each database and run the query one at a time.
The User table is only listed in Database#_Account.
If the query is ran it errors out because Database#_Admin does not have User Table.
(EXAMPLE Database List)
Database:
---------------------
MASTER
Model
msdb
tempdb
Database1_Account
Database1_Admin
Database2_Account
Database2_Admin
Database3_Account
Database3_Admin
Query:
EXEC sp_MsForEachDb #command1 = SELECT "?" as DatabaseName, *
FROM ?.User
WHERE Name = "John" AND "?" LIKE "%_Account"
ms_foreachDb is still an undocumented function and it subject to change anytime. I would use a cursor for something like this.
Here is a working template to get you started:
DECLARE #tsql nvarchar(max)
DECLARE #dbname varchar(500)
DECLARE MyCur CURSOR STATIC FORWARD_ONLY FOR
SELECT [name]
FROM sys.databases
WHERE [name] NOT IN ('tempdb')
OPEN MyCur
WHILE (1=1)
BEGIN
FETCH NEXT FROM MyCur INTO #dbname
IF ##FETCH_STATUS <> 0
BREAK
SET #tsql = 'use ' + #dbname + ' SELECT * FROM sys.tables'
EXEC sp_executesql #tsql
END
CLOSE MyCur;
DEALLOCATE MyCur;
You need to pass the command as an nvarchar literal, not as a query.
You need to use the correct nomenclature. You've left out the schema name. It's Database.Schema.Table, not Database.Table. I'm assuming all tables use the default dbo schema.
Write the query to test if the table exists before executing. Easiest way to do that is with IF OBJECT_ID(N'TableName') IS NOT NULL.
Avoid double quotes. They're normally field identifiers like square brackets are, so they're potentially ambiguous when used for varchar literals.
Try:
EXEC sp_MsForEachDb #command1 = N'IF OBJECT_ID(N''?.dbo.User'') IS NOT NULL SELECT ''?'' as DatabaseName, * FROM ?.dbo.User WHERE Name = ''John'' AND ''?'' LIKE ''%_Account'''
Here's the query I use to do a while loop to iterate through Databases.
Just put your code where it says PUT CODE HERE.
SET NOCOUNT ON
DECLARE #Database TABLE (DbName SYSNAME)
DECLARE #DbName AS SYSNAME
SET #DbName = ''
INSERT INTO #Database (DbName)
SELECT NAME
FROM master.dbo.sysdatabases
WHERE NAME <> 'tempdb'
ORDER BY NAME ASC
WHILE #DbName IS NOT NULL
BEGIN
SET #DbName = (
SELECT MIN(DbName)
FROM #Database
WHERE DbName > #DbName
)
/*
PUT CODE HERE
EXAMPLE PRINT Database Name
*/
PRINT #DbName
END
To create a list of users that match certain conditions you can modify this script.
Related
My application runs over several databases, and it needs to be able to check from one to see if a column exists in the other. Unfortunately, I won't know the name of the second database until runtime, so it needs to be dynamic. Also, it has to do this in multiple places, so ideally I'd like to make it into a function, but this gives me problems because functions won't run dynamic SQL.
This is the (non-working) function I wrote.....
CREATE FUNCTION [dbo].[fn_checkcolexists] (
#dbname VARCHAR(100)
,#tablename VARCHAR(100)
,#colname VARCHAR(100)
)
RETURNS BIT
AS
BEGIN
DECLARE #sqlstring NVARCHAR(2000)
SET #sqlstring = 'select #retVal = 1 from ' + #dbname + '.sys.columns cols inner join yodata_dev_load.sys.tables tabs
on cols.object_ID=tabs.object_ID where cols.name=''' + #colname + ''' and tabs.name=''' + #tablename + ''''
DECLARE #retVal INT
EXEC sp_executesql #sqlstring
,N'#retVal int output'
,#retVal OUTPUT
RETURN #retval
END
Has anyone got any suggestions how I can accomplish this? I can't find a way to access the column information for every database. Does this information exist in the system databases anywhere?
Alternatively, can I create some sort of synonym for the other database?
Edit: How to find column names for all tables in all databases in SQL Server isn't an ideal solution, because it also relies on dynamic SQL, so I couldn't use this as a function
Use stored procedure and use one of these
One of the methods is to use undocumented
EXEC sp_msforeachdb 'SELECT table_catalog FROM ?.INFORMATION_SCHEMA.COLUMNS
where table_name=''your_table'' and column_name=''your_column_name'''
or simulate it
declare #sql varchar(max), #table_name varchar(100)
select #sql='', #table_name='your_table'
select #sql=#sql+ 'SELECT table_catalog
FROM '+name+'.INFORMATION_SCHEMA.TABLES
where table_name='''+#table_name+''' and
column_name=''your_column_name''' from sys.databases
exec(#sql)
I think I've got the solution I was after. I am using COL_LENGTH, which seems to do the job. You can specify a dbname to is, and even pass that as a parameter, and it returns a null if the column does not exist.
eg
declare #dbname varchar(200)='dbname'
select COL_LENGTH(#dbname + '.dbo.tablename','columnname')
if this returns a null, the column doesn't exist
Many thanks for all the contributors to this thread
Hope this works for you
CREATE FUNCTION [dbo].[fn_checkcolexists]
(
#dbname VARCHAR(100)
,#tablename VARCHAR(100)
,#colname VARCHAR(100)
)
RETURNS INT
AS
BEGIN
DECLARE #RECCOUNT INT = 0
SELECT #RECCOUNT = COUNT(*) FROM information_schema.columns WHERE TABLE_CATALOG = #dbname AND COLUMN_NAME = #colname AND TABLE_NAME = #tablename
RETURN #RECCOUNT
END
GO
I need to do a "log" with all the information of a certain table, of all databases, inside a new table that I will create (With the same structure).
But not all databases has this table.
I could make a query to find all databases that has this table I want:
SELECT name
FROM sys.databases
WHERE CASE
WHEN state_desc = 'ONLINE'
THEN OBJECT_ID(QUOTENAME(name) + '.[dbo].[tblLogdiscador]', 'U')
END IS NOT NULL
It will only list the databases with this table I want to log. But now I need to do a loop, to pass by all databases, inserting the information of the "tbllogdiscador" into the table I created. I was thinking in SP_MSFOREACHDB but I see a lot of people saying to not use it.
How can I loop trough all databases that has the table, and if it has, insert into the new log table??
The code below is not helping me:
exec sp_msforeachdb 'if ((select count(*)
from [?].sys.tables Where name in(''tbllogdiscador''))=1)
begin
insert into [The new tbl log]
select * from ?.dbo.tbllog
end
I'm trying to use a cursor, but i'm having problems.
Any Ideas how to do this with WHILE?
To do what you are thinking, you need some kind of process flow logic (A cursor seems to be the most fitting choice), and dynamic sql.
So the high level is we need to get all of the DB names, put them into the cursor, and then use the cursor to execute the dynamic sql statement where you test to see of the table exists, and pull the records if so.
Ok so here is an example cursor that loops through the DBs on a server looking for a particular table name, and if it exists, does something (you'll have to do the sql for #Sql2):
declare #DBName varchar(256)
declare #SQL1 nvarchar(max)
declare #Sql2 nvarchar(max)
DECLARE db_cursor CURSOR FOR
SELECT name
FROM sys.databases
WHERE state_desc = 'ONLINE'
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #DBName
WHILE ##FETCH_STATUS = 0
Begin
set #SQL1 = 'Select Name from ' + #DBName + '.sys.objects where name = ''tblLogdiscador'' '
set #SQL2 = --Your select and insert statement selecting from #DBName + 'dbo.tbllogdiscador'
if exists(exec sp_executesql #Sql1)
begin
exec sp_executesql #sql2
end
FETCH NEXT FROM db_cursor INTO #DBName
end
close db_cursor
deallocate db_cursor
I have 3000+ tables in my SQL 2008 database with names like listed below, that all starts with tempBinary_, that I need to delete programmatically, how do I do that?
I don't know if I prefer the solution in a SQL-script or with use of LINQtoSQL, i guess both are fine.
tempBinary_002c90322f4e492795a0b8a14e2f7c99
tempBinary_0039f7db05a9456f96eb3cd6a788225a
tempBinary_0057da9ef0d84017b3d0bbcbfb934fb2
I've used Like before on columns, but I don't know if it good for table names too.
Maybe something like this, where LIKE is used, can do it? I don't know.
Use [dbo].[database_name]
DROP TABLE table_name
WHERE table_name LIKE 'tempBinary_%'
Any ideas?
declare #stmt varchar(max) = ''
declare #tbl_name varchar(255)
DECLARE tbl_cursor CURSOR FORWARD_ONLY READ_ONLY
FOR select name
from sysobjects
where xtype='u' and name like 'tempBinary%'
OPEN tbl_cursor
FETCH NEXT FROM tbl_cursor
INTO #tbl_name;
WHILE ##FETCH_STATUS = 0
BEGIN
set #stmt = #stmt + 'drop table ' + #tbl_name + ';' + CHAR(13)
FETCH NEXT FROM tbl_cursor
INTO #tbl_name
end
CLOSE tbl_cursor;
DEALLOCATE tbl_cursor;
execute sp_sqlexec #stmt
So, I have a database with around 100 audit tables and I want to empty them out preferably in 1 sql query. The database has 2 sets of tables [audit] and [dbo]. I'm not even sure what I should be calling these groups of tables so finding any kind of results from google is proving difficult.
Any suggestions?
You can find all tables with a certain schema name like:
select name from sys.tables where schema_name(schema_id) = 'audit'
With a cursor, you iterate over those tables, and empty them using TRUNCATE TABLE:
use db
declare #query nvarchar(max)
declare #tablename nvarchar(max)
declare #curs cursor
set #curs = cursor for select name from sys.tables
where schema_name(schema_id) = 'audit'
open #curs
fetch next from #curs into #tablename
while ##FETCH_STATUS = 0
begin
set #query = N'truncate table audit.' + #tablename
exec sp_executesql #query
fetch next from #curs into #tablename
end
close #curs
deallocate #curs
If you want to delete the tables instead, use:
set #query = N'drop table audit.' + #tablename
You could also use the sp_msforeachtable stored procedure.
It lets you perform a query on each user table in the current DB.
For example the following will truncate all user tables in your DB
Use YourDB
Exec sp_msforeachtable 'TRUNCATE TABLE ?'
And this will truncate all user tables in the specified db that belong to the audit schema.
Use YourDB
Exec sp_msforeachtable #command1 = '
if (Select Object_Schema_name(object_id(''?''))) = ''dbo''
Begin
TRUNCATE TABLE ?
print ''truncated '' + ''?''
End
'
Also Here is a blog entry with some more uses for this stored procedure.
As a part of a scripted procedure I'm trying to programmatically update references to linked servers in stored procs. We have several references like this:-
SELECT foo, bar
FROM [Server].[Database].dbo.[Table]
Which I would like to translate to:-
SELECT foo, bar
FROM [Database].dbo.[Table]
I would like to do this entirely programmatically in a 'fire and forget' script across several databases.
The idea I have right now is to use metadata to find references to linked tables, read the text of each sp from metadata again, adjust each sp's text, then shove each block of updated text into an exec statement to rebuild 'em one-by-one.
I do wonder whether this will be a humongous pain however, so does anybody have any better ideas? I am open to using powershell if that could provide a better solution.
Thanks in advance!
Hopefully I am understanding the questions, but rather than removing or replacing [Server], I suggest one of two approaches:
Option 1: Don't change any of the
SPs. Instead, update the linked
server configuration to point a
different database, even the local
box.
Option 2: Don't change any of the
SPs. Instead, start using SQL Server
Aliases. SQL Server Aliases are
managed via the CliConfig utility and
are ultimately stored in the
registry. Thus, they can be applied
manually or via .reg script.
Basically, the SQL Server Alias
deciphers the server (along with
port) which is being referenced. If
you update the link server
configuration to reference the SQL
Server Alias rather than a specific
server, you can point your procedures
to different server (even the local server) whenever you
would like.
I hope it helps.
Your approach is the easiest, frankly. I had a similar issue earlier this year
Read sys.sql_modules
REPLACE the linked server text and CREATE -> ALTER
EXEC (#Result)
Here's a script to find all procs/functions/views that reference linked servers on a SQL 2005 instance - might be useful too:
USE master
GO
SET NOCOUNT ON;
--------------------------------------------------------------------
-- Test linked server connections
--------------------------------------------------------------------
BEGIN TRY DROP TABLE #Svrs; END TRY BEGIN CATCH END CATCH;
CREATE TABLE #Svrs
(
[Server] nvarchar(max),
[CanConnectAsDefault] bit
);
DECLARE #ServerName nvarchar(max), #RetVal int;
DECLARE Svrs CURSOR FAST_FORWARD READ_ONLY
FOR
SELECT ServerName = S.name
FROM sys.servers S;
OPEN Svrs;
FETCH NEXT FROM Svrs INTO #ServerName;
WHILE ##FETCH_STATUS = 0
BEGIN
BEGIN TRY
EXEC #RetVal = sys.sp_testlinkedserver #ServerName;
END TRY
BEGIN CATCH
SET #RetVal = sign(##error);
END CATCH;
INSERT INTO #Svrs
SELECT #ServerName
, CASE WHEN #RetVal = 0 THEN 1 ELSE 0 END;
FETCH NEXT FROM Svrs INTO #ServerName;
END;
CLOSE Svrs;
DEALLOCATE Svrs;
SELECT * FROM #Svrs
DROP TABLE #Svrs;
GO
--------------------------------------------------------------------
-- Report linked server references
--------------------------------------------------------------------
BEGIN TRY DROP TABLE #Refs; END TRY BEGIN CATCH END CATCH;
CREATE TABLE #Refs
(
[Server] nvarchar(max),
[Database] nvarchar(max),
[Schema] nvarchar(max),
[Object] nvarchar(max),
[Type] nvarchar(max)
);
DECLARE #DatabaseName nvarchar(max), #ServerName nvarchar(max), #SQL nvarchar(max);
DECLARE Refs CURSOR FAST_FORWARD READ_ONLY
FOR
SELECT DatabaseName = D.name
, ServerName = S.name
-- , ServerProvider = S.provider
-- , ServerSource = S.data_source
FROM sys.databases D
CROSS JOIN sys.servers S
WHERE D.name NOT IN ('master', 'tempdb', 'model', 'msdb', 'ReportServer', 'ReportServerTempDB');
OPEN Refs;
FETCH NEXT FROM Refs INTO #DatabaseName, #ServerName;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = 'USE [' + #DatabaseName + '];
INSERT INTO #Refs
SELECT DISTINCT ''' + #ServerName + ''', ''' + #DatabaseName + ''', S.[name], O.[name], O.type_desc
FROM syscomments C
INNER JOIN sys.objects O ON C.id = O.[object_id]
LEFT JOIN sys.schemas S ON S.[schema_id] = O.[schema_id]
WHERE C.[TEXT] LIKE ''%[ ,~[( '''']' + #ServerName + '[ .,~])'''' ]%'' ESCAPE ''~'';'
PRINT 'Looking for ' + #ServerName + ' refs in ' + #DatabaseName -- + ': ' + #SQL;
EXEC sp_executesql #SQL;
FETCH NEXT FROM Refs INTO #DatabaseName, #ServerName;
END
CLOSE Refs;
DEALLOCATE Refs;
SELECT * FROM #Refs
DROP TABLE #Refs;
GO
--------------------------------------------------------------------
SET NOCOUNT OFF;
GO
This is not going to be a good idea for a production environment, but if you need a loopback linked server for dev purposes this worked for me:
EXEC sp_addlinkedserver #server = N'name_for_linked_server',
#srvproduct = N' ',
#provider = N'SQLNCLI',
#datasrc = N'name_of_my_sqlserver_instance',
#catalog = N'name_of_database'