Bulk select into FROM Information_schema.Tables - sql

I want to copy records from a bunch of tables into one table. I run through the tables dynamically. This works OK for the first encountered table. However, when it gets to the next variable table, it reports the destination table is already existing:
DECLARE #SQL nvarchar(max) = ''
SELECT #SQL = #SQL + 'select Datum, Tijd, Scanner into my_destination_table from '+ TABLE_NAME +' where tijd > 12 '
FROM Information_schema.Tables
WHERE TABLE_NAME LIKE 'tbl_%_Tijden'
EXEC sp_executesql #SQL;
Msg 2714, Level 16, State 6, Line 1
There is already an object named 'my_destination_table' in the database.
I just need it to keep filling my_destination_table with the records found.
Any suggestions?
thx,
James
#Giorgos, your solution seems to be working OK. I have tweaked it a liitle bit, and it does work. What I don't understand is why the results are doubled? This is the SQL:
DECLARE #SQL nvarchar(max) = ''
declare #t nvarchar(max) = '';
select #t = min(TABLE_NAME) from Information_schema.Tables
where TABLE_NAME > #t and TABLE_NAME like 'tbl_%_Tijden';
delete from tblfouttijden;
while #t is not null
begin
set #SQL = 'insert into tblfouttijden (Projectnr, Datum, Start, Einde, Tijd, TijdTijd, Scanner, StartID, EindID, Naam) select Projectnr, Datum, Start, Einde, Tijd, TijdTijd, Scanner, StartID, EindID, Naam from '+ #t +' where tijd > 12 ';
EXEC sp_executesql #SQL;
-- Move to the next table, if one exists:
select #t = min(TABLE_NAME) from Information_schema.Tables WHERE TABLE_NAME > #t and TABLE_NAME LIKE 'tbl_%_Tijden';
end

This will avoid duplicates:
create table tblfouttijden (Datum varchar(10), Tijd int, Scanner varchar(20)
, ... ) ;
DECLARE #SQL nvarchar(max) = '';
declare #t nvarchar(max) = '';
select #t = min(TABLE_NAME) from Information_schema.Tables WHERE TABLE_NAME > #t and TABLE_NAME LIKE 'tbl_%_Tijden';
set #SQL = 'select Projectnr, Datum, Start, Einde, Tijd, TijdTijd, Scanner, StartID, EindID, Naam from '
+ #t +' where tijd > 12 ';
while #t is not null
begin
select #t = min(TABLE_NAME) from Information_schema.Tables WHERE TABLE_NAME > #t and TABLE_NAME LIKE 'tbl_%_Tijden';
if #t is not null
set #SQL = #SQL + 'union select Projectnr, Datum, Start, Einde, Tijd, TijdTijd, Scanner, StartID, EindID, Naam from '
+ #t +' where tijd > 12 ';
end
set #SQL = 'insert into tblfouttijden (Projectnr, Datum, Start, Einde, Tijd, TijdTijd, Scanner, StartID, EindID, Naam) '
+ #SQL;
EXEC sp_executesql #SQL;

Related

Dynamic FROM source based on query results

I have a SQL Server instance that has several databases. Each database has a users table. I'm attempting to write a query that will count user record counts for each database. I've gotten to a point where I can select all database_name, schema_name, and table_name's into a temp table. I need to be able to select as from the results of that query though but I don't know how to go about doing that.
DECLARE #sql nvarchar(max);
SELECT #sql =
(SELECT ' UNION ALL
SELECT ' + + quotename(name,'''') + ' as database_name,
s.name COLLATE DATABASE_DEFAULT
AS schema_name,
t.name COLLATE DATABASE_DEFAULT as table_name
FROM '+ quotename(name) + '.sys.tables t
JOIN '+ quotename(name) + '.sys.schemas s
ON s.schema_id = t.schema_id'
FROM sys.databases
WHERE state = 0
ORDER BY [name] FOR XML PATH(''), type).value('.', 'nvarchar(max)');
SET #sql = stuff(#sql, 1, 12, '') + ' order by database_name,
schema_name,
table_name';
DECLARE #allTables TABLE
(
database_name NVARCHAR(MAX),
schema_name NVARCHAR(MAX),
table_name NVARCHAR(MAX)
)
INSERT INTO #allTables
EXEC (#sql)
SELECT
*
-- ,(select count(*) from [database_name].[schema_name].[table_name] where isadmin = 0) as 'Users'
-- ,(select count(*) from [database_name].[schema_name].[table_name] where isadmin = 1) as 'Admins'
FROM
#allTables
WHERE
table_name = 'C2_SEC_USERS'
This results in the following result set:
I have commented out the part of the select that I need to actually count the users because it's not valid SQL. Is it possible to select FROM based on the result set? If so, how?
You are almost there. Now you need to do 2 things -
Firstly Add an identity field in your Table variable #allTables like following
DECLARE #allTables TABLE
(
id int identity(1,1),
database_name NVARCHAR(MAX),
schema_name NVARCHAR(MAX),
table_name NVARCHAR(MAX)
)
Secondly build dynamic query and execute
DECLARE
#CurrentId AS INT = 0,
#CurrentDb AS NVARCHAR(MAX),
#CurrentSchema AS NVARCHAR(MAX);
SET #sql = '';
WHILE (EXISTS(SELECT id FROM #allTables WHERE table_name = 'C2_SEC_USERS' AND id > #CurrentId))
BEGIN
SELECT TOP 1 #CurrentId = id, #CurrentDb = [database_name], #CurrentSchema = [schema_name] FROM #allTables WHERE table_name = 'C2_SEC_USERS' AND id > #CurrentId
SET #sql += CONCAT('SELECT ''', #CurrentDb, ''' ''database'', COUNT(*) ''User Count'' FROM ', #CurrentDb,'.',#CurrentSchema,'.C2_SEC_USERS');
SET #sql += '
';
END;
EXEC (#sql)
I ended up figuring it out by using a cursor
declare #sql nvarchar(max);
declare #allTables TABLE (database_name NVARCHAR(MAX), schema_name NVARCHAR(MAX), table_name NVARCHAR(MAX))
select #sql =
(select ' UNION ALL
SELECT ' + quotename(name,'''') + ' as database_name,
s.name COLLATE DATABASE_DEFAULT
AS schema_name,
t.name COLLATE DATABASE_DEFAULT as table_name
FROM '+ quotename(name) + '.sys.tables t
JOIN '+ quotename(name) + '.sys.schemas s
on s.schema_id = t.schema_id'
from sys.databases
where state=0
order by [name]
for xml path(''), type).value('.', 'nvarchar(max)');
set #sql = stuff(#sql, 1, 12, '') + ' order by database_name, schema_name, table_name';
insert #allTables
exec (#sql)
declare #userCounts TABLE (database_name NVARCHAR(MAX),
users int,
admins int)
DECLARE #dbName VARCHAR(MAX)
DECLARE #schemaName VARCHAR(MAX)
DECLARE #tableName VARCHAR(MAX)
DECLARE #countSql NVARCHAR(MAX)
DECLARE tables_cursor CURSOR FOR SELECT database_name, schema_name, table_name
FROM #allTables
where table_name = 'C2_SEC_USERS' and database_name != 'master'
OPEN tables_cursor
FETCH NEXT FROM tables_cursor INTO #dbName, #schemaName, #tableName
WHILE ##FETCH_STATUS = 0 BEGIN
set #countSql = 'Select ''' + #dbName + ''' as database_name, (select count(*) from [' + #dbName + '].dbo.C2_SEC_USERS where isadmin = 0 and isinactive = 0) as users, (select count(*) from [' + #dbName + '].dbo.C2_SEC_USERS where isadmin = 1 and isinactive = 0) as admins'
INSERT INTO #userCounts
exec (#countSql)
FETCH NEXT FROM tables_cursor INTO #dbName, #schemaName, #tableName;
END;
CLOSE tables_cursor;
DEALLOCATE tables_cursor;
select * from #userCounts;

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

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

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

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