Rowcount for all tables by year in dbo schema - sql

I'm looking for a query in SQL Server that will return the total number of rows for a given year across all tables in a particular schema, in this case dbo.
All tables in this schema have a column named UPDT_TS which stores the last time the row was updated and can be used for this date calculation.

You can use a simple cursor, and dynamic SQL.
declare #v_Year int
set #v_Year = 2013
declare #v_TableName nvarchar(256)
declare #v_SQL nvarchar(max)
declare #v_Count int
declare #v_ResultTable table
(
TableName nvarchar(256),
[RowCount] int
)
declare cur cursor local fast_forward read_only for
select t.name from sys.tables as t
inner join sys.columns as c on t.object_id = c.object_id
where c.name = 'UPDT_TS' and t.schema_id = schema_id('dbo')
open cur
while (1=1)
begin
fetch next from cur into #v_TableName
if ##FETCH_STATUS <> 0
break;
set #v_SQL = 'select #v_Count = count(*) from '+QUOTENAME(#v_TableName)+' where year(UPDT_TS) = ' + cast(#v_Year as nvarchar(4))
exec sp_executesql #v_SQL, N'#v_Count int output', #v_Count output
insert into #v_ResultTable
select #v_TableName, #v_Count
end
close cur
deallocate cur
select * from #v_ResultTable
EDIT
Just saw Goat's answer after posting :) looks like we hade the same idea :)

Assuming you have a Results table to insert records into, you can do this with a simple cursor:
DECLARE #Iterator varchar(255)
,#strSQL varchar(MAX)
DECLARE xyz CURSOR CURSOR FAST_FORWARD READ_ONLY
FOR
SELECT t.name
FROM sys.schemas s
JOIN sys.tables t
ON s.schema_id = t.schema_id
WHERE s.name = 'dbo'
OPEN xyz
FETCH NEXT FROM xyz
INTO #Iterator
WHILE ##FETCH_STATUS = 0
BEGIN
SET #strSQL = 'INSERT INTO Results
SELECT COUNT(*) as Rows, '''+#Iterator+''' as TableName
FROM '+QUOTENAME(#Iterator)+'
WHERE YEAR(UPDT_TS) = 2013
'
EXEC (#strSQL)
FETCH NEXT FROM xyz
INTO #Iterator
END
CLOSE xyz
DEALLOCATE xyz
GO
Whenever dealing with dynamic sql it can be helpful to change EXEC (#strSQL) to PRINT (#strSQL) to ensure you've got the syntax nailed down.

This will work also:
DECLARE #sql NVARCHAR(max)
,#year INT
SET #year = 2013
SELECT #sql = 'select sum (total) from (' + SUBSTRING(cast((
SELECT ' union select count(*) as Total
from ' + quotename(table_name) + '
where
UPDT_TS between dateadd(year,#year-1900,0) and dateadd(s,-1,dateadd(year,#year-1900+1,0))'
FROM information_schema.columns
WHERE column_name = 'UPDT_TS'
AND TABLE_SCHEMA = 'dbo'
FOR XML path('')
) AS NVARCHAR(max)), 8, 8000) + ') as Presum'
PRINT #sql
EXEC sp_executesql #sql
,N'#year int'
,#year = #year

Related

How to get metadata from columns that have specific number of distinct values?

I need to find all columns that have 5 or more distinct values. Now my query is like:
SELECT TABLE_NAME,COLUMN_NAME, DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'MY_SCHEMA'
AND TABLE_NAME IN ('TABLE_1', 'TABLE_2', 'TABLE_3')
I thought it could be done like simple subquery. Something like:
*code above*
AND (select count(distinct COLUMN_NAME) FROM TABLE_SCHEMA + TABLE_NAME) > 5
I just recently started to learn SQL and thought this kind of thing is easy, but still I can't figure out right query.
With help of Stu's answer and this answer I was able to make workable solution.
declare #RowsToProcess int
declare #CurrentRow int
declare #SelectCol nvarchar(max)
declare #SelectTable nvarchar(max)
declare #tablesAndColumns table(RowID int not null primary key identity(1,1), table_name nvarchar(max), column_name nvarchar(max)
insert into #tablesAndColumns
select TABLE_NAME,COLUMN_NAME,DATA_TYPE
from INFORMATION_SCHEMA.COLUMNS
where TABLE_SCHEMA = 'my schema'
and TABLE_NAME in ('myTable', 'myTable2' ,'myTable3')
set #RowsToProcess=##ROWCOUNT
set #CurrentRow=0
while #CurrentRow<#RowsToProcess
begin
set #CurrentRow=#CurrentRow+1
select
#SelectCol=column_name,
#SelectTable=table_name
from #tablesAndColumns
where RowID=#CurrentRow
declare #QRY NVARCHAR(MAX)
set #QRY = ' insert into [my_schema].[result_table] (table_name,column_name,distinct_values)
SELECT ' + '''' +#SelectTable+ '''' + ', ' + '''' +#SelectCol+ '''' + ', count(*) as cnt
FROM (SELECT DISTINCT ' +#SelectCol+ ' FROM my_schema.'+ #SelectTable+') as a'
exec SP_EXECUTESQL #QRY
end
I'd like to propose another way. You can run through all the column and table names by using a CURSOR. That way you don't need to store them beforehand and can directly access them in your loop while also having a while condition.
Also I went with sys.tables and sys.columns since I noticed that INFORMATION_SCHEMA also contains views and sys.tables can be filtered for the table's type.
I added a "HAVING COUNT(*) >= 5" into the dynamic SQL so I don't save those informations in the first place rather than filtering them later.
Finally I went with "(NOLOCK)" because you only try to acces the tables for reading and that way you don't lock them for other users / interactions.
(The #i and #max are just for tracking the progress since I ran the query on ~10k columns and just wanted to see how far it is.)
Hopefully might be helpful aswell although you seem to have solved your problem.
DECLARE #columnName nvarchar(100),
#tableName nvarchar(100),
#sql nvarchar(MAX),
#i int = 0,
#max int = (SELECT COUNT(*)
FROM sys.tables T
INNER JOIN sys.columns C ON T.object_id = C.object_id
WHERE T.[type] = 'U')
DROP TABLE IF EXISTS #resultTable
CREATE TABLE #resultTable (ColumnName nvarchar(100), TableName nvarchar(100), ResultCount int)
DECLARE db_cursor CURSOR FOR
SELECT C.[name], T.[name]
FROM sys.tables T
INNER JOIN sys.columns C ON T.object_id = C.object_id
WHERE T.[type] = 'U'
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #columnName, #tableName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = CONCAT(' INSERT INTO #resultTable (ColumnName, TableName, ResultCount)
SELECT ''', #columnName, ''', ''', #tableName, ''', COUNT(*)
FROM (
SELECT DISTINCT [', #columnName, ']
FROM [', #tableName, '] (NOLOCK)
WHERE [', #columnName, '] IS NOT NULL
) t
HAVING COUNT(*) >= 5')
EXEC sp_executesql #sql
SET #i = #i + 1
PRINT CONCAT(#i, ' / ', #max)
FETCH NEXT FROM db_cursor INTO #columnName, #tableName
END
CLOSE db_cursor
DEALLOCATE db_cursor
SELECT *
FROM #resultTable

How to get return value from stored procedure with dynamic SQL query

The problem is to display all the table name from database having more than 10 rows and columns.
I have a cursor which is successfully returning table names with more than 10 columns, but when I try to count all the rows of particular table by passing table name as variable, I get an error like can not pass object as variable thus trying to get return value (all row count) using stored procedure with dynamic SQL.
I want to get return result as all row count from stored procedure thus
I can pass it to another variable into cursor.
DECLARE #TABLENAME VARCHAR(50)
DECLARE #COUNTROW INT
DECLARE #COLUMNCOUNT INT
DECLARE #ROWCOUNT INT
DECLARE TABLECURSOR CURSOR SCROLL FOR
SELECT NAME FROM SYS.TABLES
OPEN TABLECURSOR
FETCH NEXT FROM TABLECURSOR INTO #TABLENAME
WHILE ##FETCH_STATUS = 0
BEGIN
--EXEC #COUNTROW = USP_XX_EXECUTESQL #TABLENAME --[ CALL SP AND RETURN RESULT TO #COUNTROW ]
SELECT #COLUMNCOUNT = COUNT(*)
FROM INFORMATION_SCHEMA.Columns
WHERE TABLE_NAME = #TABLENAME
IF (#COLUMNCOUNT > 10)
BEGIN
PRINT #TABLENAME
END
FETCH NEXT FROM TABLECURSOR INTO #TABLENAME
END
CLOSE TABLECURSOR
DEALLOCATE TABLECURSOR
----STORED PROCEDURE TO COUNT ROWS
CREATE PROCEDURE USP_XX_EXECUTESQL(#TABLE VARCHAR(MAX))
AS
BEGIN
EXEC('SELECT COUNT(*) FROM ' + #TABLE) -- How to return value from here
END
You don't really need a cursor for this anyway, you could generate the SQL and execute it in one shot dynamically.
But in any case, it's far better to just query the system views for rowcounts
SELECT
SchemaName = SCHEMA_NAME(t.schema_id),
TableName = t.name,
TotalRowCount = (
SELECT SUM(p.rows)
FROM sys.partitions p
WHERE t.object_id = p.object_id
AND p.index_id IN ( 0, 1 ) -- heap or clustered
),
TotalColumns = (
SELECT COUNT(*)
FROM sys.columns c
WHERE t.object_id = c.object_id
)
FROM sys.tables t;
Here is answer
DECLARE #sql nvarchar(MAX)
DECLARE #TABLE NVARCHAR(30)
DECLARE #params nvarchar(4000)
SET #TABLE = N'AGENT'
SELECT #sql = N'SELECT #cnt = COUNT(*)
FROM ' + #TABLE
SELECT #params = N' #cnt int OUTPUT'
DECLARE #cnt int`enter code here`
EXEC sp_executesql #sql, #cnt OUTPUT
SELECT #cnt AS TOTALROW

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;

SQL: Select columns WITHOUT NULL values only in a table

This question is the exact opposite of SQL: Select columns with NULL values only.
Given a table with 1024 columns, how to find all columns WITHOUT null values?
Input:a table with 1024 columns
Output:col1_name(no null values) col2_name(no null values)...
If you want to avoid using a CURSOR, this method will simply list out the column names of any columns that have no NULL values in them anywhere in the table... just set the #TableName at the top:
DECLARE #tableName sysname;
DECLARE #sql nvarchar(max);
SET #sql = N'';
SET #tableName = N'Reports_table';
SELECT #sql += 'SELECT CASE WHEN EXISTS (SELECT 1 FROM ' + #tableName + ' WHERE '+ COLUMN_NAME + ' IS NULL) THEN NULL ELSE ''' + COLUMN_NAME +
''' END AS ColumnsWithNoNulls UNION ALL '
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #tableName
SELECT #sql = SUBSTRING(#sql, 0, LEN(#sql) - 10);
IF OBJECT_ID('tempdb..#Results') IS NOT NULL DROP TABLE #Results;
CREATE TABLE #Results (ColumnsWithNoNulls sysname NULL);
INSERT INTO #Results EXEC(#sql);
SELECT * FROM #Results WHERE ColumnsWithNoNulls IS NOT NULL
As a bonus, the results are in a temp table, #Results, so you can query to get any information you want... counts, etc.
i modified the Select columns with NULL values only.
to work for your case :
SET ANSI_WARNINGS OFF
declare #col varchar(255), #cmd varchar(max)
DECLARE getinfo cursor for
SELECT c.name FROM sys.tables t JOIN sys.columns c ON t.Object_ID = c.Object_ID
WHERE t.Name = 'Reports_table'
OPEN getinfo
FETCH NEXT FROM getinfo into #col
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #cmd = 'IF (SELECT sum(iif([' + #col + '] is null,1,null)) FROM Reports_table) is null BEGIN print ''' + #col + ''' end'
exec(#cmd)
FETCH NEXT FROM getinfo into #col
END
CLOSE getinfo
DEALLOCATE getinfo
SET ANSI_WARNINGS on

INSERT INTO command doesn't work

I have one outer cursor and one inner cursor also have two tables to work with. Now with the outer cursor i'm making new columns in the table 1 and naming them by the values from the table two, and that works just fine. Problem is with the inner cursor witch i used to insert the values into those new columns from one specific column from another table. This seams not to work, but what confusing me is that i do not get any error messages. Now i hope you understand what i'm trying to do, here is the code so comment for more description about the problem :
DECLARE #rbr_param nvarchar(255)
DECLARE #vrednost nvarchar(255)
DECLARE #cName nvarchar(255)
DECLARE #sql nvarchar (255)
DECLARE curs CURSOR FOR SELECT DISTINCT rbr_param FROM dbo.parametri_pomocna ORDER BY rbr_param
OPEN curs
FETCH NEXT FROM curs
INTO #rbr_param
WHILE ##FETCH_STATUS = 0
BEGIN
SET #cName = 'P_'+#rbr_param+'_P'
EXEC('ALTER TABLE dbo.Parametri ADD ' + #cName + ' nvarchar(255)')
DECLARE vrd CURSOR FOR SELECT DISTINCT vrednost FROM dbo.parametri_pomocna
OPEN vrd
FETCH NEXT FROM vrd
INTO #vrednost
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = 'INSERT INTO dbo.Parametri'+(#cName)+ ' SELECT vrednost FROM dbo.parametri_pomocna WHERE vrednost = '+#vrednost+ ' AND rbr_param = '+#rbr_param
if exists (select * from INFORMATION_SCHEMA.COLUMNS where table_name = 'dbo.Parametri' and column_name = '#cName')
begin
exec(#sql)
end
FETCH NEXT FROM vrd
INTO #vrednost
END --end vrd
CLOSE vrd
DEALLOCATE vrd
FETCH NEXT FROM curs
INTO #rbr_param
END
CLOSE curs
DEALLOCATE curs
You have two problems here:
if exists ( select * from INFORMATION_SCHEMA.COLUMNS
where table_name = 'dbo.Parametri'
and column_name = '#cName'
)
(1) This view will never have table_name = schema name and table name.
(2) You have enclosed your variable name in single quotes for some reason.
For both of these reasons, your IF condition will never return true.
Try:
IF EXISTS
(
SELECT 1 FROM sys.columns
WHERE [object_id] = OBJECT_ID('dbo.Parametri')
AND name = #cName
)
(And here is why I prefer catalog views over INFORMATION_SCHEMA.)
Also this double-nested cursor thing seems quite inefficient and a lot more code than necessary to achieve what I think you're trying to do. How about something like this instead:
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'';
SELECT #sql = #sql + N'ALTER TABLE dbo.Parametri ADD '
+ QUOTENAME('P_' + rbr_param + '_P') + ' NVARCHAR(255);'
FROM dbo.parametri_pomocna GROUP BY rbr_param;
EXEC sp_executesql #sql;
SET #sql = N'';
SELECT #sql = #sql + N'INSERT dbo.Parametri('+QUOTENAME('P_' + rbr_param + '_P')+ ')
SELECT vrednost
FROM dbo.parametri_pomocna WHERE rbr_param = ''' + rbr_param + '''
GROUP BY vrednost;'
FROM dbo.parametri_pomocna
GROUP BY rbr_param;
EXEC sp_executesql #sql;