SQL Loop through all tables and get the max value from a specific column - sql

I'm trying to create an audit table that checks the loaded date for that table.
Basically, I want to loop through all tables in the database and check for a specific column - LoadedDate and return the max value for that column for each table
SELECT TABLE_NAME
INTO #TableList
FROM INFORMATION_SCHEMA.COLUMNS
WHERE column_name = 'LoadedDate'
SELECT MAX(LoadedDate) FROM #TableName -- I guess using a cursor to loop through #TableList
in to a results table
TableName LoadedDate
Table 1 2016-06-01
Table 2 2016-07-01
Table 3 2016-06-01
and so on.

You can try this code, but it will consume some time
SELECT TABLE_NAME,TABLE_SCHEMA
INTO #TableList
FROM INFORMATION_SCHEMA.COLUMNS
WHERE column_name = 'LoadedDate'
CREATE TABLE #TempResult (TableName VARCHAR(100), MaxDate DATETIME2)
DECLARE #TableName VARCHAR(100)
,#TableSchema VARCHAR(100)
DECLARE #SqlQuery NVARCHAR(MAX)
WHILE(EXISTS(SELECT TOP(1) * FROM #TableList))
BEGIN
SELECT TOP(1) #TableName = TABLE_NAME, #TableSchema = TABLE_SCHEMA FROM #TableList
DELETE #TableList WHERE TABLE_NAME = #TableName
SET #TableName = #TableSchema +'.'+ #TableName
SET #SqlQuery = 'SELECT '''+#TableName+''' AS ''TableName'', MAX(UpdatedDate) AS MaxDate FROM '+ #TableName
INSERT INTO #TempResult
EXECUTE sp_executesql #SqlQuery
END
SELECT * from #TempResult
DROP TABLE #TableList
DROP TABLE #TempResult

Perhaps a little dynamic SQL
Select Table_Name = cast('' as varchar(150))
,LoadedDate = GetDate()
Into #TableList
Where 1=0
Declare #SQL varchar(max) = '>>>'
Select #SQL = #SQL + SQL
From (
Select Table_Name,SQL='Union All Select Table_Name='''+Table_Name+''',LoadedDate=max(LoadedDate) From ['+Table_Name+'] '
From INFORMATION_SCHEMA.COLUMNS
Where column_name Like '%UTC%' --= 'LoadedDate'
) A
Set #SQL = Replace(#SQL,'>>>Union All','Insert Into #TableList ')
Exec(#SQL)
Select * from #TempResult

Related

How can I return a distinct count of a variable for all tables in my database?

I have a SQL database with 60+ tables, almost all of which are populated with a CLIENTID field. I want to count the number of unique client IDs in each table.
The results I'm looking for are:
TABLE_NAME; CLIENTID_COUNT
dbo.HISTORY; 650
dbo.VISITS; 596
dbo.SALES; 1053
...; ...
This seems like it should be so simple but I've been playing around with cursors for hours and can't figure this one out. Please help!
IF OBJECT_ID('tempdb..#temp_RESULTS') IS NOT NULL DROP TABLE #temp_RESULTS
CREATE TABLE #TEMP_RESULTS
(
TABLENAME VARCHAR(MAX),
CLIENTCNT BIGINT
)
DECLARE #TABLENAME VARCHAR(MAX)
DECLARE #command VARCHAR(MAX)
IF OBJECT_ID('tempdb..#temp_PROCESS') IS NOT NULL DROP TABLE #temp_PROCESS
SELECT * INTO #TEMP_PROCESS FROM sys.tables
WHILE EXISTS(SELECT * FROM [#TEMP_PROCESS])
BEGIN
SET #TABLENAME = (SELECT TOP 1 [NAME] FROM [#TEMP_PROCESS])
SET #command = ('SELECT ''' + #TABLENAME + ''', COUNT(DISTINCT CLIENTID) AS CLIENTCNT FROM ' + #TABLENAME)
SELECT #command
INSERT INTO #TEMP_RESULTS
EXEC(#command)
DELETE FROM [#TEMP_PROCESS] WHERE [NAME] = #TABLENAME
END
SELECT * FROM [#TEMP_RESULTS]
Assuming the column is exactly ClientId in every table, you should be able to use this as is:
DROP TABLE IF EXISTS #clientId
CREATE TABLE #clientId
(
TableName nvarchar(1000),
ClientIdCount bigint
)
DECLARE #TableName nvarchar(1000);
DECLARE #CurrentQuery nvarchar(2000);
DECLARE result_cursor CURSOR local fast_forward FOR
SELECT DISTINCT
'['+TABLE_SCHEMA + '].[' + TABLE_NAME + ']'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
COLUMN_NAME = 'ClientId'
OPEN result_cursor
FETCH NEXT FROM result_cursor into #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #CurrentQuery = 'SELECT ''' + #TableName + ''', COUNT(DISTINCT ClientId) FROM ' + #TableName
--print #CurrentQuery
INSERT INTO
#clientId
(
TableName,
ClientIdCount
)
EXEC(#CurrentQuery)
FETCH NEXT FROM result_cursor into #TableName
END
--end loop
--clean up
CLOSE result_cursor
DEALLOCATE result_cursor
GO
SELECT
*
FROM
#clientId
You could use dynamic sql.
This will read through your system tables, find those that have a ClientID column, and build the text of a query that's in the general shape of 'Select Count(DISTINCT ClientID)' from each table.
DECLARE #SQLQuery as nvarchar(max) = ''
------------------------------------
-- GET THE TABLES THAT HAVE A CLIENTID FROM SCHEMA
SELECT #SQLQuery = #SQLQuery + qryTxt FROM (
SELECT DISTINCT 'SELECT ''' + tables.name + ''', COUNT(DISTINCT CLIENTID) FROM ' + tables.name + ' UNION ' AS qryTxt
FROM sys.columns left join sys.tables on columns.object_id = tables.object_id where columns.name = CLIENTID AND isnull(tables.name, '') <> '') subquery
------------------------------------
-- REMOVE THE LAST 'UNION' KEYWORD FROM SQLQUERY
SET #SQLQuery = left(#sqlQuery, len(#sqlQuery) - 5)
------------------------------------
-- EXECUTE
execute sp_executesql #SQLQuery
I really dislike cursors and loops. Even though this is not going to be much difference for a performance perspective I like to share how you can leverage the system tables and dynamic sql to avoid using a cursor, while loop or temp tables for something like this.
This code is literally all you need to to do this.
declare #SQL nvarchar(max) = ''
select #SQL = #SQL + 'select TableName = ''' + t.name + ''', ClientID_Count = count(distinct clientID)
from ' + QUOTENAME(t.name) + ' UNION ALL '
from sys.tables t
join sys.columns c on c.object_id = t.object_id
where c.name = 'clientID'
select #SQL = left(#SQL, len(#SQL) - 10) --removes the last UNION ALL
select #SQL
--once your comfortable the dynamic sql is correct just uncomment the line below.
--exec sp_executesql #SQL
A similar pattern to other answers here, but this is how I would tackle it:
IF OBJECT_ID('#Tables', 'U') IS NOT NULL
DROP TABLE #Tables;
SELECT ID = IDENTITY(INT, 1, 1),
SchemaName = OBJECT_SCHEMA_NAME([object_id]),
TableName = OBJECT_NAME([object_id]),
ColumnName = name,
DistinctCount = 0
INTO #Tables
FROM sys.columns
WHERE name = 'CLIENTID';
DECLARE #ID INT = 1,
#MaxID INT = (SELECT MAX(ID) FROM #Tables);
WHILE #ID < #MaxID
BEGIN;
DECLARE #SQLCommand VARCHAR(MAX);
SELECT #SQLCommand = FORMATMESSAGE('
UPDATE #Tables SET DistinctCount = (
SELECT COUNT(DISTINCT %s) FROM %s.%s
)
WHERE ID = %i;',
QUOTENAME(ColumnName), QUOTENAME(SchemaName), QUOTENAME(TableName), ID)
FROM #Tables
WHERE ID = #ID;
EXEC (#SQLCommand);
SET #ID += 1;
END;
SELECT *
FROM #Tables;

Select table with name from column value

I have this SQL query
SELECT table_name
INTO #LukaTestTable
FROM information_schema.columns
WHERE column_name = 'GUID'
ORDER BY TABLE_NAME ;
How get the value of the column GUID in all tables with the name from TABLE_NAME?
Or can I get table like?
TABLE_NAME GUID_VALUE
In order to be able to query a table before you know what table you want you have to use dynamic queries. If you have an arbitrary number of tables, in SQL Server you could achieve this with cursors:
DECLARE
#column VARCHAR(256) = 'GUID',
#table VARCHAR(256),
#query VARCHAR(4000);
DECLARE #Tables CURSOR LOCAL FAST_FORWARD
FOR
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME = #column
ORDER BY TABLE_NAME;
OPEN #Tables
FETCH NEXT FROM #Tables INTO #table
WHILE ##FETCH_STATUS = 0
BEGIN
SET #query = 'SELECT ' + #column + ' FROM ' + #table
EXEC(#query)
FETCH NEXT FROM #Tables INTO #table
END
CLOSE #Tables
DEALLOCATE #Tables;
If you're sure your 'GUID' column will have same data type - you can create a temp table and insert to it in WHILE loop and SELECT everything in one go at the end.
You can use dynamic sql like this:
declare #sql varchar(max) = cast (' ' as varchar(max));
select #sql = #sql +
(select ' select GUID from ' + quotename(table_name) + ' union all ' as 'text()'
from #LukaTestTable
for xml path(''));
set #sql = reverse(stuff(reverse(#sql), 1, 10, ''));
--print #sql
exec(#sql);
And if you want table_name as e separate column you should first save it in your #LukaTestTable
Try this below script using While loop and dynamic Sql
IF OBJECT_ID('tempdb..#GetSqlQuery') IS NOT NULL
DROP TABLE #GetSqlQuery
IF OBJECT_ID('tempdb..#GetSpecficcolumnvalue') IS NOT NULL
DROP TABLE #GetSpecficcolumnvalue
GO
CREATE TABLE #GetSqlQuery
(
ID INT IDENTITY
,SELECtQuery nvarchar(max)
,TableName varchar(200)
)
CREATE TABLE #GetSpecficcolumnvalue
(
ID INT IDENTITY
,GetValue nvarchar(max)
,TableName varchar(200)
)
INSERT INTO #GetSqlQuery(SELECtQuery,TableName)
SELECT 'SELECT GUID,'''+TABLE_NAME+''' As TableName FROM '+QUOTENAME(TABLE_NAME) AS SELECtQuery,QUOTENAME(TABLE_NAME) AS TableName
FROM(
SELECT
TABLE_SCHEMA,
TABLE_NAME,
COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME ='GUID'
)dt
ORDER BY TABLE_NAME
DEclare #MinId INt,#MaxId INT,#Sql nvarchar(maX),#TableName varchar(1000)
SELECT #MinId =MIN(Id),#MaxId=MAX(ID) FROM #GetSqlQuery
WHILE (#MinId<=#MaxId)
BEgin
SELECT #Sql=SELECtQuery ,#TableName=TableName From #GetSqlQuery WHERE ID=#MinId
PRINT #Sql
INSERT INTO #GetSpecficcolumnvalue(GetValue,TableName)
EXEC (#Sql)
SET #MinId=#MinId+1
END
SELECT ROW_NUMBER()OVER(ORDER BY (SELECT 1)) AS Seq,GetValue,TableName
FROM
(
SELECT *, ROW_NUMBER ()OVER(PArtition by GetValue,TableName ORDER BY
TableName) AS dup FROM #GetSpecficcolumnvalue
)dt where dt.dup=1

T-SQL Select to list all tables in a set database and CHECKSUM_AGG(BINARY_CHECKSUM(*)) for each table

I need to list all tables in a set database, and CHECKSUM_AGG(BINARY_CHECKSUM(*)) for each table.
I know I can use sp_tables to list all tables, but how could I use this with CHECKSUM_AGG(BINARY_CHECKSUM(*)) as an extra column on the end?
This seems to work, though I have no idea how long it will take on larger databases with a lot of tables:
if object_id('tempdb..#Tables') is not null
drop table #Tables;
select row_number() over (order by (select null)) as RowNum
,t.table_catalog as TableQualifier
,t.table_schema as TableOwner
,t.table_name as TableName
,case t.table_type
when 'base table'
then 'table'
else t.table_type
end as TableType
,null as TableChecksum
into #Tables
from information_schema.tables t
where t.table_type <> 'view';
declare #RowNum int;
declare #TableName nvarchar(100);
declare #TableChecksum bigint;
declare #SQL nvarchar(max);
while (select count(1) from #Tables where TableChecksum is null) > 0
begin
select top 1 #RowNum = RowNum
,#TableName = TableName
,#SQL = 'select #TableChecksum = checksum_agg(binary_checksum(*)) from ' + #TableName
from #Tables
where TableChecksum is null;
exec sp_executesql #SQL
,N'#TableChecksum bigint output'
,#TableChecksum output;
update #Tables
set TableChecksum = #TableChecksum
where RowNum = #RowNum;
end;
select *
from #Tables;
if object_id('tempdb..#Tables') is not null
drop table #Tables;

View that displays all rows from tables returned by separate query

Need to display all rows from multiple tables. The tables to be included are to come from a separate query to ensure. So we know where the data has originated from, a new column must be added that contains the table name.
The tables to be included in this must be dynamic, to reduce maintenance, so newly created tables are automatically included.
To select the tables I have create this query:
select TABLE_NAME
from INFORMATION_SCHEMA.COLUMNS
where COLUMN_NAME = 'contact_info_type'
and TABLE_NAME NOT LIKE '%test%'
and TABLE_NAME NOT LIKE '%_STAGING'
The testing of the 'contact_info_type' column is to ensure the table is of the expected structure. All of these tables are exactly the same.
But then how do you pass these results into a new select statement?
I attempts this, which produced duplicate results and would not stop processing. Its also missing the extra column that adds in the table name
declare #tableNames nvarchar(max)
select #tableNames = COALESCE(#tableNames + ', ', '') + Cast(TABLE_NAME as varchar) from INFORMATION_SCHEMA.COLUMNS
where COLUMN_NAME = 'contact_info_type' and TABLE_NAME not like '%_STAGING' and TABLE_NAME like '%test%'
select #tableNames
declare #sqlText nvarchar(max)
set #sqlText = ''
select #sqlText = #sqlText + 'SELECT * from ' + #tableNames where person = 'Joe'
select #sqlText
Each table will contain a few thousand rows but the condition (name = 'Joe') will limit the results to around 100.
Running Server 2008 R2 SP3 (10.50).
May be this query would help you,
But you need to add the additional columns as what you need in your #results table.
DECLARE #tables TABLE (tableName VARCHAR(1000), nmbr INT IDENTITY(1,1))
DECLARE #results TABLE (person varchar(500), tablename varchar(1000))
DECLARE #i INT,
#tableName varchar(1000),
#sql varchar(500)
INSERT INTO #tables
SELECT DISTINCT TABLE_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME = 'contact_info_type'
AND TABLE_NAME NOT LIKE '%test%'
AND TABLE_NAME NOT LIKE '%_STAGING'
SELECT #i = MAX(nmbr)
FROM #tables AS t
WHILE (#i > 0)
BEGIN
SELECT #tableName = tablename
FROM #tables
WHERE nmbr = #i
SET #sql = 'SELECT person, '''+#tablename +''' as tablename
FROM '+ #tableName +'
WHERE person = ''joe'''
INSERT INTO #results
EXEC (#sql)
SET #i = #i - 1
END
SELECT * FROM #results

SQL schema and value

I have a select statement I want to make. I want to select
SELECT COLUMN_NAME AS FieldName FROM
INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'table1'
However I want to create another column named Value which is a particular row in table1
so I have rows of the column name and the corresponding single value. Any thoughts on how to approach this?
The following query produces a value (the minimum) for each column:
SELECT '''select '+COLUMN_NAME+''' AS FieldName, (select cast(MIN('+COLUMN_NAME+') as varchar(8000)) from '+const.tablename+')'
FROM INFORMATION_SCHEMA.COLUMNS c cross join
(select 'AllCurveNames' as tablename) const
WHERE c.TABLE_NAME = const.tablename
However, this produces a separate query for each row. To combine them together, you need a string aggregate concatenation. This is how you would do it in SQL Server:
declare #sql varchar(max);
SELECT #sql = (select 'select '''+COLUMN_NAME+''' AS FieldName, (select cast(MIN('+COLUMN_NAME+') as varchar(8000)) from '+const.tablename + ') union all '
FROM INFORMATION_SCHEMA.COLUMNS c cross join
(select WHATEVER as tablename) const
WHERE c.TABLE_NAME = const.tablename
for xml path('')
);
select #sql = LEFT(#sql, len(#sql) - 9);
exec(#sql);
Use a cross join, which is implicit if you just select from two tables with no join (i.e., from t1, t2):
SELECT COLUMN_NAME AS FieldName,
Table1.MyField
FROM
INFORMATION_SCHEMA.COLUMNS, Table1
WHERE
TABLE_NAME = 'table1'
AND
MyTable.ID = 123
I actually came up with a bit of a crazy solution but it works:
declare #tbl_name as varchar(255)
declare #field as varchar(255)
declare #val as varchar(255)
declare #SQL as nvarchar(4000)
create table #tbl ( [FieldName][varchar](255), [FieldVal][varchar](255))
set #tbl_name = 'table1'
DECLARE mah_cursor CURSOR FAST_FORWARD
FOR
SELECT COLUMN_NAME FROM
INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #tbl_name
OPEN mah_cursor
FETCH NEXT FROM mah_cursor INTO #field
WHILE ##FETCH_STATUS = 0
BEGIN
set #SQL = 'set #val = (Select top 1 ' + #field + ' from ' + #tbl_name + ')'
print #SQL
exec sp_executesql #query = #SQL, #params = N'#val varchar(255) OUTPUT', #val = #val OUTPUT
insert into #tbl ([FieldName],[FieldVal] ) values (#field, #val)
FETCH NEXT FROM mah_cursor INTO #field
END
CLOSE mah_cursor
DEALLOCATE mah_cursor
select * from #tbl
drop table #tbl
It loops through each value and adds it. The Fast_Forward feature optimizes the query for high performance