I have a SQL Server table with 100 columns. I would like to do this to each column:
UPDATE 2011INFODATA
SET [Attribute_Code_1] = ltrim(rtrim(Attribute_Code_1))
WHERE len(ltrim(rtrim(Attribute_Code_1))) > 0
My question is how I can use the informatin_schema.columns to dynamically generate or insert the various column names into the above SQL?
I could of course do it by hand for each column and get the same result. But that would be no fun and I want to use this same sql for many tables.
Something along these lines will do it.
DECLARE #Sql nvarchar(max)
SET #Sql = '';
SELECT #Sql = #Sql + '
UPDATE 2011INFODATA
SET ' + QUOTENAME(COLUMN_NAME) + ' = ltrim(rtrim(' + QUOTENAME(COLUMN_NAME) + '))
WHERE len(ltrim(rtrim(' + QUOTENAME(COLUMN_NAME) + '))) > 0'
FROM information_schema.columns
WHERE TABLE_NAME = '2011INFODATA' AND DATA_TYPE LIKE '%char'
EXEC sp_executesql #Sql
Edit: updated sample SQL to incorporate helpful comments from #billinkc, #Martin Smith, #Aaron Bertrand.
Related
Here the #Data has a value with apostophe(')s . how do i update or insert a data based on the data value which is having apostophe in a dynamic sql
suppose #data has one value abc and another value abc's it throwing error for the second one
SET #SQL = ' Update '+ #ProcessCode + '_abc SET IS_IGNORING = 1 where Column_Name = '''+ #Column_Name +''' and [DATA] = ''' + #Data + ''' and Table_name = '''+ #Table_Name + ''''
Generally what i found is a manual process of adding one more apostophe but i am not really sure how to use that in a dynamic sql where not all data in the table is same, few of the data records has got this type of apostophe(')
Parameterized your query using sp_executesql
Example:
SET #SQL = 'Update ' + #ProcessCode + '_abc '
+ 'SET IS_IGNORING = 1 '
+ 'where Column_Name = #Column_Name '
+ 'and [DATA] = #Data '
+ 'and Table_name = #Table_Name '
EXEC sp_executesql #SQL,
N'#Column_Name varchar(100), #Data varchar(100), #Table_Name varchar(100)',
#Column_Name, #Data, #Table_Name
Do read up more on dynamic query and SQL Injection
You might find convenient to use parameterized queries, so you can replace static values with placeholders and then bind values to those placeholders before executing the query. It has certain advantages like better performance and helps to avoid SQL-injection attacks.
More info here: https://techcommunity.microsoft.com/t5/sql-server-blog/how-and-why-to-use-parameterized-queries/ba-p/383483
I have some bulk views to create for an entire database.
To create a view the general syntax is as follows:
CREATE VIEW [TABLE_NAME]
AS
SELECT [COLUMN1], [COLUMN2], [COLUMN3], [COLUMN4]
FROM [TABLE_NAME]
WITH CHECK OPTION;
I would like to set the column names in the script above by querying the column names ([COLULMN1], [COLUMN2], etc) from INFORMATION_SCHEMA.COLUMNS.
Is there a way to achieve this by table name?
COALESCE is your friend good programmer. What you want to do is get a csv list of COLUMNS. Then using dynamic sql you can auto generate the rest of the code.
declare #columns AS VARCHAR(MAX)
SELECT #COLUMNS = NULL
select #COLUMNS = coalesce(#columns+',','')+c.name from syscolumns as c
inner join sysobjects as o on c.id = o.id
WHERE O.NAME = 'change me to your table name'
SELECT #COLUMNS
SELECT ' CREATE VIEW ' + 'COOL VIEW NAME' + ' AS ' +
' SELECT ' + #COLUMNS +
' FROM '+ ' change me to your table name '+
' WITH CHECK OPTION;'
EDIT
I purposely didn't declare the view anywhere. If you want to declare the view just execute the scripts like so. BUT YOU SHOULD NEVER just execute code on your servers without reading it all I purposely excluded the execution part as I think it is bad judgement just to cut and paste code and execute it without understanding/testing.
DECLARE #sql varchar(max)
SELECT #sql = ' CREATE VIEW ' + 'COOL VIEW NAME' + ' AS ' +
' SELECT ' + #COLUMNS +
' FROM '+ ' change me to your table name '+
' WITH CHECK OPTION;'
EXEC(#sql);
Here's one option... replace "MyTableName" with the table name you want, or wrap it in a cursor that reads TABLE_NAME from INFORMATION_SCHEMA.VIEWS into #tableName:
DECLARE #tableName sysname;
DECLARE #sql nvarchar(max);
DECLARE #columnList nvarchar(max);
SELECT #tableName = 'MyTableName';
SELECT #columnList = '';
SELECT #columnList += CASE WHEN LEN(#columnList) = 0 THEN '' ELSE ', ' END + COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #tableName ORDER BY ORDINAL_POSITION;
SELECT #sql = 'CREATE VIEW [TABLE_NAME] AS
SELECT ' + #columnList + '
FROM [' + #tableName + ']
WITH CHECK OPTION;'
PRINT #sql
EXEC(#sql);
Is there a possibility to alter a column from "allows null" to "does not allow null" without knowledge of the actual data type of the column?
I think no, so I have made as the basic skeleton code for my stored procedure:
SELECT t.name,c.max_length FROM sys.types t
LEFT JOIN sys.columns c ON(t.system_type_id = c.system_type_id)
WHERE object_id=OBJECT_ID(#TableName) AND c.name=#FieldName;
and
EXEC('UPDATE ' + #TableName + ' SET ' + #FieldName + ' = ' + #DefaultValue + ' WHERE ' + #FieldName + ' IS NULL');
EXEC('ALTER TABLE ' + #TableName + ' ALTER COLUMN ' + #FieldName + ' NOT NULL');
I guess now I only have to get the return values from the first query back into the second. I can't get my head around how to get the values into a variable and then access them again. Ideas?
Since the INFORMATION_SCHEMA has all required information and is part of a SQL standard, it might be better to use that in this case (however, SQL Server's ALTER TABLE ALTER COLUMN is non-standard anyway so it might not matter as much).
Either way, you should also be checking for whether there's character length and/or numeric precision being specified, and make sure you're altering the table in the correct schema (and not getting dbo.TableName instead of customschema.TableName). You could try something like this (I used INFORMATION_SCHEMA here but you could easily refactor this to use the sys.columns view):
DECLARE #retVal VARCHAR(500);
SELECT #retVal =
CASE WHEN CHARACTER_MAXIMUM_LENGTH > 0
THEN CONCAT(DATA_TYPE, '(', CHARACTER_MAXIMUM_LENGTH ,')')
WHEN CHARACTER_MAXIMUM_LENGTH = -1 AND DATA_TYPE <> 'xml'
THEN CONCAT(DATA_TYPE, '(MAX)')
WHEN DATA_TYPE IN ('numeric', 'decimal')
THEN CONCAT(DATA_TYPE, '(', NUMERIC_PRECISION,',', NUMERIC_SCALE,')')
ELSE DATA_TYPE
END
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = #schemaName
AND TABLE_NAME = #tableName
AND COLUMN_NAME = #columnName
#retVal will now capture datatypes like int, varchar(100), varbinary(MAX), or decimal(10,2) correctly.
And then build up a dynamic SQL Query like this:
DECLARE #sql VARCHAR(MAX);
SET #sql = 'ALTER TABLE ' + #schemaName + '.' + #tableName + ' ALTER COLUMN ' + #columnName + ' ' + #retVal + ' NOT NULL;'
EXEC(#sql);
You select values into variables like this:
SELECT #Var1=t.name,#Var2=c.max_length FROM sys.types t
LEFT JOIN sys.columns c ON(t.system_type_id = c.system_type_id)
WHERE object_id=OBJECT_ID(#TableName) AND c.name=#FieldName;
This of course assumes that you have already declared Var1 & Var2, and that your query will only return one row.
I want to delete the ABCDEF column from all tables of my database. I am trying this code:
declare #SQL nvarchar(max)
SELECT
#SQL = STUFF((SELECT ' DROP column ' + quotename(TABLE_SCHEMA) + '.' + quotename(table_NAME ) +'.ABCDEF;'
FROM
information_schema.columns
FOR XML PATH('')),1,2,'')
PRINT #SQL
EXECUTE (#SQL)
But I am getting an error
incorrect syntax near column
How to do this?
You can use an undocumented feature of SQL Server sys.sp_msforeachtable. The below script will basically iterate for all the tables in database and alter them if required.
select '[dbo].['+tab.name+']' name into #table from
sys.tables tab join sys.columns col on tab.object_id = col.object_id and col.name = 'ABCDEF'
exec sys.sp_msforeachtable 'if exists (select 1 from #table where name = ''?'')
alter table ? drop column [ABCDEF]'
That's not the right way to drop a column from a table. It should be
ALTER TABLE table_name DROP COLUMN column_name
Build your dynamic query something like this:
DECLARE #sql NVARCHAR(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 = 'abcd' -- Here alone mention
--the Column to be removed
EXEC Sp_executesql #sql
While dropping the columns from multiple tables, I faced following default constraints error.
The object 'DF_TableName_ColumnName' is dependent on column 'ColumnName'.
To resolve this, I have to drop all those constraints first, by using following query
DECLARE #sql NVARCHAR(max)=''
SELECT #SQL += 'Alter table ' + Quotename(tbl.name) + ' DROP constraint ' + Quotename(cons.name) + ';'
FROM SYS.DEFAULT_CONSTRAINTS cons
JOIN SYS.COLUMNS col ON col.default_object_id = cons.object_id
JOIN SYS.TABLES tbl ON tbl.object_id = col.object_id
WHERE col.[name] IN ('Column1','Column2')
--PRINT #sql
EXEC Sp_executesql #sql
After that, I dropped all those columns by using the answer above.
DECLARE #sql NVARCHAR(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 IN ('Column1','Column2')
--PRINT #sql
EXEC Sp_executesql #sql
I posted here in case someone find the same issue.
Happy Coding!
I have a table where I have columns like below
[Index], [Length],[N1],[N2]....[N99]
Now, is possible to select only [N2]] ... [N29] columns without writing all names.
No, it's not possible. You need to explicitly list the subset of columns you want to return.
This is not possible without writing all names.
You can of course drag and drop all the columns from the object browser and then delete the ones you don't want. At least that way you don;t have any typos.
I would be concerned about the design of a table with that many columns. Espceially if they really are N1-N99. You may need a redesign to a related table. Also wide tables can cause performance issues.
How about this:
DECLARE #columns VARCHAR(MAX),
#tablename VARCHAR(255),
#from VARCHAR(255),
#select VARCHAR(100)
SET #tablename = 'orderheader'
SELECT #columns = STUFF(
(
SELECT ',[' + column_name + ']'
FROM information_schema.columns
WHERE table_name = #tablename
AND NOT column_name IN ('N2', 'Index', 'Length')
FOR XML PATH('')
),1, 1, '')
SELECT #select = 'SELECT ', #from = ' from ' + #tablename
EXEC(#select + #columns + #from)
Using dynamic sql is the closest you can get to not writing the columns. Here is an example:
declare #sql varchar(max)
select #sql = coalesce(#sql+',', 'select ') + 'n' + cast(number as varchar(2))
from master..spt_values as N
where type = 'P' and
number between 2 and 29
set #sql = #sql + ' from <yourtable>'
--select #sql
exec (#sql)