Add new column to all tables in database - sql

I'm trying to figure out if there's a quick way or single query to add a new column to all tables in database.
Right now I'm doing this for each table
ALTER TABLE [dbo].[%TABLE_NAME%] ADD %COLUMN_NAME% DATATYPE NOT NULL DEFAULT %VALUE%;
Is there a procedure or query I can make in AzureDataStudio to add a new column to all tables with the same name and default value.

select 'ALTER TABLE ' + QUOTENAME(SCHEMA_NAME([schema_id])) + '.' + QUOTENAME([name])
+ ' ADD %COLUMN_NAME% DATATYPE NOT NULL DEFAULT %VALUE%;'
from sys.tables
Create the statements you need with the above then run them.

I'd personally create a loop with dynamic SQL which gets executed as it is ran. The code below creates a temp table which is utilized for the loop which will iterate through each table listed in the temp table based on a calculated row number. The dynamic SQL is then set and executed.
Once you make the necessary changes, putting in your database name, column name, data type, and default value and you are satisfied with the results that get printed, you can un-comment the EXECUTE(#SQL) and re-run the script and it will add the new column to all your tables.
USE [INSERT DATABASE NAME HERE]
GO
IF OBJECT_ID(N'tempdb..#TempSysTableNames') IS NOT NULL
BEGIN
DROP TABLE #TempSysTableNames
END;
DECLARE #ColumnName VARCHAR(250) = 'INSERT COLUMN NAME HERE'
,#DataType VARCHAR(250) = 'INSERT DATA TYPE HERE'
,#DefaultValue VARCHAR(250) = 'INSERT DEFAULT VALUE HERE'
,#SQL VARCHAR(8000)
,#MaxRowNum INT
,#I INT = 1;
SELECT '[' + DB_NAME() + '].[' + OBJECT_SCHEMA_NAME([object_id],DB_ID()) + '].[' + name + ']' AS [name]
,ROW_NUMBER() OVER (ORDER BY [create_date]) AS RowNum
INTO #TempSysTableNames
FROM sys.tables
WHERE [type] = 'U';
SET #MaxRowNum = (SELECT MAX(RowNum)
FROM #TempSysTableNames);
WHILE (#I <= #MaxRowNum)
BEGIN
SET #SQL = (SELECT 'ALTER TABLE ' + [name] + ' ADD ' + #ColumnName + ' ' + #DataType + ' NOT NULL DEFAULT ' + #DefaultValue + ';'
FROM #TempSysTableNames
WHERE RowNum = #I);
PRINT(#SQL);
--EXECUTE(#SQL);
SET #I += 1;
END;

Related

How to add Column or create new table dynamically using stored procedure

I have a table in database with column and its type and table status new or old.
column data_type table_name status
id int employee new
name varchar(20) employee new
region varchar(20) student old
I want to create a stored procedure which functions like below.
It should read data from this table and if table status is new then it should create new table with column name given and its data type ,but it should create table with all the columns in a single create statement.
If status is old then it should execute alter command and add new column with its data type and if we have to add more than one column in a same table then it should be added using single alter statement not again and again.
Now my question is i have approach if solution required is adding column one by one but how can i fetch all the rows and read all columns with data type which should be added in same table..
i am giving pseudo code for my approach.
declare temp variable
read data from table through select statement
set variable equal to data
if status is new then run create command
else run alter command.
But my question is after if or else how can i find all the columns which will be added in same column.
You need to aggregate twice, once on columns, then again all the tables. I added a nullable column also:
DECLARE #sql nvarchar(max) =
(SELECT STRING_AGG(stmt, NCHAR(10))
FROM (
SELECT
stmt = 'CREATE TABLE ' +
QUOTENAME(table_name) +
' ( ' +
STRING_AGG(QUOTENAME(column) + ' ' + data_type + IIF(nullable, ' NULL', ' NOT NULL'), ', ') +
' );'
FROM Table
WHERE status = 'new'
GROUP BY table_name
) AS v);
SET #sql = #sql + NCHAR(10) + NCHAR(10) +
(SELECT STRING_AGG(stmt, NCHAR(10))
FROM (
SELECT
stmt = 'ALTER TABLE ' +
QUOTENAME(table_name) +
' ALTER COLUMN ' +
NCHAR(10) +
STRING_AGG(QUOTENAME(column) + ' ' + data_type + IIF(nullable, ' NULL', ' NOT NULL'), ', ') +
';'
FROM Table
WHERE status = 'old'
GROUP BY table_name
) AS v);
PRINT #sql --for testing
EXEC(#sql)

Variable value was lost when while loop finished

I want to set some default values to some table by using dynamic SQL in SQL Server, so I write 2 while loop, one is for tables and one is for columns in that table. so the outer loop is used to iterate table and the inner loop is used to iterate columns according to different data types the default will vary from one to other. So I need to catenate strings to build the dynamic SQL, please see my code below:
DECLARE #V_TABLE_LIST TABLE (TABLE_NAME VARCHAR(300))
DECLARE #V_COLUMN_LIST TABLE (TABLE_NAME VARCHAR(300), COLUMN_NAME VARCHAR(300), DATA_TYPE VARCHAR(300))
DECLARE #V_TABLE_NAME VARCHAR(300)
DECLARE #V_TABLE_NAME2 VARCHAR(300)
DECLARE #V_COLUMN_NAME VARCHAR(300)
DECLARE #V_DATA_TYPE VARCHAR(300)
DECLARE #V_SQL_ENABLE_IDENTITY_INSERT VARCHAR(200)
DECLARE #V_SQL_INSERT VARCHAR(3500)
DECLARE #V_SQL_COLUMN_LIST_NAME VARCHAR(3000)
DECLARE #V_SQL_COLUMN_LIST_VALUE VARCHAR(3000)
DECLARE #V_SQL_DISABLE_IDENTITY_INSERT VARCHAR(200)
INSERT INTO #V_TABLE_LIST
(TABLE_NAME)
SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE 'DIM%' AND TABLE_TYPE = 'BASE TABLE'
AND TABLE_NAME NOT IN ('DIM_DATE') AND TABLE_NAME = 'DIM_ASSET'
--loop through each table
WHILE (SELECT COUNT(*) FROM #V_TABLE_LIST) > 0
BEGIN
SELECT TOP 1
#V_TABLE_NAME = TABLE_NAME
FROM #V_TABLE_LIST
--PRINT(#V_TABLE_NAME)-------------
SET #V_SQL_ENABLE_IDENTITY_INSERT = 'SET IDENTITY_INSERT ' + #V_TABLE_NAME + ' ON'
SET #V_SQL_DISABLE_IDENTITY_INSERT = 'SET IDENTITY_INSERT ' + #V_TABLE_NAME + ' OFF'
--load column info into #v_column_list table variable for each table
INSERT INTO #V_COLUMN_LIST
(TABLE_NAME, COLUMN_NAME, DATA_TYPE)
SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #V_TABLE_NAME
SET #V_SQL_INSERT = ''
SET #V_SQL_COLUMN_LIST_NAME = ''
SET #V_SQL_COLUMN_LIST_VALUE = ''
--loop through each column for each table
WHILE (SELECT COUNT(*) FROM #V_COLUMN_LIST) > 0
BEGIN
SELECT TOP 1
#V_TABLE_NAME2 = TABLE_NAME
,#V_COLUMN_NAME = COLUMN_NAME
,#V_DATA_TYPE = DATA_TYPE
FROM #V_COLUMN_LIST
SET #V_SQL_COLUMN_LIST_NAME = #V_SQL_COLUMN_LIST_NAME + #V_COLUMN_NAME + ' --' + #V_DATA_TYPE +CHAR(10) + ','
SET #V_SQL_COLUMN_LIST_VALUE = #V_SQL_COLUMN_LIST_VALUE +
CASE WHEN #V_DATA_TYPE IN ('VARCHAR','NVARCHAR','CHAR', 'NCHAR') THEN '''UNKNOWN'''
WHEN #V_DATA_TYPE IN ('bigint', 'INT', 'smallint', 'DECIMAL','NUMERIC','MONEY','SMALLMONEY') THEN '-1'
WHEN #V_DATA_TYPE IN ('BIT', 'TINYINT') THEN NULL
WHEN #V_DATA_TYPE IN ('DATE', 'DATETIME','SMALLDATETIME','DATETIMEOFFSET','DATETIME2') THEN '''1957-01-01'''
ELSE ''
END + ' --' + #V_COLUMN_NAME + CHAR(10) + ','
DELETE FROM #V_COLUMN_LIST WHERE TABLE_NAME = #V_TABLE_NAME2 AND COLUMN_NAME = #V_COLUMN_NAME
--PRINT(#V_SQL_COLUMN_LIST_VALUE)
END
PRINT(#V_SQL_COLUMN_LIST_NAME)
PRINT(#V_SQL_COLUMN_LIST_VALUE)
--PRINT(#V_SQL_ENABLE_IDENTITY_INSERT)
SET #V_SQL_INSERT = 'INSERT INTO ' + #V_TABLE_NAME + CHAR(10)
+ '('
+ #V_SQL_COLUMN_LIST_NAME
+ ')'
+ ' VALUES ' + CHAR(10)
+ '(' + CHAR(10)
+ #V_SQL_COLUMN_LIST_VALUE
+ ')'
--PRINT(#V_SQL_INSERT)
--PRINT(#V_SQL_DISABLE_IDENTITY_INSERT)
DELETE FROM #V_COLUMN_LIST
DELETE FROM #V_TABLE_LIST WHERE TABLE_NAME = #V_TABLE_NAME
END
I added 2 print statements:
PRINT(#V_SQL_COLUMN_LIST_NAME) ---the concatenated field list can be printed out normally
PRINT(#V_SQL_COLUMN_LIST_VALUE) ---cannot print concatenated default value list , why?
as you can see the two print statements are the next step for the finishing of inner loop, but the first print statement can print out the something and the second one is empty, I checked the code a long time, I cannot find why the second print statement output empty string. Any logic errors in the code above?
This row sets the entire result to NULL if any column of BIT or TINYINT type is met.
WHEN #V_DATA_TYPE IN ('BIT', 'TINYINT') THEN NULL
Should be
WHEN #V_DATA_TYPE IN ('BIT', 'TINYINT') THEN 'NULL'
the same way as any other constant in a dynamic sql.

DELETE FROM AllTables Where ColumnName="Some Text"

I'm working with some databases and I have quite a few items I need to remove from them. There are about 300 or so tables in each database and I am trying to find a way to drop the rows that contain the string I need to remove.
DELETE FROM TableName WHERE ColumnName='Some String'
Works just fine, but I do not want to run that for each table. I'm hoping there is a way to do something like:
DELETE FROM * WHERE ColumnName='Some String'
I have been searching around on Google and have come accross several articles on how to drop all of the data in all the tables, which is not what I am trying to accomplish.
I am using MS SQL Server 2014.
I am still somewhat new to SQL and I don't know if there is already a way to this and I am just not using the write search terms to find it.
Thank you for your assistance.
This works for other databases, too - on other databases, you only have to change the syntax for the concatenation from + to || and change schema and name of the COLUMNS system view / table.
Run the query to a report on file. Remove any headers/footers. Then, run that file as a SQL script.
SELECT
'DELETE FROM ' + table_catalog
+ '.' + table_schema
+ '.' + table_name
+ ' WHERE ' + column_name
+ ' = ''Some String'';'
FROM information_schema.columns
WHERE UPPER(column_name) = 'NAME'
;
My database, which happens to be Vertica, returns:
DELETE FROM gpb.agreement_type WHERE name = 'Some String';
DELETE FROM gpb.product WHERE name = 'Some String';
DELETE FROM gpb.account_type WHERE name = 'Some String';
DELETE FROM gpb.document_type WHERE name = 'Some String';
DELETE FROM gpb.transaction_type WHERE name = 'Some String';
DELETE FROM gpb.account WHERE name = 'Some String';
DELETE FROM public.people_with_dupes WHERE name = 'Some String';
DELETE FROM public.people WHERE name = 'Some String';
There is an undocumented (but not unknown) stored procedure called sp_MSforeachtable.
exec sp_MSforeachtable 'DELETE FROM ? WHERE ColumnName = 'Some String';
TRY: You can use this technique to get rid from the tedious job of typing code for every table. You can first retrieve all the objects which have that <column_name> and then create a loop with delete statement. Please change <column_name> with your real column name.
Please try it, I am sure it will work for you:
select id = identity(int, 1, 1), o.name INTO #object_name
from sys.objects o
inner join sys.columns c on c.object_id = o.object_id
where type = 'u' and c.name = <column_name>
declare #i int = 1, #j int = 0, #sql varchar(500)
select #j = count(*) from #object_name
while (#i <= #j)
begin
declare #tbl_name varchar(100)
select #tbl_name = name from #object_name where id = #i
set #sql = 'delete from ' + #tbl_name + ' where <column_name> = <some_string>'
--print(#sql)
exec(#sql)
set #i = #i+1
end
Delete column from single table :
ALTER TABLE table_name DROP COLUMN column_name
Delete column from multiple table :
DECLARE #SQL VARCHAR(MAX)
SELECT #SQL += 'Alter table ' + Quotename(table_catalog)
+ '.' + Quotename(table_schema) + '.'
+ Quotename(TABLE_NAME) + ' DROP column '
+ Quotename(column_name) + ';'
FROM information_schema.columns where COLUMN_NAME = 'your_column_name' --
EXEC Sp_executesql #sql

sql create database from variable

I have #db_out = 'aux.dbo.some_table_name' , and I don't know how to drop , create based on that variable like :
IF OBJECT_ID(#db_out) IS NOT NULL DROP TABLE "#db_out" - not working
CREATE TABLE "#db_out" .... etc
it creates master.dbo.#dbo_out
How can I use that variable to create that table or verify it and drop it ?
You will have to build the statement in varchar variable and execute it:
declare #strSql as varchar(max)
IF OBJECT_ID(#db_out) IS NOT NULL
BEGIN
EXEC ('DROP TABLE [' + #db_out + ']')
END
set #strSql = 'CREATE TABLE [' + #db_out + '] (' -- Etc
EXEC (#strSql)
You need to write dynamic SQL.
SET #sql = 'IF OBJECT_ID(#db_out) IS NOT NULL DROP TABLE ' + #db_out + '; '
SET #sql = #sql + 'CREATE TABLE ' + #db_out + ' (...)'
EXEC(#sql)

How can I copy a row into same table with SQL Server 2008

A) My way so far:
sqlCommand.CommandText =
"INSERT Table1 ([column1],[column2],[column3])" +
" SELECT [column1],[column2],[column3]" +
" FROM Table1 WHERE Id =" + param +
";select scope_identity() as id";
B) I wish to do something like this:
INSERT INTO "table1" (* (without the ID-column))
SELECT (* (without the ID-column))
FROM "table1"
Note: I'm copying to the same table. I just want to easy copy it all to another row, while ofcourse giving the new row a new ID.
Is that good practice and possible or not?
I had the same issue myself and wanted a nice and simple way of doing this.
I found a solution here which allows this. I've modified it slightly to remove the output id and also to make the IdColumnName have a default value of 'Id'.
IF OBJECT_ID('TableRowCopy') IS NOT NULL DROP PROCEDURE TableRowCopy
GO
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[TableRowCopy](
#TableName VARCHAR(50),
#WhereIdValue INT,
#IdColumnName VARCHAR(50) = 'Id'
)
AS
BEGIN
DECLARE #columns VARCHAR(5000), #query VARCHAR(8000);
SET #query = '' ;
SELECT #columns =
CASE
WHEN #columns IS NULL THEN column_name
ELSE #columns + ',' + column_name
END
FROM INFORMATION_SCHEMA.COLUMNS
WHERE (
TABLE_NAME = LTRIM(RTRIM(#TableName))
AND
column_name != LTRIM(RTRIM(#IdColumnName))
);
SET #query = 'INSERT INTO ' + #TableName + ' (' + #columns + ') SELECT ' + #columns + ' FROM ' + #TableName + ' WHERE ' + #IdColumnName + ' = ' + CAST(#WhereIdValue AS VARCHAR);
EXEC (#query);
SELECT SCOPE_IDENTITY();
END
Example usage:
EXEC TableRowCopy 'MyTable', 3
The only way of doing this is to list all the columns out as in your first example. There is no syntax like SELECT *, -Id
You should use parameterised SQL though for SQL injection and plan caching reasons.