Is it possible to Add column to multiple table simultaneously? - sql-server

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.

Related

Rename a column in a table with SQL Sserver 2008

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';

Compare tables on Linked Servers SQL dynamically

I have to compare table (SIB$) to get unmatched records on two different LINKED SERVERS (LATESTDUMP, OLDDUMP) that are identical. I have already tried to create a dynamic query. Can some one please help me with following:
1) Is there a way where I dont have to pass the column names to the code and the code dynamically gets the column names and use it as the column list to compare.
So all I have to do is pass to the stored proc the two table names
Code i have worked on:
DECLARE #sql nvarchar(max) = ' ((SELECT * FROM LATESTDUMP...SIB$) t1 FULL
OUTER JOIN (SELECT * FROM OLDDUMP...SIB$) t2
ON t1.id = t2.id
WHERE
t1.id IS NULL OR
t2.id IS NULL)'
SELECT #sql += ' or t1.' + quotename(column_name) + ' <> t2.' +
quotename(column_name) from information_schema.columns where table_name =
'SIB$'
Looks like you might want to look at the MERGE statement that's been available since SQL 2008.
https://learn.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql
Here's some code to get you started. It pulls out a primary key (assumes one column primary keys, which may or may not be a valid assumption for you), and grabs a comma-delimited string list of the remaining columns.
From here you can use split-string to build a sql string that joins the two same-named tables from the hard-coded linked servers on the primary key, comparing each of the columns for difference, and then execute the dynamic SQL. I've included some test scaffolding so you can work it through:
DECLARE #tableName sysname;
SET #tableName = 'some table'
-- Validate parameter
IF #tableName IS NULL OR NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #tableName AND TABLE_TYPE = 'BASE TABLE')
BEGIN
RAISERROR ('Invalid table name specified', 16, 1);
RETURN;
END;
-- Validate table has a primary key
IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_TYPE = 'PRIMARY KEY' AND TABLE_NAME = #tableName)
BEGIN
RAISERROR ('Specified table does not have a primary key', 16, 1);
RETURN;
END;
-- Get info about the Primary Key columns
DECLARE #pkcolName sysname;
SELECT #pkcolName = c.COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu ON tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
INNER JOIN INFORMATION_SCHEMA.COLUMNS c ON kcu.TABLE_NAME = c.TABLE_NAME AND kcu.COLUMN_NAME = c.COLUMN_NAME
WHERE tc.CONSTRAINT_TYPE = 'PRIMARY KEY' AND tc.TABLE_NAME = #tableName AND kcu.ORDINAL_POSITION = 1
-- Grab the names of all the remaining columns
DECLARE #nonKeyColumns nvarchar(MAX);
SELECT #nonKeyColumns = STUFF ( ( SELECT N'], [' + c.name
FROM sys.columns c
WHERE object_id = (select top 1 object_id FROM sys.objects where name = #tableName)
AND c.name <> #pkcolName
ORDER BY c.column_id
FOR XML PATH('')), 1, 2, '') + ']';
SELECT #pkcolName
SELECT #nonKeyColumns

Getting tablenames from sys.tables and using them to find max value of a column in each table

I've transaction Backup tables in sqlserver and all have a similar naming convention like RECEIVER_CHARGE_LOG_[MONYYYY].I've queried sys.tables to find out 82 backup tables
I now need the max and min of the date in the table,so that i can check if any data's missing.
I need the output to be like
Table_name,MAX(DATE_TIME),MIN(DATE_TIME)
Is there a way I can reference all the tables from sys.tables and use them in from part of the main query.
Using sp_MSForeachtable you can filter the tables using the #Whereand parameter to restrict it to only the tables beginning with "RECEIVER_CHARGE_LOG_":
exec sp_MSforeachtable
#command1 = 'select ''?'', MAX(DATE_TIME),MIN(DATE_TIME) from ?',
#Whereand = ' and o.name like ''RECEIVER_CHARGE_LOG_%'''
EDIT
To make this even easier to use, you can insert into a temp table and then select from this:
CREATE TABLE #result
(
TableName SysName,
MaxDateTime DateTime,
MinDateTime DateTime
)
exec sp_MSforeachtable
#command1 = 'INSERT INTO #result select ''?'', MAX(DATE_TIME), MIN(DATE_TIME) from ?',
#Whereand = ' and o.name like ''RECEIVER_CHARGE_LOG_%''',
#postcommand = 'select * from #result;drop table #result'
You can use dynamic SQL like that. You may have to tweak it as you didn't give the exact schema of your tables:
declare #sql nvarchar(max) = N'create table #temp(Table_Name nvarchar(max), MaxDate datetime, MinDate datetime); '
select #sql = #sql + 'insert into #temp select ''' + name + ''', MAX(DATE_TIME),MIN(DATE_TIME) from [' + name + ']; '
from sys.tables where type = 'U' and name like 'RECEIVER_CHARGE_LOG%'
set #sql = #sql + ' select * from #temp; drop table #temp;'
exec (#sql)
#Damien_The_Unbeliever: Unbelievable. I didn't know sp_msforeachtable had such potential. I've always made these pages long scripts with while loops and a lot of dynamic SQL.
So OP, as an answer, you might try something like this for instance:
EDIT
You can also get the set out by using a temporary table to store the results, such as:
IF OBJECT_ID('tempdb..#TEMP') IS NOT NULL
DROP TABLE #TEMP
CREATE TABLE #TEMP (TableName VARCHAR(256) COLLATE DATABASE_DEFAULT, MaxDate DATETIME, MinDate DATETIME)
EXEC SP_msForeachTable 'IF OBJECT_ID(''?'') IN
(SELECT T.object_id
FROM sys.tables T
JOIN sys.columns C on C.object_id = T.object_id
JOIN sys.schemas S on S.schema_id = T.schema_id
WHERE T.name like ''RECEIVER_CHARGE_LOG_%'' AND C.name LIKE ''DATE_TIME''
AND S.name = ''dbo'')
EXEC(''INSERT INTO #TEMP SELECT ''''?'''' Table_Name, MAX(DATE_TIME) MaxDate, MIN(DATE_TIME) MinDate FROM ?'')'
SELECT *
FROM #TEMP
ORDER BY MinDate
Though I recommend you use Steve Ford's template for the sp_msForEachTable procedure, since it's far more reasonable and efficient.

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)

Resources