Rename a column in a table with SQL Sserver 2008 - sql-server

I have read through all the answers for this question and none (that I have found) answers my question.
Can I rename a column using the column number?
I have a table that may have already had a column name changed, so I may not know what it is to "hard code" it. Can I reference the column by number, something like this...
EXEC sp_rename '[ChkLst].column7', 'NewColName', 'COLUMN'"
Thanks

You can get the current column name from SYS.COLUMNS using the table name and column ID, then execute SP_RENAME as below. Note that the column IDs are NOT guaranteed to be sequential and that you should confirm the ID of the column before attempting to rename it.
DECLARE #ColumnName VARCHAR(50);
SET #ColumnName = 'YourSchema.YourTable.';
SET #ColumnName = CONCAT(#ColumnName,
(SELECT name FROM SYS.COLUMNS WHERE OBJECT_ID = OBJECT_ID(N'YourTable') AND column_id = 7));
EXEC SP_RENAME #ColumnName, 'NewColumnName', 'COLUMN';

If you know column number (ordinal position), you can get column name by running following query:
SELECT COLUMN_NAME FROM Information_Schema.Columns
WHERE TABLE_NAME = '<TableName>' AND ORDINAL_POSITION = <ColumnNumber>
and you can use the column name using following query:
DECLARE #ColName nvarchar(100)
SELECT #ColName = COLUMN_NAME FROM Information_Schema.Columns
WHERE TABLE_NAME = 'TableName' AND ORDINAL_POSITION = 2
SET #ColName = 'TableName.[' + #ColName + ']'
sp_RENAME #ColName , '[NewColumnName]', 'COLUMN'

You can do like
DECLARE #SchemaTable VARCHAR(50),
#NewName VARCHAR(50) = 'MyColumn';
SELECT #SchemaTable =
CONCAT(S.name, '.', T.name, '.', COL_NAME(T.object_id, 2))
FROM Sys.Tables T INNER JOIN Sys.Schemas S
ON T.schema_id = S.schema_id
WHERE T.name = 'YourTableName'
AND
T.type = 'U';
EXEC sp_rename #SchemaTable, #NewName, 'COLUMN';

Related

Update value in all tables that have a column name in SQL Server

I am writing a stored proc that needs to search a database for all tables that have a certain column name. Once I get a list of tables that have that column I need to update a value in that column. So first I get a list of tables that have a certain column.
SELECT c.name AS 'ColumnName'
,t.name AS 'TableName'
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE c.name LIKE 'company'
ORDER BY TableName
Now that I have a list of tables that need to be updated I need to run a query similar to the following to update the data in each table.
update table1 set company = #newValue where company = #oldvalue
I'm not sure how to go about writing this part. My first thought was to write a dynamic update statement inside of a cursor like:
Declare #newValue
Declare #oldValue
SET #companyCursor = CURSOR FOR
SELECT t.name AS 'TableName'
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE c.name LIKE 'company'
OPEN #companyCursor;
FETCH NEXT FROM #companyCursor INTO #tableName;
WHILE ##FETCH_STATUS = 0
BEGIN
update #tableName set company = #newValue where company = #oldValue
FETCH NEXT FROM #companyCursor INTO INTO #tableName;
END
Is this a good strategy?
I really dislike cursors so even in cases like this where a cursor is a viable solution I like to leverage the system views to avoid looping. You still have to use dynamic sql because object names cannot be parameterized.
Please note that I am guessing the datatype for company here and you can change this easily. Make sure you change the variable definition both in your script AND in the dynamic sql. You entire script could be shortened to something like this.
declare #SQL nvarchar(max) = ''
, #newValue varchar(10) = 'new'
, #oldValue varchar(10) = 'old'
select #SQL = #SQL + 'Update ' + quotename(object_name(c.object_id)) + ' set company = #newValue where company = #oldValue;'
from sys.columns c
where c.name = 'company'
select #SQL
--uncomment the line below when you are satisfied the dynamic sql is correct.
--This dynamic sql is parameterized as much as possible
--exec sp_executesql #SQL, N'#newValue varchar(10), #oldValue varchar(10)', #newValue = #newValue, #oldValue = #oldValue
No the update at the end will not work. You need to use exec (#sql) like this:
declare #sql varchar(4000)
begin
set #sql = 'update ' + #tableName + 'set company = ' + #newValue + 'where company = ' + #oldValue
exec (#sql)
fetch next ...
end
This assumes that #newvalue and #oldvalue are being assigned values somewhere.

How to join sys.databases, sys.tables and sys.columns

I have to check for value existence in a subset of tables in a subset of databases of a sql server instance. Beware I need to do this because I have 30 databases with same schema name and similar structure. Querying all databases separately is a waste of time.
The query generates correctly code for existing tables, but the additional check for column existence in table fails.
The column in some tables does not exist so the generated code must not include queries on tables without this column.
To solve this I need to realiably find a way to join sys.databases with sys.tables and then sys.columns. Or an alternative way to query all the required databases in a time saving manner.
SET NOCOUNT ON;
IF OBJECT_ID (N'tempdb.dbo.#temp') IS NOT NULL
DROP TABLE #temp
CREATE TABLE #temp
(
exist INT
, DB VARCHAR(50)
, tbname VARCHAR(500)
)
/*tables common root,
all tables i need to query start with this prefix and a number between 1 and 50
and some resulting tables do not exist
ex: dbo.Z_WBL_ASCHEDA23 exist in wbcto, while dbo.Z_WBL_ASCHEDA23 does not exist in db wbgtg
*/
DECLARE #TableName NVARCHAR(200)
SELECT #TableName = 'dbo.Z_WBL_ASCHEDA'
DECLARE #SQL NVARCHAR(MAX)
;WITH n(n) AS
(
SELECT 1
UNION ALL
SELECT n+1 FROM n WHERE n < 50
)
SELECT #SQL = STUFF((
SELECT CHAR(13)+'SELECT COUNT(1), ''' + db.name + ''', '''+
#TableName+CONVERT(VARCHAR, n.n)+''' FROM ' +#TableName+CONVERT(VARCHAR, n.n)
+ ' WHERE COALESCE(s_dettagli,'''') = ''CONTROLLATO'' '
+CHAR(13)
FROM sys.databases db
INNER JOIN n ON 1=1
INNER JOIN sys.tables t ON OBJECT_ID(db.name + '.' + #TableName+CONVERT(VARCHAR, n.n)) IS NOT NULL
INNER JOIN sys.columns c ON t.OBJECT_ID = c.OBJECT_ID and c.name = 's_dettagli'
/*join on columns not working, generates sql for tables without 's_dettagli' column and query fails*/
WHERE db.name like 'wb%' --check only databases starting with 'wb'
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
select #SQL
INSERT INTO #temp (exist, DB, tbname)
EXEC sys.sp_executesql #SQL
SELECT *
FROM #temp t
where exist <> 0
EDIT: adding some sql generated from query
SELECT COUNT(1), 'wb360', 'dbo.Z_WBL_ASCHEDA23' FROM wb360.dbo.Z_WBL_ASCHEDA23 WHERE COALESCE(s_dettagli,'') = 'CONTROLLATO'
SELECT COUNT(1), 'Wbbim', 'dbo.Z_WBL_ASCHEDA32' FROM Wbbim.dbo.Z_WBL_ASCHEDA32 WHERE COALESCE(s_dettagli,'') = 'CONTROLLATO'
the table of first query doesn't contain 's_dettagli' column
EDIT2: SOLUTION
EXEC sp_MSforeachdb '
IF ''?'' not like ''wb%''
RETURN
USE [?]
EXEC sp_MSforeachtable
#replacechar = ''!'',
#command1 = ''SELECT ''''?'''' AS db_name, ''''!'''' AS table_name, COUNT(*) FROM ! '',
#whereand = '' And Object_id In (
Select t.Object_id
From sys.objects t
INNER JOIN sys.columns c on c.Object_id = t.Object_id
Where t.name like ''''Z_WBL_ASCHEDA%''''
AND c.name = ''''s_dettagli'''' )'' '
Sys.columns can be joined to sys.tables using the object_id field (the object_id is the representation of the table itself).
sys.tables is run in the context of the database you are querying, hence you cannot see a table contained in another database. sys.databases can be run on any database on an instance and allow you to view other databases on the same instance. As such you don't need to join the table to the database (also the reason why there is no database_id field within sys.tables).
I hope that helps. Any clarification please let me know.
I would suggest alternative ways:
use registered Servers in SSMS and run the script on each database here
use exec sys.sp_MSforeachdb here
use sqlcmd and powershell to switch databases
I believe this script can help you :
SET NOCOUNT ON;
IF OBJECT_ID (N'tempdb.dbo.#Temp') IS NOT NULL
DROP TABLE #Temp
CREATE TABLE #Temp
(
exist INT
, DB VARCHAR(50)
, tbname VARCHAR(500)
)
DECLARE #SchemaName NVARCHAR(200)
DECLARE #TableName NVARCHAR(200)
DECLARE #ColumnName NVARCHAR(200)
DECLARE #SearchText NVARCHAR(200)
DECLARE #DBNameStartWith NVARCHAR(200)
DECLARE #SQL NVARCHAR(MAX)
SET #DBNameStartWith = 'wb'
SET #SchemaName = 'dbo'
SET #TableName = 'Z_WBL_ASCHEDA'
SET #ColumnName = 's_dettagli'
SET #SearchText = 'CONTROLLATO'
DECLARE #DatabaseName varchar(100)
DECLARE Crsr CURSOR FOR
SELECT name
FROM MASTER.sys.sysdatabases
WHERE name LIKE ''+#DBNameStartWith+'%'
OPEN Crsr
FETCH NEXT FROM Crsr INTO #DatabaseName
WHILE ##FETCH_STATUS = 0
BEGIN
IF ISNULL((SELECT COUNT(1) FROM SYS.TABLES T,SYS.COLUMNS C WHERE T.object_id=C.object_id AND T.name=#TableName AND C.name=#ColumnName),0)>0
BEGIN
SET #SQL = '
IF EXISTS (SELECT 1 FROM '+#DatabaseName+'.SYS.TABLES T,'+#DatabaseName+'.SYS.COLUMNS C WHERE T.object_id=C.object_id AND T.name='''+#TableName+''' AND C.name='''+#ColumnName+''')
BEGIN
SELECT COUNT(1),'''+#DatabaseName+''','''+#TableName+'''
FROM '+#DatabaseName+'.'+#SchemaName+'.'+#TableName+'
WHERE '+#ColumnName+'=''' +#SearchText+'''
END'
PRINT(#SQL)
INSERT INTO #Temp
EXEC sp_executesql #SQL
END
FETCH NEXT FROM Crsr INTO #DatabaseName
END
CLOSE Crsr
DEALLOCATE Crsr
SELECT * FROM #Temp

How to adjust TSQL code to truncate only tables of the dim schema?

I have the following to modify..
DECLARE #tablename AS VARCHAR (1000)
DECLARE #sql AS VARCHAR (1000)
IF OBJECT_ID('tempdb.dbo.#tables') IS NOT NULL
DROP TABLE #tables
SELECT *
INTO #tables
FROM sys.tables
WHILE EXISTS (SELECT *
FROM #tables)
BEGIN
SELECT #tablename = name
FROM #tables
SELECT #sql = 'truncate table ' + #tablename;
PRINT #sql
EXECUTE (#sql)
DELETE #tables
WHERE name = #tablename;
END
The above code will truncate all tables in a database, which is what I need however I only want this to work on tables that are part of the "dim" schema.
As an example, I have the following tables..
dbo.sales
dim.employee
dim.office
I'd like the script to only truncate the "dim" schema tables and not the dbo.sales table.
This is assuming that I don't have any PK/FK (as I have another code handling the drop/create of PK/FK).
Any help would be appreciated.
Thanks.
Try this one -
Query:
DECLARE #SQL NVARCHAR(MAX) = ''
SELECT #SQL = (
SELECT 'TRUNCATE TABLE [' + s.name + '].[' + o.name + ']' + CHAR(13)
FROM sys.objects o WITH (NOWAIT)
JOIN sys.schemas s WITH (NOWAIT) ON o.[schema_id] = s.[schema_id]
WHERE o.[type] = 'U'
AND s.name = 'dim'
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
PRINT #SQL
EXEC sys.sp_executesql #SQL
Output:
TRUNCATE TABLE [dim].[test1]
TRUNCATE TABLE [dim].[test2]
TRUNCATE TABLE [dim].[test3]
TRUNCATE TABLE [dim].[test4]
...
SELECT SCHEMA_NAME(schema_id) + '.' + name as name
INTO #tables
FROM sys.tables
where SCHEMA_NAME(schema_id) = 'dim'

Find Identity columns from another database

Normally with SQL Server you can use the COLUMNPROPERTY function like this to find the Identity columns in a database:
select TABLE_NAME + '.' + COLUMN_NAME, TABLE_NAME
from INFORMATION_SCHEMA.COLUMNS
where TABLE_SCHEMA = 'dbo'
and COLUMNPROPERTY(object_id(TABLE_NAME), COLUMN_NAME, 'IsIdentity') = 1
order by TABLE_NAME
But I can't figure out how to get this to work when running the query from another database. E.g. this does not return any results:
Use FirstDatabase
Go
select TABLE_NAME + '.' + COLUMN_NAME, TABLE_NAME
from SecondDatabase.INFORMATION_SCHEMA.COLUMNS
where TABLE_SCHEMA = 'dbo'
and COLUMNPROPERTY(object_id(TABLE_NAME), COLUMN_NAME, 'IsIdentity') = 1
order by TABLE_NAME
Object_ID only works in current db, unless you use a 3-part name, but that form is complicated to use. Also, ColumnProperty only works in current db.
select o.name + '.' + c.name, o.name
from test1.sys.columns c
join test1.sys.objects o on c.object_id = o.object_id
join test1.sys.schemas s on s.schema_id = o.schema_id
where s.name = 'dbo'
and o.is_ms_shipped = 0 and o.type = 'U'
and c.is_identity = 1
order by o.name
There is no way to get information with the help of COLUMNPROPERTY from another database. But there is on workaround:
DECLARE #DatabaseName VARCHAR(MAX)
DECLARE #TableName VARCHAR(MAX)
DECLARE #SQL VARCHAR(MAX)
SET #DatabaseName = 'MyDatabase'
SET #TableName = 'MyTable'
SET #SQL = '
SELECT
C.TABLE_NAME,
C.COLUMN_NAME,
S.IS_IDENTITY
FROM ' + #DatabaseName + '.INFORMATION_SCHEMA.COLUMNS AS C
LEFT JOIN ' + #DatabaseName + '.SYS.COLUMNS AS S ON OBJECT_ID(''' + #DatabaseName + '.dbo.' + #TableName + ''') = S.OBJECT_ID AND C.COLUMN_NAME = S.NAME
WHERE S.IS_IDENTITY = 1'
EXEC(#SQL)
This worked for me using a specific database:
USE <database_name>;
GO
SELECT SCHEMA_NAME(schema_id) AS schema_name
, t.name AS table_name
, c.name AS column_name
FROM sys.tables AS t
JOIN sys.identity_columns c ON t.object_id = c.object_id
ORDER BY schema_name, table_name;
GO
In this example, I've constructed a stored procedure in "Database1" that uses dynamic SQL to retrieve column information from a table in "Database2" (using the [INFORMATION_SCHEMA].[COLUMNS] system view residing in "Database2"):
ALTER PROCEDURE [Database1].[Schema1].[ColumnNames] #Database2 nvarchar(128), #Schema2 nvarchar(128), #Table2 nvarchar(128)
AS
BEGIN
DECLARE #Sql nvarchar(1000)
SET #Sql = 'SELECT [COLUMN_NAME], [ORDINAL_POSITION] FROM [' + #Database2 + '].[INFORMATION_SCHEMA].[COLUMNS]
WHERE [TABLE_SCHEMA] = ''' + #Schema2 + ''' AND [TABLE_NAME] = ''' + #Table2 + ''''
EXEC(#Sql)
END
I'm using SQL Server 2019, and I have run into the same challenge. I'm not sure if this fix will work for older versions, but there is a view in each DB called Your-DB-Name.sys.identity_columns. If you select from this view, you'll see the list of identity columns you have defined in that DB.
From that information you should be able to write a join connecting YourDBName.Information_schema.columns such as below:
SELECT *
FROM YourDBName.Information_Schema.columns col
LEFT OUTER JOIN YourDBName.sys.identity_columns idc
ON idc.name = col.COLUMN_NAME AND idc.object_id = object_id('YourDBName..YourTableName')
WHERE col.TABLE_NAME = 'YourTableName' AND col.table_catalog = 'YourDBName';
The YourDbName.sys.identity_columns view contains the following fields that might be useful:
object_id (used to join back to the table in question in case you have multiple tables with the same identity field name)
name (the name of the Identity field)
column_id (the order of the column in your table)
is_identity (tells you if this is an identity field)
seed_value (the initial value of the identity field)
increment_value (how much the identity field goes up with each insert)

Is it possible to Add column to multiple table simultaneously?

I am using SQL Server. I want to add a single column named [DateCreated] to multiple tables. Is it possible that with a single statement I could add this column to all the tables in my database?
I stumble upon an answer by Joe Steffaneli in which he suggested a query which in turn returns rows consisting Alter table statements.
Query is as follows :
select 'alter table ' + quotename(s.name) + '.' + quotename(t.name) + ' add [DateModified] datetime'
from sys.columns c
inner join sys.tables t
on c.object_id = t.object_id
inner join sys.schemas s
on t.schema_id = s.schema_id
left join sys.columns c2
on t.object_id = c2.object_id
and c2.name = 'DateModified'
where c.name = 'DateCreated'
and t.type = 'U'
and c2.column_id is null /* DateModified column does not already exist */
Is there any way that I can execute returned rows? Sorry for English.
You probably need something like this. Check that the script does what you want before running it (adds a non null column with a default value of getdate())!
DECLARE #Dynsql nvarchar(max)
SET #Dynsql = ''
SELECT #Dynsql = #Dynsql + '
alter table ' + QUOTENAME(SCHEMA_NAME(schema_id))+ '.' + QUOTENAME(name) +
' add [DateCreated] datetime not null default getdate()'
FROM sys.tables
WHERE type='U' and object_id NOT IN (select object_id from sys.columns where name='DateCreated')
EXEC (#Dynsql)
You can use this query
I use the where clause in this query (it is optional) to find all tables that have ID and add the new field to them.
you can change where clause for example find all tables that have BusinessId column and add new filed or remove where clause and add for all tables
DECLARE #SQL varchar(max)
SELECT #SQL= STUFF((SELECT ';' + 'ALTER TABLE ' + t.TABLE_SCHEMA+'.' +t.TABLE_NAME+ ' ADD newfield nvarchar(max)'
from information_schema.tables t
inner join information_schema.columns c on c.table_name = t.table_name
and c.table_schema = t.table_schema
where c.column_name = 'id'
and t.table_schema not in ('information_schema', 'pg_catalog')
and t.table_type = 'BASE TABLE'
FOR XML PATH('')),1,1,'')
EXEC (#SQL)
This stored procedure works fine
USE databaseName;
exec sp_msforeachtable 'alter table ? Add [DateCreated] datetime not null default getdate()';
declare #i int
set #i=1
while(#i<45)
begin
declare #sql varchar(200)
with TempTable as (select Row_number() over(order by stdID) as RowNo,* from SomeTable)
select #sql= 'alter table Table'+(select Name from TempTable where RowNo=#i)+' add NewColumn int'
exec (#sql)
set #i=#i+1
end
No, there is no single statement that will add a column to all the tables in your database.
Next time please tag your question with the RDBMS you're using. If there were a way, we wouldn't be able to give you the command without knowing which database system you are using.

Resources