Undocumented System Procedure 'sp_MSforeachtable' and the #whereand parameter - sql-server

I'm attempting to use the undocumented system procedure sp_MSforeachtable. But I need to restrict the affected tables to those that start with "smp" and that are in the "dbo" schema. I was able to find how to find procedures that start with "smp". I simply do:
sp_MSforeachtable #command1=' print ''?''', #whereand=' and name like ''smp%'' '
but how do I filter for a given schema using the #whereand parameter?
UPDATE: I tried the following but it didn't work:
sp_MSforeachtable #command1=' print ''?''', #whereand=' and name like ''smp%'' and Left(''?'', 5)=''[dbo]'' '
Update 2: I'm running on SQL Server 2000.

Update for SQL2000:
declare #s nvarchar(1000)
set #s = ' and uid = ' + convert(nvarchar, user_id('my_schema'))
exec sp_msforeachtable #command1='print ''?''', #whereand = #s

This should works in SQL Server 2000 (can't test now):
#whereand = '
AND name like ''smp%'' AND
OBJECTPROPERTY(OBJECT_ID(''name''), ''OwnerID'') = USER_ID(''dbo'')'
Use OBJECTPROPERTY to find the schema owner id.
Edit: OK, tested it on a SQL 2000 box:
#whereand = ' AND name LIKE ''smp%'' AND uid = 1'
OR
#whereand = ' AND name LIKE ''smp%'' AND USER_ID(''dbo'')'
I could not get OBJECTPROPERTY to work

From here:
---------------------
--Drop table of particular shcemaID/shemaName and with name starting with 'Temp_'
Exec sp_MSforeachtable #command1 = "DROP TABLE ? PRINT '? dropped'"
,#whereand = "and uid = (SELECT schema_id FROM sys.schemas WHERE name = 'dbo')
and o.name LIKE 'Temp_%'"
---------------------

This verion works in Sql Server 2005:
exec sp_MSforeachtable
#command1=' print ''?''',
#whereand=' and schema_name(schema_id) = ''dbo'' '
Not exactly sure for Sql Server 2000, but this version might work:
exec sp_MSforeachtable
#command1=' print ''?''',
#whereand=' and user_name(uid) = ''dbo'' '

This worked in 2008 R2
#whereand='and uid = (SELECT schema_id FROM sys.schemas WHERE name = ''dbo'') and o.name LIKE ''TEMP_%'''

Related

SQL Server 2016 Change Object Owner

I've inherited a SQL 2008 dbase in which all of its objects are prefixed with the name of the developer as owner, i.e. ownername.sp_get_all_users.
I've restored the dbase onto SQL Server 2016 Express Edition.
There are several hundred dbase objects, is there a way to automate changing the object owners to dbo rather than manually editing each object?
I've tried the following but apparently you can no longer make ad-hoc changes to objects since SQL Server 2005?
SELECT * from sysobjects where uid = user_id('UseNAme')
declare #Return int
exec #Return = sp_configure 'allow updates', '1'
SELECT #Return as 'Returned Code'
GO
reconfigure WITH OVERRIDE
GO
DECLARE #Rows int, #Error int
BEGIN TRANSACTION
update sysobjects set uid = user_id('dbo') where uid = user_id('UseNAme')
SELECT #Error = ##Error, #Rows = ##RowCount
SELECT #Rows as '#Rows'
IF #Rows > 0
BEGIN
SELECT #Rows AS '#Rows'
COMMIT TRANSACTION
END
else
BEGIN
SELECT #Error AS 'Error #'
ROLLBACK TRANSACTION
END
exec sp_configure 'allow updates', '0'
reconfigure WITH OVERRIDE
go
Any help most appreciated.
You have to use Alter Schema...
ALTER SCHEMA oldschemaname TRANSFER dbo.Address;
To Automate use below
this will change all tables which have a schema other than system to dbo,note if you have two tables in different schema,they can't exist in same schema
select *,row_number() over (order by (select null)) as rownum
into #tables
from information_Schema.tables
where table_schema in (select name from sys.schemas
where name not in ('dbo','guest','INFORMATION_SCHEMA','sys') and principal_id <16384
)
now move
declare #min int,#max int
select #min=min(rownum),#max=max(rownum)
from #tables
declare #tblname varchar(255),#schemaname sysname
declare #sql varchar(max)
while #min<=#max
Begin
select #tblname=table_name,#schemaname=table_schema from
#tables where rownum=#min
set #sql='alter schema dbo transfer '+ #schemaname+'.'+#tblname
--print #sql
exec(#sql)
Set #min=#min+1
End
sp_change object owner as per documentation states..
This stored procedure only works with the objects available in MicrosoftSQL Server 2000. This feature will be removed in a future version of Microsoft SQL Server. Avoid using this feature in new development work, and plan to modify applications that currently use this feature. Use ALTER SCHEMA or ALTER AUTHORIZATION instead. sp_changeobjectowner changes both the schema and the owner. To preserve compatibility with earlier versions of SQL Server, this stored procedure will only change object owners when both the current owner and the new owner own schemas that have the same name as their database user names.
Use this sp_changeobjectowner
As explained here MSDN
For example: EXEC sp_changeobjectowner 'YourObject', 'dbo'
You can use this to alter schema statement for newer SQL Server DBS
declare #sql varchar(8000), #table varchar(1000), #oldschema varchar(1000), #newschema varchar(1000)
set #oldschema = 'dbo'
set #newschema = 'exe'
while exists(select * from sys.tables where schema_name(schema_id) = #oldschema)
begin
select #table = name from sys.tables
where object_id in(select min(object_id) from sys.tables where schema_name(schema_id) = #oldschema)
set #sql = 'alter schema ' + #newschema + ' transfer ' + #oldschema + '.' + #table
exec(#sql)
end
Your general idea of looping through the objects owned by the developer is a good idea (assuming you've tested the heck out of it). I'd suggest using the ALTER AUTHORIZATION command instead MSDN Doc
In addition to the advice above, the following changes the owner of SPs:
Declare #sql varchar(8000),
#table varchar(1000),
#oldschema varchar(1000),
#newschema varchar(1000)
set #oldschema = 'developername'
set #newschema = 'dbo'
while exists(select * from information_schema.routines where routine_type = 'PROCEDURE' and routine_schema = #oldschema )
begin
select #table = SPECIFIC_NAME from information_schema.routines
where SPECIFIC_NAME in(select SPECIFIC_NAME from information_schema.routines where routine_type = 'PROCEDURE' and routine_schema = #oldschema)
set #sql = 'alter schema ' + #newschema + ' transfer ' + #oldschema + '.' + #table
exec(#sql)
end

SQL Studio Prepared Statement

I am doing work for a company that stores each of their client's info in a different database. When a table needs modification, I have to go to each database and run the ALTER TABLE script. Is there a way I can use a prepared statement to run through all 100+ DBO names?
ALTER TABLE ?.dbo.profileTable
ADD COLUMN profileStatus int
where ? = 'CompanyA, CompanyB, CompanyC' or something similar?
Use Sp_MSforeachdb
EXECUTE master.sys.sp_MSforeachdb 'USE [?]; alter query'
[?] is used as a placeholder for the heretofore unspecified database name
You can modify the query as per your needs ,to exclude system databases use like below..
EXECUTE master.sys.sp_MSforeachdb 'USE [?]; IF DB_ID(''?'') > 4 begin yourquery end'
This will exclude any database that does not have the table you are looking for including system databases.
Declare #TableName Varchar(8000) = 'ProfileTable'
Declare #Sql Varchar(8000)
Select #Sql = Stuff(
(Select ';', 'Alter Table ' + Name + SqlText
From sys.databases
Cross Apply (Select '.dbo.profileTable ADD profileStatus int' SqlText) CA
Where Case When State_Desc = 'ONLINE'
Then Object_Id (QuoteName(Name) + '.[dbo].' + #TableName, 'U')
End Is Not Null
FOR XML PATH('')
),1,1,'')
Exec (#Sql)
This ? before is database ([database].[schema].[table]). Thus you can use sp_MSforeachdb or, as I prefer, use sys.databases view to prepare dynamic queries.
Beware, both methods can interfere with system databases.
Take a look at this solution:
DECLARE #query nvarchar(MAX)='';
SELECT #query = #query + 'USE '+QUOTENAME(name)+';ALTER TABLE dbo.profileTable ADD profileStatus int;'
FROM sys.databases
WHERE OBJECT_ID(QUOTENAME(name)+'.dbo.profileTable', 'U') IS NOT NULL
EXEC(#query)
It adds column col1 int to each dbo.profileTable in every database.

how to Find a patricular table Table on a SQL Server across all Databases?

I want to find a table in SQL Server, let's say "XYZ". i don't know in which DB it is located. The server has many DBs
SELECT * FROM sys.Tables
WHERE
name LIKE '%XYZ%'
You can make use of SP sp_Msforeachdb - this will be executed on every single database you have in the server.
EXEC sp_Msforeachdb "USE [?]; SELECT '[?]' dbname, *
FROM sys.tables
WHERE name like '%XYA%'"
This query will return a listing of all tables in all databases on a SQL instance:
DECLARE #command varchar(1000)
SELECT #command = 'USE ? SELECT name FROM sysobjects WHERE xtype = ''U'' AND Name LIKE ''%A'' ORDER BY name'
EXEC sp_MSforeachdb #command

How to use both sp_msforeachtable and sp_msforeachdb in the same query?

Is there any way to reference the table inside a 'sp_MSforeachtable' loop running inside a 'sp_msforeachdb' loop?
For example, in the following query the '?' is always referencing the database:
DECLARE #cmd VARCHAR(8000);
SET #cmd = 'USE ?; EXEC sp_MSforeachtable #command1="select db_name = DB_NAME(), db_foreach = ''?'', tb_foreach = ''?'' "'
EXEC sp_msforeachdb #command1 =#cmd
Resulting in:
db_name db_forearch tb_foreach
ServerMonitor master master
I want to have something like:
db_name db_forearch tb_foreach
ServerMonitor master <TABLE_NAME>
What should I change?
Solved. I used my ow cursor, as suggested by Sean. But the #replacechar solution suggested by Ben Thul is exactly what I was looking for.
DECLARE #cmd VARCHAR(8000);
SET #cmd = 'USE ^; EXEC sp_MSforeachtable #command1="select db_name = DB_NAME(), db_foreach = ''^'', tb_foreach = ''?'' "'
EXEC sp_msforeachdb #command1 =#cmd, #replacechar = '^'
Take a look at the parameters for sp_msforeachtable. One of them is #replacechar which, by default, is a question mark (i.e. ?). Feel free to pass in another equally unlikely character to occur in a query (maybe a ^).
Of course, I'd be remiss if I didn't mention that depending on what you're trying to do (and I would argue that anything that you're trying to do over all tables is doable this way), there are easier to read (and write) solutions in powershell:
import-module sqlps -disablenamechecking;
$s = new-object microsoft.sqlserver.management.smo.server '.';
foreach ($db in $s.databases) {
foreach ($table in $db.Tables) {
$table | select parent, name; --merely list the table and database
}
}
For what you are doing you could do something like this. Although this is still using the for each db procedure which can be problematic. You will want to add a where clause to the final select statement to filter out some databases (model, tempdb, master, etc)
declare #TableNames table
(
DatabaseName sysname
, TableName sysname
)
insert #TableNames
EXEC sp_msforeachdb #command1 = 'use ?;select ''?'', name from sys.tables'
select *, 'exec ' + Databasename + '..sp_spaceused [''' + TableName + ']'';'
from #TableNames

Renaming multiple tables

In SQL Server, I have a database abc. In this database I have hundreds of tables. Each of these tables is called xyz.table
I want to change all the tables to be called abc.table.
Do we have a way by which I can change all the names from xyz.table to abc.table in database abc?
I am able to manually change the name by changing the schema for each table to abc
You could have a cursor run over all your tables in the xyz schema and move all of those into the abc schema:
DECLARE TableCursor CURSOR FAST_FORWARD
FOR
-- get the table names for all tables in the 'xyz' schema
SELECT t.Name
FROM sys.tables t
WHERE schema_id = SCHEMA_ID('xyz')
DECLARE #TableName sysname
OPEN TableCursor
FETCH NEXT FROM TableCursor INTO #TableName
-- iterate over all tables found
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #Stmt NVARCHAR(999)
-- construct T-SQL statement to move table to 'abc' schema
SET #Stmt = 'ALTER SCHEMA abc TRANSFER xyz.' + #TableName
EXEC (#Stmt)
FETCH NEXT FROM TableCursor INTO #TableName
END
CLOSE TableCursor
DEALLOCATE TableCursor
You can use Alter Schema with an undocumented Stored Procedure exec sp_MSforeachtable which basically iterates through all the tables .
exec sp_MSforeachtable "ALTER SCHEMA new_schema TRANSFER ? PRINT '? modified' "
change the new_schema keyword with your new Schema .
For details please go through the link
sp_MSforeachtable
Alter Schema for all the tables
As others have pointed out that the SP is deprecated so There is another way to do this by getting the names of the table from sys.tables
Declare #value int
Set #value=1
declare #sql varchar(max), #table varchar(50), #old varchar(50), #new varchar(50)
set #old = 'dbo'
set #new = 'abc'
while exists(select * from sys.tables where schema_name(schema_id) = #old)
begin
;With CTE as
(
Select *,row_number() over(order by object_id) rowNumber from sys.tables
where schema_name(schema_id) = #old
)
select #table= name from CTE where #value=rowNumber
Set #value=#value+1
set #sql = 'alter schema ' + #new + ' transfer ' + #old + '.' + #table
exec(#sql)
end
I'm assuming You've already created the schema abc in the database.
If not you can refer here
http://www.youtube.com/watch?v=_DDgv8uek6M
http://www.quackit.com/sql_server/sql_server_2008/tutorial/sql_server_database_schemas.cfm
To change the schema of all the tables in database you can use following system created msforeachtable stored procedure to rename schema of each table with alter schema.
exec sp_MSforeachtable "ALTER SCHEMA abc TRANSFER ? PRINT '? modified' "
Without using the undocumented/unsupported sp_MSforeachtable procedure, here's a somewhat concise way to select and/or run all of the necessary ALTER statements for every table on the given schema:
declare #oldSchema nvarchar(50) = 'abc' -- usually 'dbo'
declare #newSchema nvarchar(50) = 'xyz' -- use your new schema name
declare #sql nvarchar(max) =
(select
(select N'alter schema [' + #newSchema + '] transfer [' + #oldSchema + '].[' + name + ']
' as 'data()'
from sys.tables
where schema_name(schema_id) = #oldSchema for xml path(''), type)
.value('text()[1]','nvarchar(max)'))
-- You can select out the results for scrutiny
select #sql
-- Or you can execute the results directly
exec (#sql)
This avoids using a cursor, and uses brackets to escape table names that may conflict with SQL keywords.

Resources