select columns with value NA - sql

How to select columns in a table that only contain a specific value for all the rows? I am trying to find these columns to do an update on those values with a NULL value. In my columns I have varied range of values including NA
I am using SQL Server 2012.
I've tried doing: thsi only gives me column names. Can i add to this condition for columns with value 'NA'?
SELECT COLUMN_NAME AS NAMES,COLUMN_DEFAULT
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = 'ABC'
I am a beginner in SQL. Trying to figure out how to do this.

If min of column equals to max then that column contains same values:
Select
case when min(col1) = max(col1) then 1 else 0 end as Col1IsSame,
case when min(col2) = max(col2) then 1 else 0 end as Col2IsSame,
...
from Table
With dynamic query:
declare #s nvarchar(max) = 'select '
select #s = #s + 'case when min(' + COLUMN_NAME + ') = max(' +
COLUMN_NAME + ') then 1 else 0 end as ' + COLUMN_NAME + ','
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = 'Table'
Set #s = substring(#s, 1, len(#s) - 1) + ' from Table'
exec(#s)

TRY THIS QUERY
DECLARE #SQLQUERY NVARCHAR(MAX)
declare #tableName varchar(50)
DECLARE #NAME VARCHAR(50)
Declare #ParamDefinition AS NVarchar(2000)
Set #ParamDefinition = '#OIM VARCHAR(20)'
SELECT NAME
FROM sys.objects
WHERE [object_id]=#OIM
set #tableName= (SELECT NAME
FROM sys.objects
WHERE [object_id]=#OIM)
SET #NAME=(SELECT C.NAME
FROM sys.columns c
JOIN
sys.tables t ON c.object_id = t.object_id
WHERE c.name in (select distinct name
from sys.columns
where object_id=#OIM))
SET #SQLQUERY = ''
SELECT #SQLQUERY = #SQLQUERY + 'UPDATE ' + #tableName + ' SET ' + #NAME + ' = NULL WHERE ' + #NAME + ' = NA ; '
PRINT #SQLQUERY
Execute sp_Executesql #SQLQUERY , #ParamDefinition, #OIM
end

Related

SQL count distinct or not null for each column for many columns

I need to analyze a large table with hundreds of columns. A lot of columns are unused.
To investigate I could do something like
SELECT DISTINCT Column1
FROM myTable
or
WITH C AS
(
SELECT DISTINCT Column1
FROM MyTable
)
SELECT COUNT(*)
FROM C
Then I do the same for column2 and so on. However these queries only work for one column which is time consuming and does not give overview in one glance.
Any idea how to build such investigation query for all columns in one?
You need only 1 query where you have to list all the columns of the table:
SELECT COUNT(DISTINCT Column1) column1_count,
COUNT(DISTINCT Column2) column2_count,
COUNT(DISTINCT Column3) column3_count
.....................................
FROM MyTable;
For local purposes only, you can make it dynamic like this:
Get the columns of the table
the query is created as the colleagues did and then it is executed with the EXEC()
DECLARE #columns as Table(RowId INT IDENTITY(1,1), ColumnName nVarchar(50))
DECLARE #ii int = 0
DECLARE #max int = 0
DECLARE #sqlQuery nVarchar(MAX)
INSERT INTO #columns
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = N'Customer'
SET #sqlQuery = 'SELECT '
SELECT #max = COUNT(*) FROM #columns
WHILE #ii <= #max
BEGIN
SELECT #sqlQuery = CONCAT(#sqlQuery,'COUNT(DISTINCT ',ColumnName,') ',LOWER(ColumnName),'_count, ')
FROM #columns
WHERE RowId = #ii
SET #ii = #ii + 1
END
SELECT #sqlQuery = CONCAT(#sqlQuery,'FROM Customer')
SELECT #sqlQuery = REPLACE(#sqlQuery,', FROM',' FROM')
select #sqlQuery
EXEC (#sqlQuery)
You should flesh out your requirement a bit more. If all you want to know is if a column contains only NULLs, you'll want to check for max(ColumnName) is null
declare #sql table (id int identity(1,1), QueryString nvarchar(max))
create table ##emptyColumns (emptyColumn nvarchar(128))
declare #i int = 0
declare #iMax int
declare #runthis nvarchar(max)
insert #sql
select 'select ''' + QUOTENAME(s.name) + '.' + QUOTENAME(o.name) + quotename(c.name) + ''' as ''column''
from ' + QUOTENAME(s.name) + '.' + QUOTENAME(o.name) + '
having max(' + c.name + ') is null'
from sys.sysobjects o
inner join sys.syscolumns c on c.id = o.id
inner join sys.schemas s on s.schema_id = o.uid
where o.type = 'U'
order by s.name
, o.name
, c.colorder
select #iMax = count(*)
from #sql
print #iMax
while #i < #iMax
begin
set #i = #i + 1
select #runthis = 'insert into ##emptyColumns
' + QueryString
from #sql
where id = #i
execute sp_executesql #runthis
end
select *
from ##emptyColumns
drop table ##emptyColumns
One further option you might consider:
declare #sql nvarchar(max)
select #sql = isnull(#sql + ' union all ', '') + 'select ''' + COLUMN_NAME + ''',
sum(case when ' + COLUMN_NAME + ' is null then 1 else 0 end) as null_values,
count(distinct ' + COLUMN_NAME + ') as count_distinct
from ' + TABLE_SCHEMA + '.' + TABLE_NAME + '
'
from information_schema.columns
where TABLE_SCHEMA = 'MySchema' and TABLE_NAME = 'MyTable'
exec (#sql)
If you had very big tables with large numbers of columns and were only interested in empty columns you could look into something like checksum_agg(checksum(column_name)). It may help improve performance.
You'd need to be wary of column data types, as they are not all compatible with distinct.

Loop through multiple Float columns in a DB using Dynamic SQL

How do I loop through multiple columns from different tables in a database?. I am finding the result I need by querying one column at a time which takes lot of time.
SELECT MAX
(CASE Charindex('.', COLUMN1)
WHEN 0 THEN 0
ELSE
Len (Cast(Cast(Reverse(CONVERT(VARCHAR(50), COLUMN1, 128)
) AS FLOAT) AS BIGINT))
END) AS MAX_LENGTH
FROM DB1.dbo.TABLE1
My code is here but it won't work because I am sending more than one value
DECLARE #cols NVARCHAR(MAX)
,#sql NVARCHAR(MAX)
SET #cols = (SELECT quotename(COLUMN_NAME) FROM BRSDATA.INFORMATION_SCHEMA.COLUMNS A
inner join BRSDATA.INFORMATION_SCHEMA.TABLES B
on A.TABLE_NAME = B.TABLE_NAME
where TABLE_TYPE = 'BASE TABLE'
and DATA_TYPE = 'Float')
Set #sql = 'SELECT MAX(CASE Charindex(''.'',' + #cols + ')
WHEN 0 THEN 0
ELSE
Len (Cast(Cast(Reverse(CONVERT(VARCHAR(50), ' + #cols + ', 128)
) AS FLOAT) AS BIGINT))
END) AS MAX_LENGTH'
print(#sql)
Msg 512, Level 16, State 1, Line 101
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
I also tried using a cursor and the 'query executed successfully' but I don't see the results even when not closing and deallocating the cursor.
DECLARE #SchemaName SYSNAME = 'dbo'
DECLARE #TableName SYSNAME
DECLARE #ColumnName SYSNAME
DECLARE FLOAT_COLUMNS CURSOR FOR
SELECT t.name,c.name
FROM BRSDATA.sys.tables AS t
INNER JOIN BRSDATA.sys.schemas AS s
ON t.[schema_id] = s.[schema_id]
inner join BRSDATA.sys.columns AS c
on c.object_id = t.object_id
WHERE s.name = 'dbo'
and type_desc = 'USER_TABLE';
OPEN FLOAT_COLUMNS
FETCH NEXT FROM FLOAT_COLUMNS
INTO #TableName, #ColumnName
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #Columns NVARCHAR(MAX)
SET #Columns =
STUFF(
(SELECT
',' + QUOTENAME(name)
FROM
sys.columns
WHERE
object_id = OBJECT_ID(QUOTENAME(#TableName) + '.' + QUOTENAME(#ColumnName))
FOR XML PATH(''))
,1,1,'')
DECLARE #SQL AS NVARCHAR(MAX)
SET #SQL = 'SELECT' + QUOTENAME(#TableName) + ',' + QUOTENAME(#ColumnName) + ', MAX(CASE Charindex(''.'',' + #Columns + ')
WHEN 0 THEN 0
ELSE
Len (Cast(Cast(Reverse(CONVERT(VARCHAR(50), ' + #Columns + ', 128)
) AS FLOAT) AS BIGINT))
END) AS MAX_LENGTH'
--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 FLOAT_COLUMNS
INTO #TableName, #ColumnName
END
CLOSE FLOAT_COLUMNS
DEALLOCATE FLOAT_COLUMNS
not sure if this is what you need,but this is how i would do it:
DECLARE #cols NVARCHAR(MAX)
,#table NVARCHAR(MAX)
,#sql NVARCHAR(MAX)
,#X INT = 1
WHILE #X < (
SELECT MAX(ROWID) FROM
(
SELECT quotename(COLUMN_NAME) col
,ROW_NUMBER()over(ORDER BY COLUMN_NAME) ROWID
FROM INFORMATION_SCHEMA.COLUMNS A
inner join INFORMATION_SCHEMA.TABLES B
on A.TABLE_NAME = B.TABLE_NAME
where TABLE_TYPE = 'BASE TABLE'
and DATA_TYPE = 'Float'
) AS X
WHERE ROWID < 10
)
BEGIN
SET #cols =
(SELECT col FROM
(
SELECT quotename(COLUMN_NAME) col
,ROW_NUMBER()over(ORDER BY COLUMN_NAME) ROWID
FROM INFORMATION_SCHEMA.COLUMNS A
inner join INFORMATION_SCHEMA.TABLES B
on A.TABLE_NAME = B.TABLE_NAME
where TABLE_TYPE = 'BASE TABLE'
and DATA_TYPE = 'Float'
) AS X
WHERE ROWID = #X
)
SET #table =
(SELECT tablename FROM
(
SELECT quotename(A.TABLE_NAME) tablename
,ROW_NUMBER()over(ORDER BY COLUMN_NAME) ROWID
FROM INFORMATION_SCHEMA.COLUMNS A
inner join INFORMATION_SCHEMA.TABLES B
on A.TABLE_NAME = B.TABLE_NAME
where TABLE_TYPE = 'BASE TABLE'
and DATA_TYPE = 'Float'
) AS X
WHERE ROWID = #X
)
Set #sql = 'SELECT MAX(CASE Charindex(''.'',' + #cols + ')
WHEN 0 THEN 0
ELSE
Len (Cast(Cast(Reverse(CONVERT(VARCHAR(50), ' + #cols + ', 128)
) AS FLOAT) AS BIGINT))
END) AS MAX_LENGTH
,MAX(CASE Charindex(''.'',' + #table + ')
WHEN 0 THEN 0
ELSE
Len (Cast(Cast(Reverse(CONVERT(VARCHAR(50), ' + #table + ', 128)
) AS FLOAT) AS BIGINT))
END) AS MAX_LENGTH'
PRINT(#sql)
SET #X = #X +1
END
I believe this script can help you :
DECLARE #Query TABLE (SchemaName VARCHAR(100)
,TableName VARCHAR(100)
,ColumnName VARCHAR(1000)
)
INSERT INTO #Query
SELECT A.TABLE_SCHEMA,A.TABLE_NAME,' MAX(CASE Charindex(''.'',' + QUOTENAME(COLUMN_NAME) + ')
WHEN 0 THEN 0
ELSE Len (Cast(Cast(Reverse(CONVERT(VARCHAR(50), ' + QUOTENAME(COLUMN_NAME) + ', 128) ) AS FLOAT) AS BIGINT))
END) AS ['+COLUMN_NAME+'_MAX_LENGTH]'
FROM INFORMATION_SCHEMA.COLUMNS A
INNER JOIN INFORMATION_SCHEMA.TABLES B
ON A.TABLE_NAME = B.TABLE_NAME
WHERE TABLE_TYPE = 'BASE TABLE'
AND DATA_TYPE = 'FLOAT'
SELECT 'SELECT '+
STUFF((
SELECT ',' + ColumnName
FROM #Query Q2
WHERE Q1.SchemaName=q2.SchemaName AND Q1.TableName=q2.TableName
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 2, '')
+' FROM '+Q1.SchemaName+'.'+Q1.TableName
FROM
(SELECT DISTINCT SchemaName,TableName FROM #Query) Q1
use BRSDATA;
begin try drop table #temp end try begin catch end catch ;
DECLARE #TableName nvarchar(250);
DECLARE #ColumnName nvarchar(250);
declare #x int;
DECLARE #FLOATV as float = 3.141592653;--searching for this in all float vals in all tables
SELECT t.name,c.name colname
into #temp
FROM sys.tables AS t
INNER JOIN sys.schemas AS s
ON t.[schema_id] = s.[schema_id]
inner join sys.columns AS c
on c.object_id = t.object_id
WHERE s.name = 'dbo'
and type_desc = 'USER_TABLE'
and exists(select * from sys.types where name = 'float' and system_type_id = c.system_type_id);
SET Nocount on;
DECLARE FLOAT_TAB CURSOR FOR
SELECT DISTINCT [name] from #temp;
OPEN FLOAT_TAB;
FETCH NEXT FROM FLOAT_TAB
INTO #TableName;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #SQL nvarchar(max) = '';
DECLARE #COND nvarchar(max) = '';
SET #SQL = 'SELECT ';
DECLARE COL_TAB CURSOR FOR
SELECT colname from #temp where name = #TableName;
OPEN COL_TAB;
FETCH NEXT FROM COL_TAB
INTO #ColumnName;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = #SQL + ' ' + #ColumnName + ',';
SET #COND = #COND + #ColumnName + ' = ' + cast(#floatv as nvarchar(60)) + ' OR ' ;
FETCH NEXT FROM col_tab INTO #ColumnName
END
SET #SQL = LEFT(#SQL, len(#sql) - 1) + ' FROM ' + #TableName + ' WHERE ' + LEFT(#COND, LEN(#COND) - 3);
print #sql;
EXECUTE ( #SQL);
close col_tab;
deallocate col_tab;
--waitfor delay '00:05';
FETCH NEXT FROM FLOAT_TAB into #tablename;
END
close float_tab
deallocate float_tab;

How to check a condition against all the columns of a table?

I have a table which has more than 30 columns(all are varchar). I need to list out all the columns which contains blank i.e.' ' values.
I tried using 'coalesce' but it is only for NULL.
The following query will give you all the columns in a table that might have null or '' values.
It is written so that you can run it for all tables in your database but you can limit it to a single table, as I have done for this specific example, checking a table called testingNulls:
--two variables needed for table name and column name, when looping through all tables
declare #table varchar(255), #col varchar(255), #sql varchar(max)
--this will be used to store the result, to have one result set instead of one row per each cursor cycle
if object_id('tempdb..#nullcolumns') is not null drop table #nullcolumns
create table #nullcolumns (tablename varchar(255), columnname varchar(255))
declare getinfo cursor for
select t.name tablename, c.name
from sys.tables t join sys.columns c on t.object_id = c.object_id
where t.name = 'testingnulls' --here the condition for the table name
open getinfo
fetch next from getinfo into #table, #col
while ##fetch_status = 0
begin
select #sql = 'if exists (select top 1 * from [' + #table + '] where [' + #col + '] is null or [' + #col + '] like '''' ) begin insert into #nullcolumns select ''' + #table + ''' as tablename, ''' + #col + ''' as all_nulls end'
print(#sql)
exec(#sql)
fetch next from getinfo into #table, #col
end
close getinfo
deallocate getinfo
--this should be the result you need:
select * from #nullcolumns
You can see a working example here. I hope this is what you need.
List all columns that contain a blank in some record? You'd use a query per column and collect the results with UNION ALL:
select 'COL1' where exists (select * from mytable where col1 like '% %')
union all
select 'COL2' where exists (select * from mytable where col2 like '% %')
union all
...
union all
select 'COL30' where exists (select * from mytable where col30 like '% %');
If you want like select * from [your_table_name] where [col1] = '' and [col2] = ''....., then use dynamic sql query like below.
Query
declare #sql as varchar(max);
select #sql = 'select * from [your_table_name] where '
+ stuff((
select ' and [' + [column_name] + '] = ' + char(39) + char(39)
from information_schema.columns
where table_name = 'your_table_name'
for xml path('')
)
, 1, 5, ''
);
exec(#sql);
Update
Or else if you want to list the column names which have a blank value, then you can use the below dynamic sql query.
Query
declare #sql as varchar(max);
select #sql = stuff((
select ' union all select ' + [column_name] + ' as [col1], '
+ char(39) + [column_name] + char(39) + ' as [col2]'
+ ' from your_table_name'
from information_schema.columns
where table_name = 'your_table_name'
for xml path('')
)
, 1, 11, ''
);
set #sql = 'select distinct t.col2 as [blank_cols] from(' + #sql
+ ')t
where coalesce(ltrim(rtrim(t.col1)), ' + char(39) + char(39) + ') = '
+ char(39) + char(39) + ';';
exec(#sql);
Find a demo here
But still I'm not sure that this is what you are looking out for.
you have not many choices but to specify all the columns in your where clause
WHERE COL1 = '' AND COL2 = '' AND COL3 = '' AND . . .
or you can use Dynamic SQL to form your query, but that is not an easy path to go
If you want to count number of columns having '' value in a table (not for each row) then use the following
SELECT max(CASE WHEN col1 = '' THEN 1 ELSE 0 END) +
max(CASE WHEN col2 = '' THEN 1 ELSE 0 END) +
max(CASE WHEN col3 = '' THEN 1 ELSE 0 END) +
...
FROM t
demo
I created a dynamic SQL script that you can use by providing the table name only
Here it is
declare #sql nvarchar(max)
declare #table sysname = 'ProductAttributes'
select #sql =
'select * from ' + #table + ' where ' +
string_agg('[' + name + '] = '' '' ', ' and ')
from sys.columns
where object_id = OBJECT_ID(#table)
select #sql
exec sp_executesql #sql
Unfortunately, for SQL string concatenation String_Agg function is new with SQL Server 2017
But it is also possible to use SQL XML Path to concatenate WHERE clause fragments
SELECT #sql = 'select * from ' + #table + ' where ' +
STUFF(
(
SELECT
' and ' + '[' + [name] + '] = '' '' '
from sys.columns
where object_id = OBJECT_ID(#table)
FOR XML PATH(''),TYPE
).value('.','VARCHAR(MAX)'
), 1, 5, ''
)
select #sql as sqlscript
exec sp_executesql #sql

Sql - Fetch next value to replace variable value

Hi guys my basic sql knowledge needs some help
I want to be able to replace a variable value with a value in a table and keep running the query until the end value in the table is reached
start query
DECLARE #prime_SCHEMA VARCHAR(20)
DECLARE #next_SCHEMA VARCHAR(20)
DECLARE #TABLE_name VARCHAR(100)
DECLARE #SQL VARCHAR(500)
SET #prime_SCHEMA = 'aaa'
SET #next_SCHEMA = 'bbb'
SET #TABLE = 'table1'
SET #sql = 'select top 1 * into '+#next_SCHEMA +'.'+#TABLE_name +' from '+#prime_SCHEMA +'.'+#TABLE_name +' TRUNCATE TABLE '+#next_SCHEMA +'.'+#TABLE_name
print #sql
I now want to wrap this in a Fetch and use a table called 'table_val' to update the TABLE_name value
and loop around till all the rows have been used in the table
so effectively i could end up with multiple #sql statements
thanks
Try this one -
Query:
DECLARE
#prime_schema SYSNAME = 'aaa'
, #next_schema SYSNAME = 'bbb'
DECLARE #SQL NVARCHAR(MAX)
SELECT #SQL = (
SELECT CHAR(13) + '
SELECT *
INTO [' + #next_schema + '].[' + o.name + ']
FROM [' + s.name + '].[' + o.name + ']
WHERE 1 != 1'
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 = #prime_schema
AND o.name IN ('table1', 'table2', 'table3')
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
PRINT #SQL
Output:
SELECT *
INTO [bbb].[table1]
FROM [aaa].[table1]
WHERE 1 != 1
SELECT *
INTO [bbb].[table2]
FROM [aaa].[table2]
WHERE 1 != 1
SELECT *
INTO [bbb].[table3]
FROM [aaa].[table3]
WHERE 1 != 1

SELECT REPLACE on every column

I have a table containing around 100 columns, is it to possible to do a Select Replace on every column at the same time rather than typing out each column individually, i'm trying to trim the '"' of each field in the table.
SELECT
REPLACE(*, '"', '')
DECLARE #tablename nvarchar(100)= 'Test'
DECLARE #col nvarchar(max)
SELECT #col = coalesce(#col + ',', 'select ' ) +
case when data_type in ('varchar', 'char','nvarchar', 'nchar') then
'replace('+column_name+' , ''"'', '''') '+' as [' + column_name + ']' else '[' + column_name + ']' end
FROM INFORMATION_SCHEMA.COLUMNS a
WHERE table_name = #tablename
SET #col += ' from ' + #tablename
EXEC (#col)
Since you're using SQL Server, you can retrieve the names of all columns on a table using the INFORMATION_SCHEMA, e.g.
select COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = 'yourTable'
You can then use a cursor to iterate over each column name, build some dynamic SQL and execute this using 'exec sp_executesql'.
Here's my solution:
declare #isString bit
declare #tableName nvarchar(256) = N'MyTableName'
declare #columnName nvarchar(max)
declare #sql nvarchar(max) = ''
declare c cursor local forward_only read_only for
select column_name, case when CHARACTER_SET_NAME is null then 0 else 1 end as IsString
from information_schema.COLUMNS WHERE table_name = #tableName
open c
fetch next from c into #columnName, #isString
set #sql = N'select '
declare #first bit = 1
while ##FETCH_STATUS = 0
begin
select #columnName
if #isString <> 0
begin
if #first = 0
begin
set #sql = #sql + ', '
end
set #sql = #sql + N'REPLACE(' + #columnName + ', ''"'', '''')'
set #first = 0
end
fetch next from c into #columnName, #isString
end
close c
deallocate c
set #sql = #sql + ' from ' + #tableName
exec sp_executesql #sql
Here is a recursive version:
declare #TABLE_NAME sysname = 'MyTableName'
declare #Prefix nvarchar(128) = 'REPLACE('
declare #Suffix nvarchar(128) = ', ''"'', '''')'
declare #Sql nvarchar(max)
;with Cols (TABLE_NAME, SELECT_LIST, ITERATION) as
(
select TABLE_NAME
, cast('' as nvarchar(max)) as SELECT_LIST
, 0 as ITERATION
from INFORMATION_SCHEMA.TABLES
where TABLE_NAME = #TABLE_NAME
union all
select c.TABLE_NAME
, c.SELECT_LIST
+ case when len(c.SELECT_LIST) > 0 then ', ' else '' end
+ case when i.DATA_TYPE like '%char' then #Prefix else '' end
+ cast(i.COLUMN_NAME as nvarchar(128))
+ case when i.DATA_TYPE like '%char' then #Suffix + ' as ' + cast(i.COLUMN_NAME as nvarchar(128)) else '' end
, c.ITERATION + 1
from INFORMATION_SCHEMA.COLUMNS i
join Cols c on i.TABLE_NAME = c.TABLE_NAME
where i.ORDINAL_POSITION = c.ITERATION + 1
)
select #Sql = ('select ' + a.SELECT_LIST + ' from ' + a.TABLE_NAME)
from Cols a
join (
select TABLE_NAME, max(ITERATION) as ITERATION
from Cols
group by TABLE_NAME
) as b on a.TABLE_NAME = b.TABLE_NAME
and a.ITERATION = b.ITERATION
exec (#sql)