Iterate Through and Rename All Tables w/ Object Qualifiers MSSQL - sql

I need to iterate through all of the tables that begin with a specific prefix to rename them. The code I've tried is below, but it ends with one of two results, either it crashes SSMS (sometimes), or I get the error message below for each table. I've tried with and with out dbo.
Can anyone tell me what I'm doing wrong or perhaps suggest a better way to do this?
No item by the name of 'dbo.prefix_TableName' could be found in the current database 'DatabaseName', given that #itemtype was input as '(null)'.
Here's the code I'm running...
SET NOCOUNT ON;
USE [DatabaseName];
DECLARE #oq NVARCHAR(5), #tableName NVARCHAR(128), #newTableName NVARCHAR(128);
SET #oq = N'prefix_';
/*
find and rename all tables
*/
DECLARE [tableCursor] CURSOR FOR
SELECT [TABLE_NAME] FROM INFORMATION_SCHEMA.TABLES
WHERE [TABLE_TYPE] = 'BASE TABLE' AND [TABLE_NAME] LIKE #oq + '%'
ORDER BY [TABLE_NAME];
OPEN [tableCursor]
FETCH NEXT FROM [tableCursor] INTO #tableName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #newTableName = REPLACE(#tableName, #oq, N'');
EXEC('EXEC sp_rename ''dbo.' + #tableName + ''', ''' + #newTableName + '''');
END
CLOSE [tableCursor];
DEALLOCATE [tableCursor];

A simpler solution without cursors
declare #oq nvarchar(max) = N'prefix_'
declare #cmd nvarchar(max)
select #cmd = a from (
select 'EXEC sp_rename ''' + TABLE_NAME + ''', ''' + REPLACE(TABLE_NAME, #oq, N'') + ''' '
from INFORMATION_SCHEMA.TABLES
for xml path('')
) t(a)
exec sp_executesql #cmd
In your example nvarchar(5) causes truncation, you probably need nvarchar(7) or nvarchar(max).

Related

Stored procedure to drop the column in SQL Server

I created many tables and I have noticed that I have created one useless column in all the tables. I want to create a stored procedure which will drop one specific column and can be useful in all the column.
I created this stored procedure but I'm getting an error. Help me please
You cannot parametrize table and column names with parameters - those are only valid for values - not for object names.
If this is a one-time operation, the simplest option would be to generate the ALTER TABLE ... DROP COLUMN ... statements in SSMS using this code:
SELECT
'ALTER TABLE ' + SCHEMA_NAME(t.schema_id) + '.' + t.Name +
' DROP COLUMN Phone;'
FROM
sys.tables t
and then execute this code in SSMS; the output from it is a list of statement which you can then copy & paste to a new SSMS window and execute.
If you really want to do this as a stored procedure, you can apply the same basic idea - and then just use code (a cursor) to iterate over the commands being generated, and executing them - something like this:
CREATE PROCEDURE dbo.DropColumnFromAllTables (#ColumnName NVARCHAR(100))
AS
BEGIN
DECLARE #SchemaName sysname, #TableName sysname
-- define cursor over all tables which contain this column in question
DECLARE DropCursor CURSOR LOCAL FAST_FORWARD
FOR
SELECT
SchemaName = s.Name,
TableName = t.Name
FROM
sys.tables t
INNER JOIN
sys.schemas s ON t.schema_id = s.schema_id
WHERE
EXISTS (SELECT * FROM sys.columns c
WHERE c.object_id = t.object_id
AND c.Name = #ColumnName);
-- open cursor and start iterating over the tables found
OPEN DropCursor
FETCH NEXT FROM DropCursor INTO #SchemaName, #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #Stmt NVARCHAR(1000)
-- generate the SQL statement
SET #Stmt = N'ALTER TABLE [' + #SchemaName + '].[' + #TableName + '] DROP COLUMN [' + #ColumnName + ']';
-- execute that SQL statement
EXEC sp_executeSql #Stmt
FETCH NEXT FROM DropCursor INTO #SchemaName, #TableName
END
CLOSE DropCursor
DEALLOCATE DropCursor
END
This procedure should work.
It loops through all cols and then deletes the column where sum(col) is zero.
Take a Backup of the Table
alter procedure deletecolumnsifzero #tablename varchar(1000)
as
set nocount on
declare #n int
declare #sql nvarchar(1000)
declare #sum_cols nvarchar(1000)
declare #c_id nvarchar(100)
set #n = 0
declare c1 cursor for
select column_name from information_schema.columns
where
table_name like #tablename
--Cursor Starts
open c1
fetch next from c1
into #c_id
while ##fetch_status = 0
begin
set #sql=''
set #sql='select #sum_cols = sum('+#c_id+') from ['+#tablename+']'
exec sp_Executesql #sql,N'#sum_cols int out,#tablename nvarchar(100)',#sum_cols out,#tablename
if(#sum_cols = 0)
begin
set #n=#n+1
set #sql=''
set #sql= #sql+'alter table ['+#tablename+'] drop column ['+#c_id+']'
exec sp_executesql #sql
end
fetch next from c1
into #c_id
end
close c1
deallocate c1

SQL Server view - bad naming convention?

DECLARE #TableName AS VARCHAR(250);
DECLARE #SQL AS VARCHAR(500);
DECLARE #ViewCheck as CURSOR;
SET #ViewCheck = CURSOR FOR
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'VIEW' AND TABLE_NAME LIKE 'V_WFC%'
OPEN #ViewCheck
FETCH NEXT FROM #ViewCheck INTO #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
Set #SQL = 'SELECT TOP 10 * FROM ' + #TableName
PRINT(#SQL)
EXEC(#SQL)
FETCH NEXT FROM #ViewCheck INTO #TableName;
END
CLOSE #ViewCheck
I have a cursor that runs through all SQL views in a particular schema to sanity check that they continue to function, some are tied to reporting and some used as an application data source in ProSolution.
One of these views is named UnmarkedRegister(Today) the brackets used to differentiate it from a from a similar view, this one is used within an application to drive display data.
While the query runs as expected, returning the correct data - the cursor returns an error
Msg 208, Level 16, State 1, Line 1
Invalid object name 'V_WFC_UnmarkedRegister'
and I'm wondering why the bracketed section is omitted in the EXEC(SQL) section of the cursor?
Use quotename():
Set #SQL = 'SELECT TOP 10 * FROM ' + QUOTENAME(#TableName);
I truly detest cursors and there is no need for a cursor here at all. You can greatly simplify this code in a couple of ways. First I am using sys.views instead of the INFORMATION_SCHEMA views. And then I am using sql to build a dynamic sql string instead of a cursor. Look how simple this can be.
declare #SQL nvarchar(max) = '';
select #SQL = #SQL + 'select top 10 * from ' + QUOTENAME(v.name) + '; select ''('' + convert(varchar(2), ##ROWCOUNT) + '' rows affected'';'
from sys.views v
where v.name LIKE 'V_WFC%'
print #SQL
exec sp_executesql #SQL
Because brackets usually identify a function call
changing this line:
Set #SQL = 'SELECT TOP 10 * FROM ' + #TableName
to this should fix it:
Set #SQL = 'SELECT TOP 10 * FROM [' + #TableName + ']'

Check if View exists before querying it

I want to check if a specific View exists before querying it. I use dynamic SQL to create the query:
DECLARE #sqlCommand varchar(1000)
DECLARE #viewName varchar(1000)
DECLARE #otherDB varchar(1000)
SET #sqlCommand = 'IF EXISTS(SELECT 1 FROM ' + #otherDB + '.sys.views WHERE name=''' + #viewName + ''')
BEGIN
SELECT * FROM ' + #viewName + '
END'
EXEC (#sqlCommand)
So everything works fine as long as #viewName actually exists. However, if #viewName is a View that does not exist in sys.views, I get an error from the compiler:
The OLE DB provider "SQLNCLI11" for linked server "server" does not contain the table #viewName. The table either does not exist or the current user does not have permiossions on that table.
I would have thought that since an IF statement is used, it would just skip the querying of the View. However, seems like the View has to exist otherwise I get the above error.
I've tried alternate solutions, such as using strings for the View names, but no luck. I've also tried the solution in: How to check the existence of a view, but at some point I have to reference the View name in my query, and it would complain
Any info would be greatly appreciated!
Check for the existence of the view outside the dynamic SQL. You are trying to prevent the compile-time error of the view not existing in the select. There is no issue with the if:
IF EXISTS(SELECT 1 FROM sys.views WHERE name = #viewName)
BEGIN
SET #sqlCommand = 'SELECT * FROM ' + #viewName
EXEC(#sqlCommand)
END;
Although it doesn't make a difference in this case, if you are using dynamic SQL, learn about sp_executesql -- it is more powerful than exec() because you can pass variables in and out.
EDIT:
In that case, you essentially have to do dynamic SQL inside dynamic SQL. The following is not tested, so there could be a syntax error:
DECLARE #viewName varchar(1000);
DECLARE #otherDB varchar(1000);
declare #sql nvarchar(max) = '
IF EXISTS (SELECT 1 FROM #otherDB.sys.views WHERE name = ''#viewName'')
BEGIN
DECLARE #sqlCommand nvarchar(max);
SET #sqlCommand = ''SELECT * FROM #viewName'';
EXEC(#sqlCommand);
END;';
SET #sql = replace(replace(#ql, '#otherDB', #otherDB), '#viewName', #viewName);
EXEC(#sql);
What version of SQL Server are you using? I only have SQL Server 2014 available to test with, but the T-SQL below works for both missing and not missing views. I wonder whether the fact that you are checking for existence of the view in otherdb.sys.views but are not qualifying otherdb when selecting from the view is to blame?
declare #viewName varchar(50) = 'MissingView';
declare #sqlCommand nvarchar(1000);
declare #otherdb varchar(20) = 'MyTestDatabase';
set #sqlCommand = N'if exists
(
select 1
from ' + #otherdb + '.sys.views as v
where v.name = ''' + #viewName + '''
)
begin
select * from ' + #otherdb + '.dbo.' + #viewName + ';
end
else
begin
select ''Nah mate - missing view'';
end';
print #sqlCommand;
execute sp_executesql #sqlCommand;
You can use the Else condition when not exists to set error message
DECLARE #sqlCommand varchar(1000)
DECLARE #viewName varchar(1000)
SET #viewName = 'vwName'
SET #sqlCommand = 'IF EXISTS(SELECT 1 FROM sys.views WHERE name=''' + #viewName + ''')
BEGIN
SELECT * FROM ' + #viewName + '
END
ELSE
BEGIN
SELECT ''View not exists''
END
'
EXEC (#sqlCommand)
Beware if your view is in a different schema, because then you need to also check the SCHEMAS table:
SELECT 1 FROM SYS.VIEWS
INNER JOIN SYS.SCHEMAS ON SYS.SCHEMAS.schema_id = SYS.VIEWS.schema_id
WHERE SYS.VIEWS.TYPE='V'
AND SYS.SCHEMAS.NAME=#Your_Schema_Name
AND SYS.VIEWS.NAME=#Your_View_Name

Incorrect syntax SQL

Why do I get this error when I try to execute the following code?
I have a table NewTable1 with two columns: column1 and column2.
I get this error: Incorrect syntax near 'column2'.
--DROP COLUMN PROCEDURE
CREATE PROCEDURE DropColumn
#tableName varchar(50),
#columnName varchar(50)
AS
BEGIN
DECLARE #SQL nvarchar(500);
SET #SQL = N'ALTER TABLE ' + QUOTENAME(#tableName)
+ ' DROP COLUMN ' + QUOTENAME(#columnName);
EXEC sp_executesql #SQL;
END
RETURN 0
GO
USE SKI_SHOP;
EXEC DropColumn 'NewTable1', 'column2';
GO
Use appropriate data types. Also You will only be able to drop Columns for tables in callers default schema. Since procedure doesn't take schema into consideration, therefore you can only pass the table name and if a table exists in other than caller default schema they wont be able to delete it using this procedure .
CREATE PROCEDURE DropColumn
#tableName SYSNAME,
#columnName SYSNAME
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQL NVARCHAR(MAX);
SET #SQL = N' ALTER TABLE ' + QUOTENAME(#tableName)
+ N' DROP COLUMN ' + QUOTENAME(#columnName);
EXEC sp_executesql #SQL;
END
GO
I over looked some basic simple issues in my first approach, whenever creating of Dropping objects in SQL Server always check if they exist, to avoid any errors . A more complete and safe approach would be something like ...
This time I have also added schema as a parameter.
ALTER PROCEDURE DropColumn
#tableName SYSNAME,
#columnName SYSNAME,
#Schema SYSNAME,
#Success BIT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQL NVARCHAR(MAX);
SET #SQL = N' IF EXISTS (SELECT * FROM sys.tables t
INNER JOIN sys.columns c
ON t.[object_id] = c.[object_id]
INNER JOIN sys.schemas sc
ON t.[schema_id] = sc.[schema_id]
WHERE t.name = #tableName
AND c.name = #columnName
AND sc.name = #Schema)
BEGIN
ALTER TABLE ' + QUOTENAME(#Schema)+ '.' + QUOTENAME(#tableName)
+ N' DROP COLUMN ' + QUOTENAME(#columnName)
+ N' SET #Success = 1; '
+ N' END
ELSE
BEGIN
SET #Success = 0;
END '
EXEC sp_executesql #SQL
,N'#tableName SYSNAME, #columnName SYSNAME, #Schema SYSNAME, #Success BIT OUTPUT'
,#tableName
,#columnName
,#Schema
,#Success OUTPUT
END
GO

How to execute store procedure for another DB?

I have a stored procedure that should be able to be executed on any table of any database on my MS Sql Server. Most of the combination of EXEC and USE statements didn't result in anything. Here is the stored procedure:
CREATE PROCEDURE [dbo].[usp_TrimAndLowerCaseVarcharFields]
(
#Database VARCHAR(200),
#TableSchema VARCHAR(200),
#TableName VARCHAR(200)
)
AS
BEGIN
DECLARE #sSql VARCHAR(MAX)
SET #Database = '[' + #Database + ']'
SET #sSql = ''
-- Create first part of a statement to update all columns that have type varchar
SELECT #sSql = #sSql + COLUMN_NAME + ' = LOWER(RTRIM(' + COLUMN_NAME + ')), '
FROM INFORMATION_SCHEMA.COLUMNS
WHERE DATA_TYPE = 'varchar'
AND TABLE_CATALOG = #Database
AND TABLE_SCHEMA = #TableSchema
AND TABLE_NAME = #TableName
SET #sSql = 'UPDATE ' + #Database + '.' + #TableSchema + '.' + #TableName + ' SET ' + #sSql
-- Delete last two symbols (', ')
SET #sSql = LEFT(#sSql, LEN(#sSql) - 1)
EXEC(#sSql)
END
Please, advice what I have to do to execute it on [OtherDB].[TargetTable].
You can fully qualify both tables and stored procedures. In other words you can do this:
UPDATE [OtherDB].[Schema].[targetTable] SET ...
It appears you are doing this in your proc already.
You can also EXEC a stored procedure using the Fully Qualified name - e.g.
EXEC [OtherDB].[dbo].[usp_TrimAndLowerCaseVarcharFields]
Honestly, your proc looks fine, are you receiving any error messages? If so please post them. Also, make sure your user has access to the other DB.
The table name in the query you used is wrong, it is looking up into same database, but you do need to look up from different database. So the query will be as below:
SELECT #sSql = #sSql + COLUMN_NAME + ' = LOWER(RTRIM(' + COLUMN_NAME + ')), '
FROM [TargetDB].INFORMATION_SCHEMA.COLUMNS
WHERE DATA_TYPE = 'varchar'
AND TABLE_CATALOG = #Database
AND TABLE_SCHEMA = #TableSchema
AND TABLE_NAME = #TableName
-- [TargetDB] = #Database
The TargetDB will be same as your passing database (#Database)
If you want to use [TargetDB] dynamically then you need to generate sql(#sSql) and the execute the sql string.
In this case I sugest to use the 2 sp's in SQL Server:
sp_MSforeachtable
sp_MSforeachdb
for more information, please read the article in here.
hope this help.
exec #RETURN_VALUE=sp_MSforeachtable #command1, #replacechar, #command2,
#command3, #whereand, #precommand, #postcommand
exec #RETURN_VALUE = sp_MSforeachdb #command1, #replacechar,
#command2, #command3, #precommand, #postcommand
Complete script:
declare #DatabaseName varchar(max), #DatabaseCharParam nchar(1)
declare #TableSchema varchar(max)
declare #TableName varchar(max), #TableCharParam nchar(1)
set #DatabaseName='DATABASENAME'; set #DatabaseCharParam='?'
set #TableSchema='dbo'
set #TableName='TABLENAME'; set #TableCharParam='$'
-- Exemple Script to execute in each table in each database
-- Create first part of a statement to update all columns that have type varchar
DECLARE #sSql VARCHAR(MAX)
set #sSql=''
SELECT #sSql = isnull(#sSql,'') + COLUMN_NAME + ' = LOWER(RTRIM(' + COLUMN_NAME + ')),'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE DATA_TYPE = 'varchar'
AND TABLE_CATALOG = #DatabaseName
AND TABLE_SCHEMA = #TableSchema
AND TABLE_NAME = #TableName
declare #EachTablecmd1 varchar(2000)
--Prepare #EachTablecmd1 the script to execution in each table using sp_MSforeachtable (ATENTION: #Command1 are limited to varchar(2000) )
--in sp_MSforeachtable #TableCharParam will be subtituted with owner i.e:[dbo].[TABLENAME]
set #sSql='update '+#TableCharParam+' set '+ left(#sSql,LEN(#sSql)-1)
set #EachTablecmd1='if '''''+ #TableCharParam +'''''=''''['+#TableSchema+'].['+#TableName+']'''' '+#sSql
--i.e.: if 'table1'='table1' update table1 set column1=LOWER(RTRIM(column1)),....
-- the #sSql for each table in a database
set #sSql ='exec sp_MSforeachtable #command1='''+#EachTablecmd1+''' ,#replacechar='''+#TableCharParam+''''
declare #EachBDcmd1 varchar(2000)
--Prepare the execution to each database using sp_MSforeachdb (ATENTION: #Command1 are limited to varchar(2000) )
set #EachBDcmd1='if '''+#DatabaseCharParam+'''='''+#DatabaseName+''' '+ #sSql
--print #EachBDcmd1
exec sp_MSforeachdb #command1=#EachBDcmd1,#replacechar=#DatabaseCharParam