Query with Group by across all tables in database - sql-server

I have a database (lets call it DB) containing 150+ tables, (eg: table1, table2 etc.)
Problem
I want to loop through all tables and get count of rows by groups, as below
Current Approach
As of now I was thinking of appending all tables or doing so manually!
Table structure
name code
A code1
A code2
A code6
A code98
B code1
Expected Output
table_name name code count
table1 A code1 100
table1 B code2 941
table2 A code1 98
Code for each table
SELECT name, code, count (*) AS count
FROM table1
GROUP BY name, code

As you want result a single result set, below will work:
CREATE TABLE #Temp
(
tableName VARCHAR(100)
,name VARCHAR(10)
,code VARCHAR(10)
, [Count] INT
)
EXEC sys.sp_MSforeachtable #command1=" insert into #Temp select '?' AS tableName,Name,Code,count(*) from ? group by Name, Code"
SELECT * FROM #Temp
DROP TABLE #temp
Just as an alternative, using dynamic sql:
CREATE TABLE #Temp
(
tableName VARCHAR(100)
,name VARCHAR(10)
,Code VARCHAR(10)
, [Count] INT
)
DECLARE #TableName VARCHAR(100)
DECLARE tableCursor CURSOR FAST_FORWARD FOR
SELECT name FROM sys.tables WHERE type ='U'
OPEN tableCursor
FETCH NEXT FROM tableCursor INTO #TableName
WHILE (##FETCH_STATUS = 0)
BEGIN
DECLARE #Query NVARCHAR(MAX)= N'select #table as TableName, Name, Code,Count(*) as [Count] from '+ #tablename + ' Group by Name, Code'
INSERT INTO #Temp
EXEC sp_executesql #Query, N'#table varchar(100)', #table = #TableName
FETCH NEXT FROM tableCursor INTO #TableName
END
CLOSE tableCursor
DEALLOCATE tableCursor
SELECT * FROM #temp
DROP TABLE #temp

You can use the SP ForEachTable that will execute the code against each table stored in your database. Try something like this:
sp_MSforeachtable #command1="select '?' AS TABLE_NAME,count(*) from ?"

Related

Count all rows from necessary tables using cursor

I have few tables in my database and I want to count total rows of all those tables based on AppoitmentID for which I use a scalar cursor and a table variable to store some data of those tables based on AppoitmentID. After the end of cursor I count rows of table variable in which I had inserted data using dynamic query in cursor.
But it gives me the following error
Must declare the table variable "#ProcCount".
Is there any other way to get the count of all rows from necessary tables.
Below is my Code :
Create FUNCTION [dbo].[ufn_GetProcedureCount]
(
)
RETURNS INT
AS
BEGIN
-- Declare the return variable here
DECLARE #MenuID bigINT, #TableName VARCHAR(150);
DECLARE #Result int
DECLARE #ProcCount TABLE (AppID INT, WoundId bigINT,TableName varchar(150));
DECLARE #sql nvarchar(2000)
DECLARE #Count int
DECLARE Cur_PendSign Cursor For
select Distinct MenuID,TableName from AppointmentTypeRequiredDocumnet A inner join Menu M on M.ID =A.MenuID where m.MenuGroupID = 8
OPEN Cur_PendSign
FETCH Cur_PendSign INTO #MenuID, #TableName
WHILE ##FETCH_STATUS=0
BEGIN
SET #sql='DECLARE #ProcCount TABLE (AppID INT, WoundId bigINT,TableName varchar(150))'
SET #sql=#sql+'INSERT INTO #ProcCount (AppID,WoundId)
SELECT TOP 1 V.AppointmentID, 1
FROM ['+#TableName+'] V WITH(NOLOCK)'
set #sql=#sql+ 'select count(*) from #ProcCount;'
--set #sql=#sql+ 'DECLARE #Count int'
EXECUTE sp_executesql #sql
FETCH Cur_PendSign INTO #MenuID, #TableName
END
CLOSE Cur_PendSign
DEALLOCATE Cur_PendSign
--set #Result = select count(*) from #ProcCount
RETURN #Result
END
There are two issues with the script.
First: Missing where clause
select Distinct MenuID,TableName from Appointment A.AppointmentID = 8
Second: you need to create persistent table rather than variable table due to limitation of scope as you cant declare variable table outside of EXEC and use it.
This query should work for you. You need to use sp_executesql to execute dynamic queries.
CREATE FUNCTION [dbo].[ufn_GetProcedureCount]
(
)
RETURNS INT
AS
BEGIN
-- Declare the return variable here
DECLARE #Result INT
DECLARE #ProcCount TABLE (
AppID INT,
WoundId BIGINT,
TableName VARCHAR(150)
)
DECLARE #MenuID BIGINT,
#TableName VARCHAR(150)
--Get all table which I need to count rows
DECLARE Cur_PendSign CURSOR FOR
SELECT DISTINCT MenuID, TableName FROM Appointment WHERE AppointmentID = 8
OPEN Cur_PendSign
FETCH Cur_PendSign INTO #MenuID, #TableName
WHILE ##fetch_status = 0
BEGIN
-- Insert require data into #ProcCount using dynamic query
DECLARE #query VARCHAR(255) = 'INSERT INTO #ProcCount (AppID,WoundId,TableName)
SELECT TOP 1 AppointmentID, WoundId, TableName
FROM [' + #TableName + '] WITH(NOLOCK) '
EXECUTE sys.sp_executesql #query
FETCH Cur_PendSign INTO #MenuID, #TableName
END
CLOSE Cur_PendSign
DEALLOCATE Cur_PendSign
--Get Count of all rows from tables
SELECT #Result = COUNT(*) FROM #ProcCount
RETURN #Result
END
Without having a cursor, Loops you can do it with Dynamic coding..
Schema:
(It may be differ with your actual schema)
CREATE TABLE #Appointment (MENUID INT IDENTITY,AppointmentID INT, TableName VARCHAR(20))
INSERT INTO #Appointment
SELECT 1,'TABLE1'
UNION ALL
SELECT 2, 'TABLE2'
UNION ALL
SELECT 8,'TABLE3'
UNION ALL
SELECT 8,'TABLE4'
UNION ALL
SELECT 8,'TABLE5'
Now do like below
DECLARE #QRY VARCHAR(MAX)='';
SELECT #QRY = #QRY+ 'SELECT COUNT(1) AS COUNT_TABLES FROM '+ TableName + ' (NOLOCK)
UNION ALL
' FROM (
SELECT DISTINCT TableName FROM #Appointment A WHERE A.AppointmentID = 8
)A
SELECT #QRY = SUBSTRING(#QRY,1,LEN(#QRY)-11)
SELECT #QRY = '
SELECT SUM(COUNT_TABLES) FROM (
' + #QRY+'
)A'
--PRINT #QRY
EXEC (#QRY)
If you want to check what #QRY contains
/*
SELECT SUM(COUNT_TABLES) FROM (
SELECT COUNT(1) AS COUNT_TABLES FROM TABLE3 (NOLOCK)
UNION ALL
SELECT COUNT(1) AS COUNT_TABLES FROM TABLE4 (NOLOCK)
UNION ALL
SELECT COUNT(1) AS COUNT_TABLES FROM TABLE5 (NOLOCK)
)A
*/

How To Select Data From Generated Information Schema Tables

I have several tables with common table names. I want to select all data from them without specifying all tables and union them. I'm thinking of using information_schema.tables to accomplish this.
Tables:
tbl_20160201
tbl_20160202
tbl_20160203
tbl_20160204
tbl_20160205
Query:
SELECT *
FROM (
SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE 'tbl_201602%'
) a
However, this query only returns the table names and not the data in the tables. I need to union all the tables included in the query. Thank you.
Assuming these tables have the same number of columns and data types, you can use dynamic sql:
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql = #sql +
'SELECT * FROM ' + TABLE_NAME + ' UNION ALL' + CHAR(10)
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE 'tbl_201602%';
SELECT #sql = SUBSTRING(#sql,1, LEN(#sql) - 11);
PRINT(#sql)
EXEC (#sql)
Otherwise, you'll get an error saying:
All queries combined using a UNION, INTERSECT or EXCEPT operator must
have an equal number of expressions in their target lists
Below query might help you :-
DECLARE #SQL VARCHAR(max)
DECLARE #T as sysname
-- Make sure below temporary table structure same as the tables e.g tbl_201602 which you want to union
declare #TT as Table(
Col1 [decimal](18, 2) NOT NULL,
Col2 [date] NOT NULL,
Col3 [varchar](200) NOT NULL
)
DECLARE TName CURSOR FORWARD_ONLY STATIC FOR
select TABLE_NAME
from
information_schema.tables where TABLE_NAME like 'tbl_201602%'
OPEN TName
FETCH NEXT FROM TName INTO #T
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #SQL = 'select * from' + ' ' + #T
INSERT INTO #TT
EXEC(#SQL)
FETCH NEXT FROM TName INTO #T
END
CLOSE TName
DEALLOCATE TName
select * from #TT
This will give the union of all tables in a single temporary table #TT
NOTE : Just make sure that all comman tables are having same no of columns and in same ordering, otherwise it may raise error

how to get all the tables from sql server database if a column name exists in the table and save the table data in a temporary table

I am trying to write a windows service, which will send automatic emails. all the tables which require email sending have common columns 'templateid' and 'emailstatus'. I want to iterate through all the tables and get the tables which has column name 'templateid'.
Now that i have the list of tables with column name 'templateid' get the data from each table whose email status is 'false' and save it in a temporary table.
if 'table1' has 4 rows of data, the temporary table should have 4 rows. after iterating through the next table the row collection should be added to the same temporary table.
IF (SELECT object_id('TempDB..#TEMPTABLE')) IS NOT NULL
BEGIN
DROP TABLE #TEMPTABLE
END
CREATE TABLE #TEMPTABLE(
[ID] INTEGER IDENTITY(1,1) NOT NULL,
[TABLE_NAME] VARCHAR(1000)
)
INSERT INTO #TEMPTABLE(TABLE_NAME)
SELECT TABLE_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME = 'TEMPLATEID'
SELECT * FROM #TEMPTABLE
DECLARE #ROWCOUNT INT
SET #ROWCOUNT = (SELECT COUNT(ID) FROM #TEMPTABLE)
DECLARE #I INT
SET #I=1
WHILE(#I<=#ROWCOUNT)
BEGIN
DECLARE #TABLENAME VARCHAR(500)
SELECT #TABLENAME=TABLE_NAME FROM #TEMPTABLE WHERE ID=#I
EXEC('SELECT * FROM '+#TABLENAME)
SET #I=#I+1
END
i found the above query which is giving me all the tables. after that i am clueless how to proceed further as i am not good with sql server.
Not sure you are still looking for an answer but the following code will give you a temporary table with just the tables that have a column named 'templateid'. From here you would need a cursor as Tanner suggested to loop through each table and then insert records from each table (into your final target table) where email status = 'false'.
Here is the code that gets you the temp tables with columns named 'templateid' along with a sample for your cursor:
declare #max_tables int
declare #max_columns int
declare #sql nvarchar(400)
declare #x int
declare #y int
declare #table varchar(50)
declare #columns varchar(800)
declare #tablename varchar(100)
create table #c ([Table] varchar(50),[Columns] varchar(800))
select ROW_NUMBER() OVER(ORDER BY name) AS Row, name
into #table_list
from sys.objects
where type_desc = 'USER_TABLE'
order by name
set #max_tables = (select count(*) from sys.objects where type_desc = 'USER_TABLE')
set #y = 0
while #y < #max_tables
begin
set #y = #y + 1
set #table = (select name from #table_list where row = #y)
create table #t (c int)
set #sql = 'select count(*) as c from Information_schema.Columns where table_name = ''' + #table + ''''
insert into #t exec sp_executesql #sql
set #max_columns = (select top 1 c from #t)
DROP TABLE #t
set #x = 0
set #columns = ''
while #x < #max_columns
begin
set #x = #x + 1
set #columns = #columns + (select column_name from Information_schema.Columns where table_name = #table and ordinal_position = #x)
if #x < #max_columns set #columns = #columns + ', '
end
insert into #c select #table,#columns
end
select * into #tables from #c c
where c.Columns like '%templateid%'
declare my_cursor cursor for
select table from #t
open my_cursor
fetch next from my_cursor into #tablename
while ##fetch_status = 0
begin
--do something here to retrieve your data from each table and
--insert into target table
end
close my_cursor
deallocate my_cursor
DROP TABLE #c,#tables,#table_List

How to copy a row with every column except identity column (SQL Server 2005)

My code:
SELECT * INTO #t FROM CTABLE WHERE CID = #cid --get data, put into a temp table
ALTER TABLE #t
DROP COLUMN CID -- remove primary key column CID
INSERT INTO CTABLE SELECT * FROM #t -- insert record to table
DROP TABLE #t -- drop temp table
The error is:
Msg 8101,
An explicit value for the identity column in table 'CTABLE' can only
be specified when a column list is used and IDENTITY_INSERT is ON.
And I did set
SET IDENTITY_INSERT CTABLE OFF
GO
DECLARE
#cid INT,
#o INT,
#t NVARCHAR(255),
#c NVARCHAR(MAX),
#sql NVARCHAR(MAX);
SELECT
#cid = 10,
#t = N'dbo.CTABLE',
#o = OBJECT_ID(#t);
SELECT #c = STRING_AGG(QUOTENAME(name), ',')
FROM sys.columns
WHERE [object_id] = #o
AND is_identity = 0;
SET #sql = 'SELECT ' + #c + ' INTO #t
FROM ' + #t + ' WHERE CID = #cid;
INSERT ' + #t + '('+ #c + ')
SELECT ' + #c + ' FROM #t;'
PRINT #sql;
-- exec sp_executeSQL #sql,
-- N'#cid int',
-- #cid = #cid;
However it seems much easier to just build the following SQL and avoid the #temp table altogether:
SET #sql = 'INSERT ' + #t + '(' + #c + ')
SELECT ' + #c + ' FROM ' + #t + '
WHERE CID = #cid;';
PRINT #sql;
-- exec sp_executeSQL #sql,
-- N'#cid int',
-- #cid = #cid;
Try this:
SELECT * INTO #t FROM CTABLE WHERE CID = #cid
ALTER TABLE #t
DROP COLUMN CID
INSERT CTABLE --Notice that INTO is removed here.
SELECT top(1) * FROM #t
DROP TABLE #t
Test Script(Tested in SQL 2005):
CREATE TABLE #TestIDNT
(
ID INT IDENTITY(1,1) PRIMARY KEY,
TITLE VARCHAR(20)
)
INSERT #TestIDNT
SELECT 'Cybenate'
Try specifying the columns:
INSERT INTO CTABLE
(col2, col3, col4)
SELECT col2, col3, col4
FROM #t
Seems like it might be thinking you are trying to insert into the PK field since you are not explicitly defining the columns to insert into. If Identity insert is off and you specify the non-pk columns then you shouldn't get that error.
Here's an example to dynamically build a list of columns - excluding the primary key columns - and execute the INSERT
declare #tablename nvarchar(100), #column nvarchar(100), #cid int, #sql nvarchar(max)
set #tablename = N'ctable'
set #cid = 1
set #sql = N''
declare example cursor for
select column_name
from information_schema.columns
where table_name = #tablename
and column_name not in (
select column_name
from information_schema.key_column_usage
where constraint_name in (select constraint_name from information_schema.table_constraints)
and table_name = #tablename
)
open example
fetch next from example into #column
while ##fetch_status = 0
begin
set #sql = #sql + N'[' + #column + N'],'
fetch next from example into #column
end
set #sql = substring(#sql, 1, len(#sql)-1)
close example
deallocate example
set #sql = N'insert into ' + #tablename + '(' + #sql + N') select top(1) ' + #sql + ' from #t'
--select #sql
exec sp_executesql #sql
If using SQL Server Management Studio and your problems you have too many fields to type them all out except the identity column, then right click on the table and click "Script table as" / "Select To" / "New Query Window".
This will provide a list of fields that you can copy & paste into your own query and then just remove the identity column.
Try invoking the INSERT statement with EXEC:
SELECT * INTO #t FROM CTABLE WHERE CID = #cid
ALTER TABLE #t
DROP COLUMN CID
EXEC('INSERT INTO CTABLE SELECT top(1) * FROM #t')
DROP TABLE #t
You can't do this:
INSERT INTO CTABLE SELECT top(1) * FROM #t
Because the column listings aren't the same. You've dropped the PK column from #t, so you have 1 less column in #t than in CTABLE. This is the equivalent of the following:
INSERT INTO CTABLE(pk, col1, col2, col3, ...)
select top(1) col1, col2, col3, ...
from #t
This wouldn't work for obvious reasons. Similarly, you aren't going to be able to specify the * wildcard to do the insert if you're not inserting all of the columns. The only way to do the insert without including the PK is to specify every column. You can generate a list of columns through dynamic sql, but you'll have to specify them one way or another.

how Do I list all table names in SQL Server using T-SQL?

SELECT name FROM sys.databases -- this can list all database name in the server
user database
SELECT * FROM INFORMATION_SCHEMA.TABLES
-- these two line can list the table for one particular database
But how can I output the results like below?
Database Table
--------- -------------
db1 t1
db1 t2
db2 t1
... ...
sp_msforeachdb 'select "?" AS db, * from [?].sys.tables'
Here is a stored procedure I use constantly to list all my tables ordered by the space used by them in the database.
GO
/****** Object: StoredProcedure [dbo].[dba_SpaceUsed] Script Date: 03/16/2010 15:09:55 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROC [dbo].[dba_SpaceUsed]
#SourceDB varchar ( 128 ) = null -- Optional database name
-- If omitted, the current database is reported.
, #SortBy char(1) = 'S' -- N for name, S for Size
-- T for table name
AS
SET NOCOUNT ON
DECLARE #sql nvarchar (4000)
IF #SourceDB IS NULL BEGIN
SET #SourceDB = DB_NAME () -- The current DB
END
--------------------------------------------------------
-- Create and fill a list of the tables in the database.
CREATE TABLE #Tables ( [schema] sysname
, TabName sysname )
SELECT #sql = 'insert #tables ([schema], [TabName])
select TABLE_SCHEMA, TABLE_NAME
from ['+ #SourceDB +'].INFORMATION_SCHEMA.TABLES
where TABLE_TYPE = ''BASE TABLE'''
EXEC (#sql)
---------------------------------------------------------------
-- #TabSpaceTxt Holds the results of sp_spaceused.
-- It Doesn't have Schema Info!
CREATE TABLE #TabSpaceTxt (
TabName sysname
, [Rows] varchar (11)
, Reserved varchar (18)
, Data varchar (18)
, Index_Size varchar ( 18 )
, Unused varchar ( 18 )
)
---------------------------------------------------------------
-- The result table, with numeric results and Schema name.
CREATE TABLE #TabSpace ( [Schema] sysname
, TabName sysname
, [Rows] bigint
, ReservedMB numeric(18,3)
, DataMB numeric(18,3)
, Index_SizeMB numeric(18,3)
, UnusedMB numeric(18,3)
)
DECLARE #Tab sysname -- table name
, #Sch sysname -- owner,schema
DECLARE TableCursor CURSOR FOR
SELECT [SCHEMA], TabNAME
FROM #tables
OPEN TableCursor;
FETCH TableCursor into #Sch, #Tab;
WHILE ##FETCH_STATUS = 0 BEGIN
SELECT #sql = 'exec [' + #SourceDB
+ ']..sp_executesql N''insert #TabSpaceTxt exec sp_spaceused '
+ '''''[' + #Sch + '].[' + #Tab + ']' + '''''''';
Delete from #TabSpaceTxt; -- Stores 1 result at a time
EXEC (#sql);
INSERT INTO #TabSpace
SELECT #Sch
, [TabName]
, convert(bigint, rows)
, convert(numeric(18,3), convert(numeric(18,3),
left(reserved, len(reserved)-3)) / 1024.0)
ReservedMB
, convert(numeric(18,3), convert(numeric(18,3),
left(data, len(data)-3)) / 1024.0) DataMB
, convert(numeric(18,3), convert(numeric(18,3),
left(index_size, len(index_size)-3)) / 1024.0)
Index_SizeMB
, convert(numeric(18,3), convert(numeric(18,3),
left(unused, len([Unused])-3)) / 1024.0)
[UnusedMB]
FROM #TabSpaceTxt;
FETCH TableCursor into #Sch, #Tab;
END;
CLOSE TableCursor;
DEALLOCATE TableCursor;
-----------------------------------------------------
-- Caller specifies sort, Default is size
IF #SortBy = 'N' -- Use Schema then Table Name
SELECT * FROM #TabSpace
ORDER BY [Schema] asc, [TabName] asc
ELSE IF #SortBy = 'T' -- Table name, then schema
SELECT * FROM #TabSpace
ORDER BY [TabName] asc, [Schema] asc
ELSE -- S, NULL, or whatever get's the default
SELECT * FROM #TabSpace
ORDER BY ReservedMB desc
;
DROP TABLE #Tables
DROP TABLE #TabSpaceTxt
DROP TABLE #TabSpace
--Thanks to Andrew Novick
if you need one result set, try this:
DECLARE #AllTables table (DatabaseName sysname, TableName sysname)
DECLARE #Current sysname
,#SQL1 varchar(500)
,#SQL2 varchar(500)
,#SQL3 varchar(500)
,#SQL varchar(1500)
SELECT TOP 1 #Current=Name FROM sys.databases Order By Name
SET #SQL1='select db.name, t.TABLE_NAME from '
SET #SQL2='.sys.databases db inner join '
SET #SQL3='.INFORMATION_SCHEMA.TABLES t on db.name = t.TABLE_CATALOG'
WHILE #Current IS NOT NULL
BEGIN
SET #SQL=#SQL1+#Current+#SQL2+#Current+#SQL3
INSERT INTO #AllTables
EXEC (#SQL)
SELECT TOP 1 #Current=Name FROM sys.databases WHERE Name>#Current Order By Name
IF ##ROWCOUNT=0 BREAK
END
SELECT * FROM #AllTables ORDER BY DatabaseName,TableName
If you're lucky enough to still be using sql2000:
CREATE TABLE #AllTables (DatabaseName sysname, TableName sysname)
DECLARE #Current sysname
,#SQL1 varchar(500)
,#SQL2 varchar(500)
,#SQL3 varchar(500)
,#SQL varchar(1500)
SELECT TOP 1 #Current=Name FROM sysdatabases Order By Name
SET #SQL1='select db.name, t.TABLE_NAME from '
SET #SQL2='sysdatabases db inner join '
SET #SQL3='.INFORMATION_SCHEMA.TABLES t on db.name = t.TABLE_CATALOG'
WHILE #Current IS NOT NULL
BEGIN
SET #SQL=#SQL1+#SQL2+#Current+#SQL3
--PRINT #SQL
SET NOCOUNT ON
INSERT INTO #AllTables
EXEC (#SQL)
SET NOCOUNT OFF
SELECT TOP 1 #Current=Name FROM sysdatabases WHERE Name>#Current Order By Name
IF ##ROWCOUNT=0 BREAK
END
SELECT * FROM #AllTables
--where TableName = 'at_errorlog'
ORDER BY DatabaseName,TableName
DROP TABLE #AllTables
select table_name from user_tables;

Resources