Refactor rename field in many database tables - sql-server

I am faced with renaming a field where the same fieldname is replicated across many tables.
Looking for a script or free/low cost tool which will go through all the tables and if fieldA exists, rename it to fieldB.
thanks

You can use SQL Server metadata tables to create dynamic sql statements for your purpose.
For list of available tables you can sys.tables for list of tables and sys.columns for list of columns. using these tables you can create a table of sql statements. For executing dynamic sqls you need to sp_executesql stored procedure.
This is a sample code just to show how to use metadata tables and sp_executesql:
And note that I used other metadata tables which I am more comfortable with. also you may use a cursor to run all the scripts returned by query.
CREATE Database TestDB
CREATE Table Table1 (Id int , fieldA varchar(50) )
Declare #update_query nvarchar(max)
select
#update_query = 'sp_rename ''' + t.TABLE_NAME + '.' + c.COLUMN_NAME + ''',''' + 'fieldB' + ''''
From INFORMATION_SCHEMA.COLUMNS c JOIN INFORMATION_SCHEMA.Tables t ON c.TABLE_CATALOG = t.TABLE_CATALOG AND c.TABLE_SCHEMA = t.TABLE_SCHEMA AND c.TABLE_NAME = t.TABLE_NAME
WHERE
c.COLUMN_NAME = 'fieldA'
SELECT #update_query
EXEC sp_executesql #update_query

Take a look at the Database project type in VS2010, if you haven't already. It has a lot of features that make DB refactoring easier than working SQL Server Management Studio.
For example if you rename a column, it will give you build errors for all the FKs that reference the old column name. And it does a lot of build-time validation to make sure your database objects don't reference objects which no longer exist. And because all of the database objects are just kept as text files, actions like rename are pretty much just search/replace.
Also, it has very handy "sync" feature which compares the DB project scripts & databases, generates a DIFF report, and generates the scripts to move selected changes between the two (either DB project to SQL Server, or vice versa).
Having said all that, it won't automatically do the renames for you -- in other words, when you rename a column it won't fix up all references to that column throughout the project. But if you make a mistake you will get build errors when it validates the database structure. So at least makes it easy to find the places that you need to change.

If you're using Azure SQL Server, you can use Sam's answer with another input parameter to sp_rename, #objtype = 'COLUMN'.
Declare #update_query nvarchar(max)
select
#update_query = 'sp_rename ''' + t.TABLE_NAME + '.' + c.COLUMN_NAME + ''',''' + 'fieldB' + ''',''' + 'COLUMN' + ''''
From INFORMATION_SCHEMA.COLUMNS c JOIN INFORMATION_SCHEMA.Tables t ON c.TABLE_CATALOG = t.TABLE_CATALOG AND c.TABLE_SCHEMA = t.TABLE_SCHEMA AND c.TABLE_NAME = t.TABLE_NAME
WHERE
c.COLUMN_NAME = 'fieldA'
SELECT #update_query
EXEC sp_executesql #update_query

Related

Get value from multiple tables in all databases on MSSQL server

Given a Database server on which I only have read access to the Master DB, I need to scan all databases on the server for tables that contain "SMTP Mail Setup" in their name. I also need to know the value of the field "SMTP Server" within each of those tables.
I've been able to cobble together the following which lists the Database and Table names where the data I need is stored.
EXEC sp_MSforeachdb 'USE [?] SELECT TABLE_CATALOG as DB_Name, Table_Name FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE ''%SMTP Mail Setup%'''
I'm stuck now as I can't seem to figure out how to pull the field "SMTP Server" from the given tables. Is there a better way to approach this?
You will need to generate and execute dynamic SQL based on the results of the first query.
Try the following (somewhat over-engineered) code:
DECLARE #TableNamePattern sysname = '%SMTP Mail Setup%'
DECLARE #ColumnName sysname = 'SMTP Server'
IF OBJECT_ID('TempDb..#SelectedTables') IS NOT NULL DROP TABLE #SelectedTables
CREATE TABLE #SelectedTables (DB_Name sysname, Table_Name sysname)
DECLARE #SqlTemplate1 VARCHAR(MAX) = '
USE [?]
INSERT #SelectedTables
SELECT T.TABLE_CATALOG as DB_Name, T.TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES T
JOIN INFORMATION_SCHEMA.COLUMNS C
ON C.TABLE_CATALOG = T.TABLE_CATALOG
AND C.TABLE_SCHEMA = T.TABLE_SCHEMA
AND C.TABLE_NAME = T.TABLE_NAME
WHERE T.TABLE_TYPE = ''BASE TABLE''
AND T.TABLE_NAME LIKE <TableNamePattern>
AND C.COLUMN_NAME = <ColumnName>
'
DECLARE #Sql1 VARCHAR(MAX) =
REPLACE(REPLACE(
#SqlTemplate1
, '<TableNamePattern>', QUOTENAME(#TableNamePattern, ''''))
, '<ColumnName>', QUOTENAME(#ColumnName, ''''))
EXEC sp_MSforeachdb #Sql1
SELECT * FROM #SelectedTables ORDER BY DB_Name, Table_Name
DECLARE #SqlTemplate2 VARCHAR(MAX) = 'UNION ALL
SELECT <DB_NAME_Text> AS DB_NAME, <Table_Name_Text> AS Table_Name, <Column_Name>
FROM <DB_NAME>..<Table_Name>
'
DECLARE #Sql2 VARCHAR(MAX) = STUFF((
SELECT REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
#SqlTemplate2
, '<DB_NAME_Text>', QUOTENAME(T.DB_NAME, ''''))
, '<Table_Name_Text>', QUOTENAME(T.Table_Name, ''''))
, '<DB_NAME>', QUOTENAME(T.DB_NAME))
, '<Table_Name>', QUOTENAME(T.Table_Name))
, '<Column_Name>', QUOTENAME(#ColumnName))
FROM #SelectedTables T
ORDER BY T.DB_NAME, T.Table_Name
FOR XML PATH(''),TYPE
).value('text()[1]','nvarchar(max)')
, 1, 9, '') -- Remove initial UNION ALL
SET #Sql2 = #Sql2 + '
ORDER BY 1, 2, 3' -- Lazy way of referencing columns
PRINT #Sql2 -- Might be truncated
EXEC (#Sql2)
DROP TABLE #SelectedTables
I added checks to ensure that the column is defined in the selected table and that the table is a true table ('BASE TABLE') and not a view. The sql templates are run through a series a replace functions that insert the properly quoted and escaped object names. The first template is an expanded version of your original executed sql. The second is used to generate a series or selects for each table.
The FOR XML PATH(''),TYPE concatenates all of the generated selects into a single XML string, and the .value() at the end reliably extracts that text avoiding any XML encoding artifacts. Newer SQL Server versions support a STRING_AGG() function that can be used instead, but the code I had on hand was already using FOR XML.
Each query starts with UNION ALL so that all results display in a combined grid. The STUFF(..., 1, 11, '') strips off the leading UNION ALL.
Finally the resulting SQL is printed and executed.

Get all the table names that are available in all the schemas

I do not have names of the schema that are available. According to my finding all the queries that i am using are specific to a schema name. I need name of all the tables irrespective of the schema name.
Can anyone help me with that.
I have used the following queries:
SELECT sch.name AS SchemaName , tbl.name AS TableName FROM sys.tables tbl INNER JOIN sys.schemas sch ON tbl.SCHEMA_ID = sch.SCHEMA_ID ORDER BY tbl.name;
select * from information.schema.tables where TABLE_TYPE='BASE TABLE';
select * from sys.tables;
select * from sysobjects where xtype='U';
All of these queries are schema specific. If we want to switch between schema we have to use USE %SCHEMA_NAME%. but i need to get all the table names irrespective of the schema name.
Apologies, but i meant to say databases
A database and a schema, in SQL Server, are very different objects. A schema is an object within a database, where as a database is on "object" in an instance. The sys and INFORMATION_SCHEMA objects are also database objects and so will only list the objects within the database context they are referenced in.
For example the below 2 examples would both return details of tables in the MyDB database:
USE master;
SELECT *
FROM MyDB.sys.tables;
GO
USE MyDB;
SELECT *
FROM sys.tables;
If you want to get the details of tables in every Database you'll either need to query them separately, or generate and run a dynamic statement. The dynamic approach would be something like this:
USE master;
GO
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
DECLARE #Delimiter nvarchar(30) = #CRLF + N'UNION ALL' + #CRLF;
SET #SQL = (SELECT STRING_AGG(N'SELECT N' + QUOTENAME(d.[name],'''') + N' AS DatabaseName, t.[name] AS TableName FROM ' + QUOTENAME(d.[name]) + N'.sys.tables t', #Delimiter) WITHIN GROUP (ORDER BY d.database_id)
FROM sys.databases d
WHERE d.database_id > 4) + N';' --skip system databases
--PRINT #SQL; --Your best friend
EXEC sys.sp_executesql #SQL;
If you're using an older version of SQL Server, you'll need to use the "old" FOR XML PATH method, rather than STRING_AGG.

exec sp_tables_ex returns nothing on a server I can find using exec sp_linkedservers

I'm trying to find data in a database which hasn't been used that much.
I've seen that it is a linked server using:
exec sp_linkedservers
which returns the servername, TheSRV along with for instance its catalog name S100.
I then try to find information about which tables are present in the linked server, TheSRV.
For this I try to use:
exec sp_tables_ex TheSRV
but this returns only the headers, without any data.
I can do a Query using openquery like this:
Select name, date From OPENQUERY(TheSRV, 'Select Name, Date from S100.F1.TableName')
which returns valid data.
How can I find the information I need about tables present, when I can't find a list of tables?
You should be able to use one of the standard ways of listing schema objects, qualifying server name as part of the four part naming convention:
SELECT *
FROM TheSRV.S100.INFORMATION_SCHEMA.TABLES T
WHERE T.TABLE_TYPE = 'BASE TABLE'
To see the columns in each of those tables:
SELECT C.*
FROM TheSRV.S100.INFORMATION_SCHEMA.TABLES T
INNER JOIN TheSRV.S100.INFORMATION_SCHEMA.COLUMNS C
ON T.TABLE_NAME = C.TABLE_NAME
AND T.TABLE_SCHEMA = C.TABLE_SCHEMA
WHERE T.TABLE_TYPE = 'BASE TABLE'
ORDER BY C.TABLE_NAME, C.ORDINAL_POSITION

Get the table name using schema

I have seen some query like this in a stored procedure
SELECT ORDINAL_POSITION 'column_id', column_name 'column_name',
column_name + ' : ' + data_type 'column_nametype',
data_type 'data_type',
CHARacter_maximum_length 'SIZE'
FROM information_schema.columns
WHERE table_name = #TableName
but i found that information_schema.columns is not a table name and it is a schema, but how it is retrieving the data?? and how can i know the actual table name?What is the use of a schema??
The INFORMATION_SCHEMA views reside in the Master database on the server and are a special type of view that will retrieve data from your database context. That is, although they are in the master database they will retrieve information for whatever database you are currently using.
Just like any other view you can see the source for these views. In the particular example given it uses sys.columns, sys.objects and sys.types which are themselves system views.
Although views such as sys.types is a view to which the source code is available some of the system tables/views within it are not available for direct reference. For example sys.sysscalartypes is not available to the user and neither is the system function sysconv.
The user of INFORMATION_SCHEMA views is not limited to SQL Server although the implementations appear to be platform specific. For example MySQL has its own INFORMATION_SCHEMA structures.
The use of the old sysobjects (as opposed to sys.objects) system tables is discouraged because there is no guarantee their use will continue in the future. It has to be said that INFORMATION_SCHEMA although generally useful doesn't always expose the level of information a DBA would require.
Try this query:
DECLARE #TableName NVARCHAR(MAX)
SET #TableName = 'tblTest'
SELECT
column_id 'column_id', columns.name 'column_name'
,columns.name + ' : ' + types.name 'column_nametype'
,types.name 'data_type'
,columns.max_length 'SIZE'
FROM
sys.columns
INNER JOIN
sys.types
ON
types.system_type_id = columns.system_type_id
WHERE
objecT_id = OBJECT_ID(#TableName)
ORDER BY
Column_id

In sql server, is there any way to check whether the schema change will impact on the stored procs?

In SQL Server, is there any way to check whether the changes in the schema will impact Stored Procedures (and/or Views)?
For example a change of the column name in one table, may break some Stored Procedures; how to check the impacted stored procs?
try using:
EXEC sp_depends 'YourTableName'
and/or
DECLARE #Search nvarchar(500)
SET #Search='YourTableName' --or anything else
SELECT DISTINCT
LEFT(o.name, 100) AS Object_Name,o.type_desc
FROM sys.sql_modules m
INNER JOIN sys.objects o ON m.object_id=o.object_id
WHERE m.definition Like '%'+#Search+'%'
ORDER BY 2,1
Use Visual Studio Database Edition for your T-SQL development. It will catch such problems during build, as it creates the deployment .dbschema file.
In SSMS (SQL Server Management Studio) right click on the object you are changing and click on View Dependencies. I don't think this will find references from another database.
You can also look for references in stored procedures if they are not encrypted. You would have to do this in each database you suspect might reference the object you are changing.
select objects.name
,sql_modules.definition
from sys.sql_modules sql_modules
join sys.objects objects on sql_modules.object_id = objects.object_id
where definition like '%some column name%';
I have found nothing that is 100.0000% accurate 100.000000% of the time.
Best way I can think to do this is to abstract your stored procedures from your actual tables using views, and to create those views with a "WITH SCHEMABINDING" clause which should prevent changes that will break your views...
Commercial tools such as Red Gate's SQL Refactor can do this.
I think that recent version of Visual Studio also include this kind of features, but I haven't tried.
To my knowledge, there are no built-in features of Microsoft SQL Server per-se which will do this. Correction: I just read about sp_depends in KM's answer to this post... Note that sp_depends's usage is deprecated; it is replaced by sys.dm_sql_referencing_entities and sys.dm_sql_referenced_entities
Also, if the underlying stored procedures use dynamic SQL, the task of detecting dependencies becomes more difficult and prone to "misses".
If you want to change the name of an object or column, then the Smart Rename feature of Red Gate Software's SQL Prompt 5 will generate a script that both performs the rename and updates references to the old name in other objects.
If you're just interested in what depends on a column name, then SQL Prompt 5 also has a Column Dependencies function, where hovering over the column name in a script pops up a window containing a list of objects that refer to the column.
You can download a 14-day trial for free, to see if either of these features works for you.
Paul Stephenson
SQL Prompt Project Manager
Red Gate Software
Have a look at these answers:
Refreshing metadata on user functions t-SQL
SQL Server relationships buried in stored procedures rather than schema
In SQL Server, how can I find everywhere a column is referenced?
How do I find all stored procedures that insert, update, or delete records?
Other than dynamic SQL, using SCHEMABINDING where possible and sp_refreshsqlmodule and sql_dependencies for everything else is very accurate.
If you use SQL Server
You can use this query after your change and find Stored Procedure Or View Or ...
that after your change might get error
USE <Your_DataBase_Name>;
SET NOCOUNT ON;
DECLARE #name NVARCHAR(MAX)
DECLARE #sql NVARCHAR(MAX)
DECLARE #type CHAR(2)
DECLARE #type_desc NVARCHAR(60)
DECLARE #params NVARCHAR(MAX)
DECLARE #tblInvalid TABLE
(
[type_desc] NVARCHAR(60) ,
[name] NVARCHAR(MAX) ,
[error_number] INT ,
[error_message] NVARCHAR(MAX) ,
[type] CHAR(2)
);
DECLARE testSPs CURSOR FAST_FORWARD
FOR
SELECT [name] = OBJECT_NAME(SM.[object_id]) ,
[type] = SO.[type] ,
SO.[type_desc] ,
[params] = ( SELECT (
SELECT CONVERT(XML, ( SELECT STUFF(( SELECT
', ' + [name]
+ '=NULL' AS [text()]
FROM
sys.parameters
WHERE
[object_id] = SM.[object_id]
FOR
XML
PATH('')
), 1, 1, '')
))
FOR XML RAW ,
TYPE
).value('/row[1]', 'varchar(max)')
)
FROM sys.sql_modules SM
JOIN sys.objects SO ON SO.[object_id] = SM.[object_id]
WHERE SO.[is_ms_shipped] = 0
AND SO.[type] = 'P'
OPEN testSPs
FETCH NEXT FROM testSPs INTO #name, #type, #type_desc, #params
WHILE ( ##FETCH_STATUS = 0 )
BEGIN
BEGIN TRY
SET #sql = 'SET FMTONLY ON; exec ' + #name + ' ' + #params
+ '; SET FMTONLY OFF;'
--PRINT #sql;
EXEC (#sql);
END TRY
BEGIN CATCH
PRINT #type_desc + ', ' + #name + ', Error: '
+ CAST(ERROR_NUMBER() AS VARCHAR) + ', ' + ERROR_MESSAGE();
INSERT INTO #tblInvalid
SELECT #type_desc ,
#name ,
ERROR_NUMBER() ,
ERROR_MESSAGE() ,
#type;
END CATCH
FETCH NEXT FROM testSPs INTO #name, #type, #type_desc, #params
END
CLOSE testSPs
DEALLOCATE testSPs
SELECT [type_desc] ,
[name] ,
[error_number] ,
[error_message]
FROM #tblInvalid
ORDER BY CHARINDEX([type], ' U V PK UQ F TR FN TF P SQ ') ,
[name];

Resources