Search table columns for values over a certain length - sql

So I have a database with many tables that have a column that contains a GL Account value (for financial purposes). The column name varies by table (i.e. in one table the column is called "gldebitaccount" and in another table it's called "glcreditaccount"). I was able to find all combinations of table / column pairs using the following query:
SELECT c.name AS ColName, t.name AS TableName
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE c.name LIKE '%gl%acc%'
This query returns close to 100 pairs of tables/columns. I am trying to find any value in any of those table/column pairs that exceeds 25 chars in length. For an individual table/column, I'd typically use:
SELECT *
FROM tableName
WHERE LEN(columnName)>25
I want to avoid having to run that query 100 times with each pair. Is there any way I can do a "for each" (which I know is frowned upon in SQL since everything should be set-based). I've done sub-SELECT statements before, but not any that involved change the table in the FROM clause. Any ideas or help would be greatly appreciated!
Thanks in advance!

As the previous answer, the solution will need dynamic SQL. Here is a way that uses both dynamic SQL and cursors, and you can expect slow performance, so use at your own risk:
DECLARE #TableName NVARCHAR(128), #ColumnName NVARCHAR(128)
DECLARE #Query NVARCHAR(4000)
DECLARE CC CURSOR LOCAL FAST_FORWARD FOR
SELECT QUOTENAME(t.name), QUOTENAME(c.name)
FROM sys.columns c
INNER JOIN sys.tables t
ON c.object_id = t.object_id
WHERE c.collation_name IS NOT NULL
AND c.max_length > 25 AND c.name LIKE '%gl%acc%';
CREATE TABLE #Results(TableName NVARCHAR(128), ColumnName NVARCHAR(128));
OPEN CC
FETCH NEXT FROM CC INTO #TableName, #ColumnName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #Query = 'IF EXISTS(SELECT 1 FROM '+#TableName+'
WHERE LEN('+#ColumnName+') > 25)
INSERT INTO #Results
VALUES(#TableName,#ColumnName)'
EXEC sp_executesql #Query,
N'#TableName NVARCHAR(128),#ColumnName NVARCHAR(128)',
#TableName,
#ColumnName;
FETCH NEXT FROM CC INTO #TableName, #ColumnName
END
CLOSE CC
DEALLOCATE CC
SELECT *
FROM #Results

Here's an option without cursors that also doesn't add XML overhead. Note that it also protects you from potential type conflicts (e.g. try the others in a database with hierarchyid columns, like AdventureWorks), from table or column names with apostrophes, and from table names that exist in more than one schema.
DECLARE #sql NVARCHAR(MAX) = N'';
CREATE TABLE #Results
(
SchemaName NVARCHAR(128), TableName NVARCHAR(128), ColumnName NVARCHAR(128)
);
SELECT #sql += N'INSERT #Results SELECT '''
+ REPLACE(s.name,'''','''''') + ''','''
+ REPLACE(t.name,'''','''''') + ''','''
+ REPLACE(c.name,'''','''''') + '''
WHERE EXISTS (SELECT 1 FROM ' + QUOTENAME(s.name)
+ '.' + QUOTENAME(t.name) + ' WHERE
LEN(' + QUOTENAME(c.name) + ') > 25);
'
FROM sys.columns AS c
INNER JOIN sys.tables AS t
ON c.[object_id] = t.[object_id]
INNER JOIN sys.schemas AS s
ON t.[schema_id] = s.[schema_id]
WHERE
(
c.system_type_id IN (35,99) -- text,ntext
OR (c.system_type_id IN (167,231) -- varchar,nvarchar, could be max
AND c.max_length > 25 OR c.max_length = -1)
OR (c.system_type_id IN (175,239) -- char, nchar
AND c.max_length > 25)
)
AND c.name LIKE N'%gl%acc%';
EXEC sp_executesql #sql;
SELECT SchemaName, TableName, ColumnName FROM #Results;

Yet another solution with dynamic SQL.
But now without cursors. It uses FOR XML statement and should be much faster.
DECLARE #sqlstatement VARCHAR(MAX);
SET #sqlstatement =
REPLACE (
STUFF ( (
SELECT 'UNION ALL SELECT ''' + t.name + ''' as TableName, '''
+ c.name + ''' AS ColumnName, '
+ c.name + ' AS Value FROM '
+ t.name + ' WHERE LEN (' + c.name + ') ' + CHAR(62) + ' 25'
FROM sys.columns c
INNER JOIN sys.tables t ON c.object_id = t.object_id
WHERE c.name LIKE '%gl%acc%'
FOR XML PATH('')
), 1, 10, '')
, '>', '>')
EXEC (#sqlstatement)
You may want to add extra filter for columns by their type and max_length:
INNER JOIN sys.types ty ON c.system_type_id = ty.system_type_id
AND (
ty.name IN ('text', 'ntext')
OR (
ty.name IN ('varchar', 'char', 'nvarchar', 'nchar')
AND (c.max_length > 25 OR c.max_length = -1)
)

You will need to create dynamic SQL because you cannot dynamically specify the source table. You could do this using a cursor, or write a select statement that makes a row for each statement you need to run. This shows how to do it with a cursor. You problem looks like an acceptable usage for a cursor:
DECLARE #ColName VARCHAR(MAX);
DECLARE #TableName VARCHAR(MAX);
DECLARE #SomeSQL VARCHAR(MAX);
DECLARE db_cursor CURSOR FOR
SELECT c.name AS ColName, t.name AS TableName
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE c.name LIKE '%gl%acc%'
OPEN db_cursor;
FETCH NEXT FROM db_cursor INTO #ColName, #TableName;
WHILE ##FETCH_STATUS = 0
BEGIN
-- you need to make dynamic SQL
SELECT #SomeSQL = 'SELECT * FROM ' + #TableName + ' WHERE LEN(' + #ColName + ') > 25;'
PRINT(#SomeSQL + CHAR(10));
-- you could execute it directly if you wish.
--EXEC (#SomeSQL);
FETCH NEXT FROM db_cursor INTO #ColName, #TableName;
END
CLOSE db_cursor;
DEALLOCATE db_cursor;

I wasn't sure if you needed to do anything with the results, but this will return the records that meet the criteria you posted in your question
Declare #TableName sysname
Declare #ColName sysname
Declare #dynamic_SQL varchar(MAX)
Declare some_cursor CURSOR FOR
SELECT c.name AS ColName, t.name AS TableName
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE c.name LIKE '%gl%acc%'
OPEN some_cursor
FETCH NEXT FROM some_cursor INTO #ColName, #TableName
WHILE ##FETCH_STATUS = 0
Begin
select #dynamic_SQL = '
Select *
From ' + #TableName + '
Where LEN('+ #ColName +') > 25
'
exec (#dynamic_SQL)
FETCH NEXT FROM some_cursor INTO #ColName, #TableName
End
CLOSE some_cursor
DEALLOCATE some_cursor

Related

What SQL Server dynamic SQL or other operations like cursors remove NULL from a column?

I am trying to do a count(*) on a column name in a dynamic sql cursor but get the following error:
Warning: Null value is eliminated by an aggregate or other SET operation.
See previous post for more info: Previous SO Post
dynamic sql is in here:
SET #sql = N'
-- Variables
DECLARE #MyTable VARCHAR(50)
DECLARE #ColName VARCHAR(50)
DECLARE #MyColCount INT = 0
SET #MyTable = ''' + #myTableNameFromDynamicSQL + '''
DECLARE TheCur CURSOR
FOR SELECT name
FROM sys.columns
WHERE object_id = OBJECT_ID(#MyTable)
AND name <> N''MyID'';
OPEN TheCur
FETCH NEXT FROM TheCur INTO #ColName
WHILE ##FETCH_STATUS = 0
IF #ColName <> N''MyID''
BEGIN
IF #ColName = ''AColumnInQuestion''
BEGIN
SET #MyColCount = (SELECT count(*) as MyColCount FROM ' + #myTableNameFromDynamicSQL + ')
PRINT #MyColCount
PRINT #ColName
END
...'
The particular error you refer to is not an error. It's a warning, and in most cases can be safely ignored.
Be that as it may, it seems (although it's rather unclear) that you are trying to get the number of nulls in each column in the table. If so, you are going the wrong way about it.
You don't need a cursor, and your dynamic SQL is all wrong (most of should have been outside the dynamic part), and is also missing escaping/quoting. Instead, the script below will give you the number of nulls in each nullable column. It generates this using STRING_AGG over all the columsn at once.
DECLARE #schema sysname = 'dbo';
DECLARE #table sysname = 'YourTable';
DECLARE #sql nvarchar(max) = N'
SELECT ' + (
SELECT STRING_AGG('
COUNT(*) - COUNT(' + QUOTENAME(c.name) + ') AS ' + QUOTENAME(c.name), ','
)
FROM sys.columns c
JOIN sys.tables t ON t.object_id = c.object_id
JOIN sys.schemas s ON s.schema_id = t.schema_id
WHERE t.name = #table
AND s.name = #schema
AND c.name <> N'MyID'
AND c.is_nullable = 1
) + '
FROM ' + QUOTENAME(#schema) + '.' + QUOTENAME(#table);
PRINT #sql; -- your friend
EXEC sp_executesql #sql;
db<>fiddle

How to get Max Date Value of Date column in Multiple tables

For example I have 2 tables in Database.
Ex :
Table T :
declare #t table (name varchar(20),DOB date)
Insert into #t (name,DOB) values ('Mohan','2001-07-19')
Insert into #t (name,DOB) values ('Minu','1998-06-19')
Table : TT
declare #tt table (name varchar(20),DOB date)
Insert into #tt (name,DOB) values ('Raju','2010-07-19')
Insert into #tt (name,DOB) values ('Rani','2001-06-19')
Now I have a Query to get Table name and column names of multiple tables basing on Date type filter .
SELECT C.TABLE_SCHEMA, c.TABLE_NAME,c.COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS c
JOIN sys.objects o ON o.name = c.TABLE_NAME
WHERE o.type = 'U' AND C.DATA_TYPE = 'Datetime'
Output :
T_Schema T_name T_column
dbo T DOB
dbo TT DOB
But how can I get max Date of both tables like below output :
T_Schema T_name T_column Max_dt
dbo T DOB 2001-07-19
dbo TT DOB 2010-07-19
Suggest me the best way to achieve this.
Here is another option that doesn't use a cursor. I doubt it will be any better from a performance perspective because you still need a subquery for every row. But I really hate cursors. I also used the system tables instead of the information schema views as those can sometimes be a bit odd. https://sqlblog.org/2011/11/03/the-case-against-information_schema-views
declare #SQL nvarchar(max) = N''
select #SQL = #SQL +
N'select SCHEMA_NAME = ''' + QUOTENAME(s.name) + ''', TABLE_NAME = '''
+ QUOTENAME(o.name) + ''', COLUMN_NAME = '''
+ QUOTENAME(c.name) + ''', MaxDate = '
+ '(select MAX(' + QUOTENAME(c.name) + ') from ' + QUOTENAME(s.name) + '.' + QUOTENAME(o.name) + ') UNION ALL '
from sys.columns c
join sys.systypes st on st.type = c.system_type_id
join sys.objects o on o.object_id = c.object_id and o.type = 'U'
join sys.schemas s on s.schema_id = o.schema_id
where st.name = 'datetime'
order by s.name
, o.name
, c.name
set #SQL = left(#SQL, len(#SQL) - 10) --removes final UNION ALL
select #SQL
--uncomment below when you are satisfied the dynamic sql is correct
--exec sp_executesql #SQL
Here's some dynamic SQL that should do what you want, via a cursor.
I'd caution using this if you have a lot of tables, or run this in test first. Cursors are not great performs generally speaking. You can run this against a system database, like master, which would have fewer values to see how it works.
create table #MaxDate (tname varchar(256), cname varchar(256), mdate datetime)
declare cur cursor local fast_forward
for
SELECT C.TABLE_SCHEMA, c.TABLE_NAME,c.COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS c
JOIN sys.objects o ON o.name = c.TABLE_NAME
WHERE o.type = 'U' AND C.DATA_TYPE = 'Datetime'
declare #schema varchar(64), #table varchar(256), #column varchar(256)
declare #sql varchar(max)
open cur
fetch next from cur into #schema, #table, #column
while ##FETCH_STATUS = 0
begin
set #sql = 'select ''' + #table + '''' + ',''' + '' + #column + '''' + ',' + 'max(' + #column + ') from ' + #schema + '.' + #table
print #sql
insert into #MaxDate
exec (#sql)
fetch next from cur into #schema, #table, #column
end
close cur
deallocate cur
select * from #MaxDate
drop table #MaxDate
Here's an answer using a cursor, dynamic SQL and a temporary table:
DECLARE table_cursor CURSOR LOCAL FAST_FORWARD FOR
SELECT
C.TABLE_SCHEMA,
c.TABLE_NAME,
c.COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS c
JOIN sys.objects o
ON o.name = c.TABLE_NAME
WHERE o.type = 'U'
AND C.DATA_TYPE = 'Datetime'
DECLARE #schema SYSNAME
DECLARE #table SYSNAME
DECLARE #column SYSNAME
DECLARE #sql NVARCHAR(1000)
CREATE TABLE #Data (SchemaName SYSNAME, TableName SYSNAME, ColumnName SYSNAME, MaxDate DATETIME)
OPEN table_cursor
FETCH NEXT FROM table_cursor INTO #schema, #table, #column
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = 'INSERT INTO #Data (SchemaName, TableName, ColumnName, MaxDate) SELECT '''+#schema+''', '''+#table+''', '''+#column+''', MAX(['+#column+']) FROM ['+#schema+'].['+#table+']'
EXEC sp_executesql #sql
FETCH NEXT FROM table_cursor INTO #schema, #table, #column
END
CLOSE table_cursor
DEALLOCATE table_cursor
SELECT * FROM #Data
DROP TABLE #Data
SQL to BUILD SQL for you
DECLARE #SQL as nvarchar(max) ='';
select #SQL = #SQL + 'SELECT ' + Column_Name + ' adate, ''' + Column_Name + ''' colname, ''' + Table_name + ''' tabname FROM ' + Table_name + ' UNION ' FROM INFORMATION_SCHEMA.COLUMNS where data_type like '%date%'
select #SQL = 'SELECT TOP 100 * FROM (' + LEFT(#SQL, LEN(#SQL) -6) + ') IQ WHERE IQ.adate IS NOT null ORDER BY IQ.adate DESC';
--cut n paste the sql below, see what it does for you
select #SQL
mark II - executes sql for you, and sorts out names with spaces in them
DECLARE #SQL as nvarchar(max) ='';
select #SQL = #SQL + 'SELECT [' + Column_Name + '] adate, ''' + Column_Name + ''' colname, ''' + Table_name + ''' tabname FROM [' + Table_name + '] UNION ' FROM INFORMATION_SCHEMA.COLUMNS where data_type like '%date%'
select #SQL = 'SELECT TOP 100 * FROM (' + LEFT(#SQL, LEN(#SQL) -6) + ') IQ WHERE IQ.adate IS NOT null ORDER BY IQ.adate DESC';
select #SQL;
EXEC sp_executesql #sql;

How to iterate through tables in a schema with Dynamic Sql

I have a database of tables, and i want to iterate through each table in 3 different schema, one schema at a time.
I figure that i'll need something along the lines of :
DECLARE #tableName varchar(50)
DECLARE #schemaName varchar(50)
For now lets call the schemas "A" "B" and "C".
I can get a list of the tables from each schema using :
SELECT t.name
FROM sys.tables AS t
INNER JOIN sys.schemas AS s
ON t.[schema_id] = s.[schema_id]
WHERE s.name = N'schema_name';
but im not sure how to iterate through that list (id like to insert values in to every table one by one, and they are dependent on the datatype of the columns of the tables so i cant just do a blanket insert into all statement).
DECLARE #SchemaName SYSNAME = 'dbo'
DECLARE #TableName SYSNAME
--note sysname is the same thing as NVARCHAR()
DECLARE CursorName CURSOR FOR
SELECT t.name
FROM sys.tables AS t
INNER JOIN sys.schemas AS s
ON t.[schema_id] = s.[schema_id]
WHERE s.name = #SchemaName;
OPEN CursorName
FETCH NEXT FROM CursorName
INTO #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #Columns NVARCHAR(MAX)
SET #Columns =
STUFF(
(SELECT
',' + QUOTENAME(name)
FROM
sys.columns
WHERE
object_id = OBJECT_ID(QUOTENAME(#SchemaName) + '.' + QUOTENAME(#TableName))
FOR XML PATH(''))
,1,1,'')
DECLARE #SQL AS NVARCHAR(MAX)
SET #SQL = 'INSERT INTO ' + QUOTENAME(#SchemaName) + '.' + QUOTENAME(#TableName) +
' (' + #Columns + ') VALUES (' +
'YOU MAKE REST DEPENDING ON YOUR NEEDS'
--use print to view and copy your dynamic sql string to see if you have formed it correctly
PRINT #SQL
--EXECUTE (#SQL)
FETCH NEXT FROM CursorName
INTO #TableName
END
CLOSE CursorName
DEALLOCATE CursorName

How can I easily convert all ntext fields to nvarchar(max) in SQL query?

I would love to be able to write a SQL query, without enumerating the columns, which will return me all the columns, and any ntext column converted to varchar(max). I was wondering if there is a clever way to do this.
This would be great because then I could do the comparison based operators such as UNION, EXCEPT etc. on such queries. The netxt column is not comparable so it fails when using those operators.
My current idea:
Create a function to build the query as dynamic sql. Something similar to this: http://lotsacode.wordpress.com/2010/03/23/sql-server-ntext-cannot-be-selected-as-distinct/
Is there a better way?
Thanks for your input!
NTEXT will be removed from future versions of SQl-Server anyway (along with Image and text), so why not just bite the bullet and change your columns to NVARCHAR(MAX)? It may be costly once, but it will probably be worth it:
ALTER TABLE dbo.T ALTER COLUMN NTextColumn NVARCHAR(MAX) NULL; -- OR NOT NULL
You can generate and execute the script for an entire database using this:
DECLARE #SQL NVARCHAR(MAX) =
( SELECT 'ALTER TABLE ' + QUOTENAME(OBJECT_SCHEMA_NAME(object_id)) + '.' +
QUOTENAME(OBJECT_NAME(object_id)) +
' ALTER COLUMN ' + QUOTENAME(Name) +
' NVARCHAR(MAX) ' +
CASE WHEN is_nullable = 0 THEN 'NOT' ELSE '' END +
' NULL;' + CHAR(13) + 'GO' + CHAR(13)
FROM sys.columns
WHERE system_type_id = 99 --NTEXT
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)');
EXECUTE sp_executesql #SQL;
I used this cursor (and here, there's no set-based alternative, I'm afraid) to do just that:
DECLARE TableCursor CURSOR FAST_FORWARD
FOR
SELECT
t.Name,
c.name,
c.is_nullable,
typ.user_type_id
FROM
sys.columns c
INNER JOIN
sys.tables t ON c.object_id = t.object_id
INNER JOIN
sys.types typ ON c.system_type_id = typ.system_type_id
WHERE
typ.name IN ('text', 'ntext') -- user_type_id: text = 35, ntext = 99
DECLARE #TableName sysname, #ColumnName sysname, #IsNullable BIT, #TypeID INT
OPEN TableCursor
FETCH NEXT FROM TableCursor INTO #TableName, #ColumnName, #IsNullable, #TypeID
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #Stmt NVARCHAR(999)
SET #Stmt = 'ALTER TABLE dbo.[' + #TableName + '] ALTER COLUMN [' + #ColumnName + '] ' +
CASE #TypeID
WHEN 35 THEN ' VARCHAR(MAX) '
WHEN 99 THEN ' NVARCHAR(MAX) '
END +
CASE WHEN #IsNullable = 1 THEN 'NULL' ELSE 'NOT NULL' END
PRINT #Stmt
EXEC (#Stmt)
FETCH NEXT FROM TableCursor INTO #TableName, #ColumnName, #IsNullable, #TypeID
END
CLOSE TableCursor
DEALLOCATE TableCursor
I simplified my code a bit by assuming that all my tables are in the dbo schema - if that's not the case for you, you'd have to include the schema from the sys.schema catalog view, too.
Running this code will turn all text into varchar(max) and all ntext into nvarchar(max) once and for all, and all your issues with text/ntext are gone forever! :-)
Here is my modified version of GarethD's answer above. Had issues with SQL not finding some tables, so I used sys.tables joined with sys.columns. Also, the is_nullable line was incorrect (if the field is not nullable, then you set it to NOT NULL).
DECLARE #SQL NVARCHAR(MAX) = ' ';
SELECT #SQL = #SQL + ' ALTER TABLE ' + QUOTENAME(OBJECT_SCHEMA_NAME(sys.columns.object_id)) + '.' +
QUOTENAME(OBJECT_NAME(sys.columns.object_id)) +
' ALTER COLUMN ' + QUOTENAME(sys.columns.Name) +
' NVARCHAR(MAX) ' +
CASE WHEN is_nullable = 0 THEN 'NOT NULL' ELSE '' END
FROM sys.Tables
inner join sys.columns on sys.tables.object_id = sys.columns.object_id
WHERE sys.columns.system_type_id = 99 ; --NTEXT
EXECUTE sp_executesql #SQL;
GO

Dynamic update statement with variable column names

We're looking to do an update in several SQL Server databases to change all NULL values in a certain table to be empty strings instead of NULL. We're potentially going to be doing this across hundreds of databases. The table name will always be the same, but the column names are variable based on how the front-end application is configured (don't judge... I didn't create this system).
Is there a way to do an update on all of these columns without knowing the column names ahead of time?
You can pass the name of the column in dynamic sql:
declare #sql nvarchar (1000);
set #sql = N'update table set ' + #column_name + '= ''''';
exec sp_executesql #sql;
You can look in the sys.columns table and join on the table name or object_id.
DECLARE #OBJ_ID INT
SELECT #OBJ_ID = OBJECT_ID
FROM SYS.tables
WHERE name = 'YOURTABLE'
SELECT * FROM SYS.columns
WHERE OBJECT_ID = #OBJ_ID
You could use the name field from the sys.columns query as a basis to perform the update on.
Assuming you want all columns of varchar/char types only (or change the type filter to whatever you need):
DECLARE #tableName varchar(10)
SET #tableName = 'yourtablenamehere'
DECLARE #sql VARCHAR(MAX)
SET #sql = ''
SELECT #sql = #sql + 'UPDATE ' + #tableName + ' SET ' + c.name + ' = '''' WHERE ' + c.name + ' IS NULL ;'
FROM sys.columns c
INNER JOIN sys.tables t ON c.object_id = t.object_id
INNER JOIN sys.types y ON c.system_type_id = y.system_type_id
WHERE t.name = #tableName AND y.name IN ('varchar', 'nvarchar', 'char', 'nchar')
EXEC (#sql)
This can be achieved with cursors. You first select the column names like #Darren mentioned, then you Set a Cursor with those values and loop:
Open oColumnsCursor
Fetch Next From oColumnscursor
Into #ColumnName
While ##FETCH_STATUS=0
Begin
Set #oQuery = 'Update [DB]..[Table] Set [' + #ColumnName + '] = ''NewValue'' Where [' + #ColumnName + '] = ''OldValue'''
Execute(#oQuery)
Fetch Next From oColumnscursor Into #ColumnName
Set #oCount = #oCount + 1
End
Close oColumnsCursor;
Deallocate oColumnsCursor;
This will work when you know the Table Name:
DECLARE #tableName varchar(10)
SET #tableName = 'Customers'
DECLARE #sql VARCHAR(MAX)
SET #sql = ''
SELECT #sql = #sql + 'UPDATE ' + #tableName + ' SET ' + c.name + ' = ISNULL('+ c.name +','''');'
FROM sys.columns c
INNER JOIN sys.tables t ON c.object_id = t.object_id
INNER JOIN sys.types y ON c.system_type_id = y.system_type_id
WHERE y.name IN ('varchar', 'nvarchar', 'char', 'nchar')
AND t.name = #tableName;
EXEC(#sql);
And this will iterate all Tables and all Columns in a Db:
DECLARE #sql VARCHAR(MAX)
SET #sql = ''
SELECT #sql = #sql + 'UPDATE ' + t.name + ' SET ' + c.name + ' = ISNULL('+ c.name +','''');'
FROM sys.columns c
INNER JOIN sys.tables t ON c.object_id = t.object_id
INNER JOIN sys.types y ON c.system_type_id = y.system_type_id
WHERE y.name IN ('varchar', 'nvarchar', 'char', 'nchar');
EXEC(#sql);
Below is the procedure.
ALTER PROCEDURE [dbo].[util_db_updateRow]
#colval_name NVARCHAR (30), -- column and values e.g. tax='5.50'
#idf_name NVARCHAR (300), -- column name
#idn_name NVARCHAR (300), -- column value
#tbl_name NVARCHAR (100) -- table name
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(MAX)
-- construct SQL
SET #sql = 'UPDATE ' + #tbl_name + ' SET ' + #colval_name +
' WHERE ' + #idf_name + '=' + #idn_name;
-- execute the SQL
EXEC sp_executesql #sql
SET NOCOUNT OFF
RETURN
END
Below is the stored procedure where you can pass Schema Name, Table Name and list of column names separted by comma.It works only in Sql Server 2016 or higher.
CREATE OR ALTER PROCEDURE UpdateData
(#SchemaName NVARCHAR(Max),#TableName NVARCHAR(MAX),#ColumnNames NVARCHAR(MAX))
AS
BEGIN
DECLARE #DynamicSql NVARCHAR(MAX);
SET #DynamicSql = 'UPDATE ' +'[' +#SchemaName+'].' + '[' +#TableName+']' +' SET ' + STUFF((SELECT ', [' + C.name + '] = ' + '''NEW_VALUE'''
FROM sys.columns C
INNER JOIN sys.tables T ON T.object_id = C.object_id
INNER JOIN sys.schemas S ON T.schema_id = S.schema_id
WHERE
T.name = #TableName
AND S.Name = #SchemaName
AND [C].[name] in (SELECT VALUE FROM string_split(#ColumnNames,','))
FOR XML PATH('')), 1,1, '')
print #DynamicSql;
EXEC (#DynamicSql);
END