How to find List Tables using columns value - sql

I have 500+ tables in database. All tables have several columns. Among them Some tables have 'CMDFLAG' column and value of the columns may have 'C'or'D' or 'M'.
My requirement is to find the list of tables where CMDFLAG is 'C'or'D' or 'M'.
Table Name Column Name Value
---------- ----------- -----
Table_A CMDFLAG C
Table_A CMDFLAG D
Table_A CMDFLAG M
Table_B CMDFLAG C
Table_B CMDFLAG D
Table_C CMDFLAG M
so on ...
I can find list of tables these have CMDFLAG column using INFORMATION_SCHEMA.COLUMNS. But I Want to find list of tables where CMDFLAG columns have value 'C'or'D' or 'M'.
I have gone through several questions but can't fulfill my requirement. Moreover I want to use simple query not procedure.

Try this. I have to use dynamic query and temp tables as EXEC does not work for common table expressions.
CREATE TABLE #t1
(
tableName varchar(30),
RN INT
)
CREATE TABLE #t2
(
tableName varchar(30),
columnName varchar(30),
value char(1)
)
INSERT INTO #t1
SELECT C.TABLE_NAME, ROW_NUMBER() OVER(ORDER BY TABLE_NAME) AS RN
FROM INFORMATION_SCHEMA.COLUMNS C
WHERE C.COLUMN_NAME = 'CMDFLAG'
DECLARE #COUNT AS INT = (SELECT COUNT(1) FROM #t1)
DECLARE #Iterator AS INT = 1
DECLARE #tableName AS VARCHAR(30)
DECLARE #script AS VARCHAR(200)
WHILE #Iterator <= #COUNT
BEGIN
SELECT #tableName = (SELECT tableName FROM #t1 WHERE RN = #Iterator)
SELECT #script = 'SELECT '''+#tableName+''', ''CMDFLAG'', CMDFLAG FROM '+#tableName+' GROUP BY CMDFLAG'
INSERT INTO #t2
EXEC(#script)
SELECT #Iterator = #Iterator + 1
END
SELECT * FROM #t2
DROP TABLE #t1
DROP TABLE #t2

You can do it as follows.
SET NOCOUNT ON;
DECLARE #colname SYSNAME='CMDFLAG';
CREATE TABLE #tablenames(tablename SYSNAME,colname SYSNAME,colval NVARCHAR(128));
DECLARE #dsql NVARCHAR(MAX)= (
SELECT
N'INSERT INTO #tablenames(tablename,colname,colval)'+
N'SELECT DISTINCT ' +
'tablename='''+REPLACE(t.TABLE_NAME,N'''',N'''''')+N''','+
'colname='''+REPLACE(#colname,N'''',N'''''')+N''','+
'colval='+QUOTENAME(#colname)+N' '+
N'FROM '+QUOTENAME(t.TABLE_SCHEMA)+N'.'+QUOTENAME(t.TABLE_NAME)+N' '+
N'WHERE '+QUOTENAME(#colname)+N' IN (''C'',''D'',''M'');'
FROM
INFORMATION_SCHEMA.TABLES AS t
INNER JOIN INFORMATION_SCHEMA.COLUMNS AS c ON
c.TABLE_SCHEMA=t.TABLE_SCHEMA AND
c.TABLE_NAME=t.TABLE_NAME
WHERE
t.TABLE_TYPE='BASE TABLE' AND
c.COLUMN_NAME=#colname
FOR
XML PATH('')
);
EXECUTE sp_executesql #dsql;
SELECT * FROM #tablenames ORDER BY tablename;
DROP TABLE #tablenames;

How about something like this:
DECLARE #ColumnName sysname = 'CMDFLAG', #Sql NVARCHAR(MAX)
IF OBJECT_ID(N'TempDB.dbo.#Results', N'U') IS NULL
CREATE TABLE #Results(TableName SYSNAME, RowCounts INT)
ELSE
TRUNCATE TABLE #Results
SELECT
#Sql = 'INSERT INTO #Results '
+ STUFF((
SELECT
'UNION ALL SELECT Table_Name = '
+ QUOTENAME(C.TABLE_SCHEMA + '.' + C.TABLE_NAME, '''')
+ ', NumRows = COUNT(*)'
+ ' FROM '
+ QUOTENAME(C.TABLE_SCHEMA) + '.'
+ QUOTENAME(C.TABLE_NAME )
+ ' WHERE ' + QUOTENAME(C.COLUMN_NAME) + ' IN(''C'', ''D'', ''M'') '
+ ' GROUP BY ' + QUOTENAME(C.COLUMN_NAME)
+ ' HAVING COUNT(*) > 0 '
FROM
INFORMATION_SCHEMA.COLUMNS C
CROSS APPLY
(
SELECT
T.TABLE_SCHEMA, T.TABLE_NAME
FROM
INFORMATION_SCHEMA.TABLES T
WHERE
T.TABLE_TYPE = 'BASE TABLE'
AND T.TABLE_SCHEMA = C.TABLE_SCHEMA
AND T.TABLE_NAME = C.TABLE_NAME
) T
WHERE
C.COLUMN_NAME = #ColumnName
FOR XML PATH(''), type).value('.', 'nvarchar(max)'), 1, 10, '')
EXEC(#Sql)
SELECT * FROM #Results
UPDATE
Ok this should do what you want:
DECLARE #ColumnName sysname = 'CMDFLAG', #Sql NVARCHAR(MAX)
IF OBJECT_ID(N'TempDB.dbo.#Results', N'U') IS NULL
CREATE TABLE #Results(TableName SYSNAME, ColumnName sysname, Value NVARCHAR(25))
ELSE
TRUNCATE TABLE #Results
SELECT
#Sql = 'INSERT INTO #Results '
+ STUFF((
SELECT
'UNION ALL SELECT DISTINCT Table_Name = '
+ QUOTENAME(C.TABLE_SCHEMA + '.' + C.TABLE_NAME, '''')
+ ', ' + QUOTENAME(C.COLUMN_NAME, '''')
+ ', ' + QUOTENAME(C.COLUMN_NAME)
+ ' FROM '
+ C.TABLE_SCHEMA + '.'
+ C.TABLE_NAME
+ ' WHERE ' + QUOTENAME(C.COLUMN_NAME) + ' IN(''C'', ''D'', ''M'') '
FROM
INFORMATION_SCHEMA.COLUMNS C
CROSS APPLY
(
SELECT
T.TABLE_SCHEMA, T.TABLE_NAME
FROM
INFORMATION_SCHEMA.TABLES T
WHERE
T.TABLE_TYPE = 'BASE TABLE'
AND T.TABLE_SCHEMA = C.TABLE_SCHEMA
AND T.TABLE_NAME = C.TABLE_NAME
) T
WHERE
C.COLUMN_NAME = #ColumnName
FOR XML PATH(''), type).value('.', 'nvarchar(max)'), 1, 10, '')
EXEC(#Sql)
SELECT * FROM #Results

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.

Dynamic SQL to get rows from information_schema

Let’s say I’m looking for a specific column in my database so I have something like this
SELECT COLUMN_NAME, TABLE_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME like ‘%employeeid%’
But I also want to know how many rows each table has, I was told I can do this using Dynamic SQL so I have this now
DECLARE
#tableName NVARCHAR(MAX),
#sql NVARCHAR(MAX),
#colName NVARCHAR(MAX);
DECLARE CUR_TABLE CURSOR FOR
SELECT TABLE_NAME FROM INFORMATION_SCHEMA.COLUMNS
OPEN CUR_TABLE
FETCH NEXT FROM CUR_TABLE
INTO #tableName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #colName = '%employeeid%'
SET #sql = 'SELECT COLUMN_NAME, TABLE_NAME, (SELECT COUNT(*) FROM ' + #tableName +') AS ROWS FROM INFORMATION_SCHEMA.COLUMNS where column_name like ' + ''' + #colName + ''';
FETCH NEXT FROM CUR_TABLE
INTO #tableName
END;
CLOSE CUR_TABLE
DEALLOCATE CUR_TABLE
EXEC sp_executesql #sql
But this doesn't work, What I'm trying to do is query a table with the column I am looking for, with the table name, and number of rows in the table.
How can I fix this?
You can make use of SQL Server's dynamic management views to quickly obtain the row counts*.
Find all tables with a column named 'MyColumn' and their current rows:
select Schema_Name(t.schema_id) schemaName, t.name TableName, s.row_count
from sys.columns c
join sys.tables t on t.object_id = c.object_id
join sys.dm_db_partition_stats s on s.object_id = c.object_id and s.index_id <= 1
where c.name='MyColumn';
* Accurate except for frequently updated tables where there could be some lag
The following uses INFORMATION_SCHEMA, dynamic SQL, and STRING_AGG() to build a query that will return a single result set.
DECLARE #ColumnName sysname = 'ProductID'
DECLARE #Newline VARCHAR(2) = CHAR(13) + CHAR(10)
DECLARE #SqlTemplate NVARCHAR(MAX) =
+ 'SELECT'
+ ' ColumnName = <ColumnNameString>,'
+ ' TableName = <TableSchemaAndNameString>,'
+ ' Rows = (SELECT COUNT(*) FROM <TableSchemaAndName>)'
+ #Newline
DECLARE #UnionSql NVARCHAR(100) = 'UNION ALL ' + #Newline
DECLARE #Sql NVARCHAR(MAX) = (
SELECT STRING_AGG(
REPLACE(REPLACE(REPLACE(
#SqlTemplate
, '<ColumnNameString>', QUOTENAME(C.COLUMN_NAME, ''''))
, '<TableSchemaAndNameString>', QUOTENAME(C.TABLE_SCHEMA + '.' + C.TABLE_NAME, ''''))
, '<TableSchemaAndName>', QUOTENAME(C.TABLE_SCHEMA) + '.' + QUOTENAME(C.TABLE_NAME))
, #UnionSql)
WITHIN GROUP(ORDER BY C.TABLE_SCHEMA, C.TABLE_NAME)
FROM INFORMATION_SCHEMA.TABLES T
JOIN INFORMATION_SCHEMA.COLUMNS C
ON C.TABLE_SCHEMA = T.TABLE_SCHEMA
AND C.TABLE_NAME = T.TABLE_NAME
WHERE T.TABLE_TYPE = 'BASE TABLE' -- Omit views
AND C.COLUMN_NAME = #ColumnName
)
SET #Sql = #Sql + 'ORDER BY Rows DESC, TableName' + #Newline
--PRINT #Sql
EXEC (#Sql)
I generalized it a bit by adding TABLE_SCHEMA so that it could be used with the AdventureWorks database. See this db<>fiddle for a working demo. Also included is equivalent logic that uses FOR XML instead of STRING_AGG for older SQL Server versions.
Assuming that you are using SQL Server, here is a shorthand way using sp_msforeachtable.
DECLARE #ColumnName NVARCHAR(200) = 'ContactID'
CREATE TABLE #T
(
ColumnName NVARCHAR(200),
TableName NVARCHAR(200),
RecordCount INT
)
INSERT INTO #T (ColumnName, TableName)
SELECT
ColumnName = C.COLUMN_NAME,
TableName = '['+C.TABLE_SCHEMA+'].['+C.TABLE_NAME+']'
FROM
INFORMATION_SCHEMA.COLUMNS C
WHERE
C.COLUMN_NAME LIKE '%' + #ColumnName + '%'
EXEC SP_MSFOREACHTABLE 'IF EXISTS(SELECT * FROM #T WHERE TableName = ''?'') UPDATE #T SET RecordCount = (SELECT COUNT(*) FROM ? ) WHERE TableName = ''?'''
SELECT
ColumnName,TableName,
TableType = CASE
WHEN RecordCount IS NULL
THEN 'View'
ELSE 'Table'
END,
RecordCount
FROM
#T
ORDER BY
CASE WHEN RecordCount IS NULL THEN 'View' ELSE 'Table' END
DROP TABLE #T

How to get Max Date Value of Date column in Multiple tables

For example I have 2 tables in Database.
Ex :
Table T :
declare #t table (name varchar(20),DOB date)
Insert into #t (name,DOB) values ('Mohan','2001-07-19')
Insert into #t (name,DOB) values ('Minu','1998-06-19')
Table : TT
declare #tt table (name varchar(20),DOB date)
Insert into #tt (name,DOB) values ('Raju','2010-07-19')
Insert into #tt (name,DOB) values ('Rani','2001-06-19')
Now I have a Query to get Table name and column names of multiple tables basing on Date type filter .
SELECT C.TABLE_SCHEMA, c.TABLE_NAME,c.COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS c
JOIN sys.objects o ON o.name = c.TABLE_NAME
WHERE o.type = 'U' AND C.DATA_TYPE = 'Datetime'
Output :
T_Schema T_name T_column
dbo T DOB
dbo TT DOB
But how can I get max Date of both tables like below output :
T_Schema T_name T_column Max_dt
dbo T DOB 2001-07-19
dbo TT DOB 2010-07-19
Suggest me the best way to achieve this.
Here is another option that doesn't use a cursor. I doubt it will be any better from a performance perspective because you still need a subquery for every row. But I really hate cursors. I also used the system tables instead of the information schema views as those can sometimes be a bit odd. https://sqlblog.org/2011/11/03/the-case-against-information_schema-views
declare #SQL nvarchar(max) = N''
select #SQL = #SQL +
N'select SCHEMA_NAME = ''' + QUOTENAME(s.name) + ''', TABLE_NAME = '''
+ QUOTENAME(o.name) + ''', COLUMN_NAME = '''
+ QUOTENAME(c.name) + ''', MaxDate = '
+ '(select MAX(' + QUOTENAME(c.name) + ') from ' + QUOTENAME(s.name) + '.' + QUOTENAME(o.name) + ') UNION ALL '
from sys.columns c
join sys.systypes st on st.type = c.system_type_id
join sys.objects o on o.object_id = c.object_id and o.type = 'U'
join sys.schemas s on s.schema_id = o.schema_id
where st.name = 'datetime'
order by s.name
, o.name
, c.name
set #SQL = left(#SQL, len(#SQL) - 10) --removes final UNION ALL
select #SQL
--uncomment below when you are satisfied the dynamic sql is correct
--exec sp_executesql #SQL
Here's some dynamic SQL that should do what you want, via a cursor.
I'd caution using this if you have a lot of tables, or run this in test first. Cursors are not great performs generally speaking. You can run this against a system database, like master, which would have fewer values to see how it works.
create table #MaxDate (tname varchar(256), cname varchar(256), mdate datetime)
declare cur cursor local fast_forward
for
SELECT C.TABLE_SCHEMA, c.TABLE_NAME,c.COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS c
JOIN sys.objects o ON o.name = c.TABLE_NAME
WHERE o.type = 'U' AND C.DATA_TYPE = 'Datetime'
declare #schema varchar(64), #table varchar(256), #column varchar(256)
declare #sql varchar(max)
open cur
fetch next from cur into #schema, #table, #column
while ##FETCH_STATUS = 0
begin
set #sql = 'select ''' + #table + '''' + ',''' + '' + #column + '''' + ',' + 'max(' + #column + ') from ' + #schema + '.' + #table
print #sql
insert into #MaxDate
exec (#sql)
fetch next from cur into #schema, #table, #column
end
close cur
deallocate cur
select * from #MaxDate
drop table #MaxDate
Here's an answer using a cursor, dynamic SQL and a temporary table:
DECLARE table_cursor CURSOR LOCAL FAST_FORWARD FOR
SELECT
C.TABLE_SCHEMA,
c.TABLE_NAME,
c.COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS c
JOIN sys.objects o
ON o.name = c.TABLE_NAME
WHERE o.type = 'U'
AND C.DATA_TYPE = 'Datetime'
DECLARE #schema SYSNAME
DECLARE #table SYSNAME
DECLARE #column SYSNAME
DECLARE #sql NVARCHAR(1000)
CREATE TABLE #Data (SchemaName SYSNAME, TableName SYSNAME, ColumnName SYSNAME, MaxDate DATETIME)
OPEN table_cursor
FETCH NEXT FROM table_cursor INTO #schema, #table, #column
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = 'INSERT INTO #Data (SchemaName, TableName, ColumnName, MaxDate) SELECT '''+#schema+''', '''+#table+''', '''+#column+''', MAX(['+#column+']) FROM ['+#schema+'].['+#table+']'
EXEC sp_executesql #sql
FETCH NEXT FROM table_cursor INTO #schema, #table, #column
END
CLOSE table_cursor
DEALLOCATE table_cursor
SELECT * FROM #Data
DROP TABLE #Data
SQL to BUILD SQL for you
DECLARE #SQL as nvarchar(max) ='';
select #SQL = #SQL + 'SELECT ' + Column_Name + ' adate, ''' + Column_Name + ''' colname, ''' + Table_name + ''' tabname FROM ' + Table_name + ' UNION ' FROM INFORMATION_SCHEMA.COLUMNS where data_type like '%date%'
select #SQL = 'SELECT TOP 100 * FROM (' + LEFT(#SQL, LEN(#SQL) -6) + ') IQ WHERE IQ.adate IS NOT null ORDER BY IQ.adate DESC';
--cut n paste the sql below, see what it does for you
select #SQL
mark II - executes sql for you, and sorts out names with spaces in them
DECLARE #SQL as nvarchar(max) ='';
select #SQL = #SQL + 'SELECT [' + Column_Name + '] adate, ''' + Column_Name + ''' colname, ''' + Table_name + ''' tabname FROM [' + Table_name + '] UNION ' FROM INFORMATION_SCHEMA.COLUMNS where data_type like '%date%'
select #SQL = 'SELECT TOP 100 * FROM (' + LEFT(#SQL, LEN(#SQL) -6) + ') IQ WHERE IQ.adate IS NOT null ORDER BY IQ.adate DESC';
select #SQL;
EXEC sp_executesql #sql;

Max(ID) of each table on a database

I am trying to get the maximum value MAX(ID) for each table I have which contains ID on my DB "Table_Example" and one schema_name in specific.
A single example:
SELECT MAX(ID) FROM Schema_name.Table_name1
This retrieve the maximum ID value that is located on Table_name1, but I have 84 tables. I would like to know the max of each table only in one column.
This is the code where I am working on currently:
I am using information_schema.columns to get the names of the tables automatic and the schema each table belongs to in order to get the whole DB IDs max(id) in one column.
USE TABLE_EXAMPLE
GO
DECLARE #ID NVARCHAR(MAX) --int
SET #ID = (SELECT DISTINCT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'SCHEMA_NAME' AND COLUMN_NAME IN ('ID') AND DATA_TYPE = 'INT')
SELECT #ID FROM (SELECT ('SCHEMA_NAME'+'.'+TABLE_NAME) AS TABLE_NAME FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'SCHEMA_NAME' AND COLUMN_NAME = 'ID' AND DATA_TYPE='INT') AS W
This Script retrieve wrong data but I think I am a bit closed to get the values, but I am not sure what I am doing wrong.
Could someone give me any good approach? Or any better option to get it done?
If you are wanting the max value in your identity columns, regardless of the names of those columns, then this is a very simple way of doing it. This will give you the Table Name, the name of the Identity Column, and the max value of that column:
SELECT sys.tables.name AS [Table Name],
sys.identity_columns.name AS [Column Name],
last_value AS [Last Value]
FROM sys.identity_columns
INNER JOIN sys.tables
ON sys.identity_columns.object_id = sys.tables.object_id
ORDER BY last_value DESC
This enumerate all tables with column Id and MAX value of this ID:
DECLARE #query nvarchar(MAX);
SELECT #query = COALESCE(#query + char(10)+'UNION ALL '+char(10)+'SELECT '''+QUOTENAME(s.name)+'.'+QUOTENAME(T.name)+''' [Table], MAX(Id) [Max] FROM '+QUOTENAME(s.name)+'.'+QUOTENAME(T.name),
'SELECT '''+QUOTENAME(s.name)+'.'+QUOTENAME(T.name)+''' [Table], MAX(Id) [Max] FROM '+QUOTENAME(s.name)+'.'+QUOTENAME(T.name))
FROM sys.schemas S
JOIN sys.tables T ON S.schema_id=T.schema_id
JOIN sys.columns C ON T.object_id=C.object_id
WHERE C.name='Id';
EXEC(#query);
Try like this,
This would give you the script.
SELECT DISTINCT 'SELECT MAX(' + + COLUMN_NAME + ') as ' + table_name + 'MaxId FROM ' + table_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'dbo'
AND COLUMN_NAME IN ('ID')
CREATE TABLE #MaxValues (SchemaName SYSNAME , TableName SYSNAME , MaxID INT)
GO
Declare #SchemaName SYSNAME = 'dbo' --<-- Pass you schema name to this variable
,#ColumnName SYSNAME = 'ID' --<-- Column Name
,#DataType SYSNAME = 'INT' --<-- Data type
DECLARE #TableName SYSNAME , #SchmaName SYSNAME
, #Sql NVARCHAR(MAX) , #ColName SYSNAME;
Declare Cur CURSOR LOCAL FAST_FORWARD FOR
SELECT s.name , t.name , c.name
FROM sys.columns c
Inner join sys.tables t on c.object_id = t.object_id
Inner join sys.schemas s on s.schema_id = t.schema_id
Inner join sys.types tp on tp.user_type_id = c.user_type_id
WHERE s.name = #SchemaName
AND c.name = #ColumnName
AND tp.name = #DataType
OPEN Cur
FETCH NEXT FROM Cur INTO #SchmaName , #TableName , #ColName
WHILE (##FETCH_STATUS =0)
BEGIN
SET #Sql = N'INSERT INTO #MaxValues (SchemaName, TableName, MaxID )'
+ N' SELECT #SchmaName ,#TableName, MAX(' + QUOTENAME(#ColName) + N') '
+ N' FROM ' + QUOTENAME(#SchmaName) + '.' + QUOTENAME(#TableName)
Exec sp_executesql #Sql
,N'#SchmaName SYSNAME , #TableName SYSNAME'
,#SchmaName
,#TableName
FETCH NEXT FROM Cur INTO #SchmaName , #TableName , #ColName
END
CLOSE Cur
DEALLOCATE Cur
SELECT * FROM #MaxValues
Perhaps a little dynamic SQL
Edit This will return the Table Name(s) and Max ID in one dataset
Declare #SQL varchar(max) = '>>>'
Select #SQL = #SQL + SQL
From (
Select SQL='Union All Select TableName='''+concat('[',Table_Schema,'].[',Table_Name,']')+''',MaxID=max(ID) From '+concat('[',Table_Schema,'].[',Table_Name,'] ')
From INFORMATION_SCHEMA.COLUMNS
Where Column_Name = 'ID'
) A
Set #SQL=Replace(#SQL,'>>>Union All ','')
Exec(#SQL)
This script will list all the max ids. It is assuming your first column is the ID, regardless of its name.
DECLARE #Script AS VARCHAR(MAX) = ''
SELECT #Script = #Script + 'SELECT MAX(' + COLUMN_NAME + ') AS ID FROM ' + c.TABLE_NAME + ' UNION ALL ' + CHAR(13)+CHAR(10)
FROM INFORMATION_SCHEMA.COLUMNS c
INNER JOIN INFORMATION_SCHEMA.TABLES t ON c.TABLE_NAME = t.TABLE_NAME
WHERE c.ORDINAL_POSITION = 1 and t.TABLE_TYPE = 'BASE TABLE' and c.TABLE_SCHEMA = 'dbo' and c.DATA_TYPE = 'int'
SELECT #Script = LEFT(#Script, LEN(#Script) - 12)
EXEC (#Script)

get a count of each value from every column in a table SQL Server

So I looked this up and this question is very similar but it's missing a key piece: SQL Server count number of distinct values in each column of a table
So in that question they want the distinct count for each column. What I am looking to do is to get a count of each distinct value for each column in a table (and I'm doing this for all the tables in a particular database which is why I'm looking to try to automate this as much as possible). Currently my code looks like this which I have to run for each column:
select mycol1, COUNT(*) as [Count]
from mytable
group by mycol1
order by [Count] desc
Ideally my output would look like this:
ColumnName1 Count
val1 24457620
val2 17958530
val3 13350
ColumnName2 Count
val1 24457620
val2 17958530
val3 13350
val4 12
and so on for all the columns in the table
This answer below (provided by #beargle) from that previous question is really close to what I'm looking to do but I can't seem to figure out a way to get it to work for what I am trying to do so I would appreciate any help.
DECLARE #Table SYSNAME = 'TableName';
-- REVERSE and STUFF used to remove trailing UNION in string
SELECT REVERSE(STUFF(REVERSE((SELECT 'SELECT ''' + name
+ ''' AS [Column], COUNT(DISTINCT('
+ QUOTENAME(name) + ')) AS [Count] FROM '
+ QUOTENAME(#Table) + ' UNION '
-- get column name from sys.columns
FROM sys.columns
WHERE object_id = Object_id(#Table)
-- concatenate result strings with FOR XML PATH
FOR XML PATH (''))), 1, 7, ';'));
You could use:
DECLARE #Table SYSNAME = 'TableName';
DECLARE #SQL NVARCHAR(MAX) = ''
SELECT #SQL = STUFF((SELECT ' UNION SELECT ''' + name
+ ''' AS [Column], '
+ 'CAST(' + QUOTENAME(Name)
+ ' AS NVARCHAR(MAX)) AS [ColumnValue], COUNT(*) AS [Count] FROM '
+ QUOTENAME(#Table) + ' GROUP BY ' + QUOTENAME(Name)
FROM sys.columns
WHERE object_id = Object_id(#Table)
-- concatenate result strings with FOR XML PATH
FOR XML PATH ('')), 1, 7, '');
EXECUTE sp_executesql #SQL;
Which will produce SQL Like the following for a table with two columns (Column1 and Column2)
SELECT 'Column1' AS [Column],
CAST([Column1] AS NVARCHAR(MAX)) AS [ColumnValue],
COUNT(*) AS [Count]
FROM [TableName]
GROUP BY [Column1]
UNION
SELECT 'Column2' AS [Column],
CAST([Column2] AS NVARCHAR(MAX)) AS [ColumnValue],
COUNT(*) AS [Count]
FROM [TableName]
GROUP BY [Column2]
EDIT
If you want a new result set for each column then use:
DECLARE #Table SYSNAME = 'TableName';
DECLARE #SQL NVARCHAR(MAX) = '';
SELECT #SQL = (SELECT ' SELECT ' + QUOTENAME(Name)
+ ', COUNT(*) AS [Count] FROM '
+ QUOTENAME(#Table) + ' GROUP BY ' + QUOTENAME(Name) + ';'
FROM sys.columns
WHERE object_id = Object_id(#Table)
-- concatenate result strings with FOR XML PATH
FOR XML PATH (''));
EXECUTE sp_executesql #SQL;
Which would produce SQL Like:
SELECT [Column1],
COUNT(*) AS [Count]
FROM [callsupplier]
GROUP BY [Column1];
SELECT [Column2],
COUNT(*) AS [Count]
FROM [callsupplier]
GROUP BY [Column2];
thought i would take a stab at this whilst waiting for a backup to restore
hope this does what you require
create Table #Temp
(tableName varchar(100),
columnName varchar(100),
value varchar(1000),
distinctItems int)
Declare #tabName as varchar(100)
Declare #colName as varchar(100)
Declare #tabid as int
Declare cursorTables Cursor
for
select t.object_id , t.name , c.name from sys.tables t inner join sys.columns c on t.object_id = c.object_id
open cursorTables
Fetch Next from cursorTables into
#tabid,#tabName,#colName
while ##Fetch_Status = 0
Begin
declare #query as nVarchar(1000)
set #query = 'Insert into #Temp SELECT ''' + #tabName + ''' , '''+ #colName +''', ' + #colName + ', COUNT([' + #colName +']) AS Expr1 FROM [' + #tabName+ '] group by [' + #colName + ']'
print #query
exec sp_executesql #query
Fetch Next from cursorTables into
#tabid,#tabName,#colName
End
Close cursorTables
Deallocate cursorTables
select * from #temp
drop table #temp
produces some not very useful results on PK values and i suspect it would not work on columns greater than varchar(1000) but works on a fe of my dbs
This version makes a good snippet:
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql += 'SELECT ''' + t.name + ''', ''' + c.name + ''', ' + c.name + ', COUNT(' + c.name + ') AS C FROM ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name) + ' GROUP BY ' + c.name + ';' + CHAR(13)
FROM sys.tables AS t
INNER join sys.columns c on t.object_id = c.object_id
INNER JOIN sys.schemas AS s ON t.[schema_id] = s.[schema_id]
WHERE s.name LIKE 'stage' AND t.name LIKE 'table' AND c.name LIKE '%whatever%';
--PRINT #sql;
EXEC sp_executesql #sql