我有一个记录包含100列的表,我需要根据某种条件(where子句)从该表中获取所有列的不同值的计数。
下面的查询工作正常,但是我无法使用where子句。因此,它给出了表中所有记录的结果。但是我希望它基于某种条件,比如说列file_id = 1;。我的问题是如何在以下查询中使用where子句。或者,如果还有其他替代方法可以解决此问题。
declare @SQL nvarchar(max)
set @SQL = ''
;with cols as (
select Table_Schema, Table_Name, Column_Name, Row_Number() over(partition by Table_Schema, Table_Name
order by ORDINAL_POSITION) as RowNum
from INFORMATION_SCHEMA.COLUMNS
)
select @SQL = @SQL + case when RowNum = 1 then '' else ' union all ' end
+ ' select ''' + Column_Name + ''' as Column_Name, count(distinct ' + quotename (Column_Name) + ' ) As DistinctCountValue,
count( '+ quotename (Column_Name) + ') as CountValue FROM ' + quotename (Table_Schema) + '.' + quotename (Table_Name)
from cols
where Table_Name = 'table_name' --print @SQL
execute (@SQL)
我正在使用动态查询,因为我还需要将该查询重新用于其他表。
答案 0 :(得分:8)
首先获取列,并使用东西以这种方式生成选择:
SELECT COUNT(ColumnA) AS ColumnA, COUNT(ColumnB AS ColumnB), COUNT(ColumnC) AS ColumnC....
这样,您只需在表上选择一次即可获取所有计数,然后,使用CROSS APPLY
“取消透视”这些列,并在每列的一行上返回输出
CROSS APPLY(
VALUES(1, 'ColumnA', ColumnA), (2, 'ColumnB', ColumnB), (3, 'ColumnC', ColumnC)
)(ID, ColumnName, DistinctCountValue)
对于过滤器,请使用sp_executesql并发送file_id作为参数
exec SP_executesql @SQL, N'@FID INT', @FID = @FileID
由于您正在使用表Row_Number() over(partition by Table_Schema, Table_Name order by ORDINAL_POSITION) as RowNum
的所有列而变得多余,因此ORDINAL_POSITION已经具有您要查找的值
declare @tablename nvarchar(50) = 'MyTestTable'
declare @fileID int = 1
declare @SQL nvarchar(max)
set @SQL = ''
;with cols as (
select TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = @TableName
)
select @SQL = ';WITH CTE AS (SELECT
' +
STUFF((
SELECT ', COUNT(DISTINCT ' + QUOTENAME(COLUMN_NAME) + ') AS ' + QUOTENAME(COLUMN_NAME)
FROM cols
ORDER BY ORDINAL_POSITION
FOR XML PATH('')
), 1, 1, '')
+ '
FROM ' + @TableName + '
WHERE File_ID = @FID
)
SELECT B.*
FROM CTE
CROSS APPLY (
VALUES ' +STUFF((
SELECT ',( ' + CAST(ORDINAL_POSITION AS VARCHAR) + ',' + QUOTENAME(COLUMN_NAME,'''') + ',' + QUOTENAME(COLUMN_NAME) + ')'
FROM cols
ORDER BY ORDINAL_POSITION
FOR XML PATH('')
), 1, 1, '') + '
)B (ID,ColumnName,DistinctCountValue)
'
from cols
exec SP_executesql @SQL, N'@FID INT', @FID = @FileID
答案 1 :(得分:4)
下面的查询创建一个包含所有列名的表,并使用while循环选择要使用的WHERE子句的计数。这对于任何表都应该非常灵活;只需更新最重要的变量。请注意,这将不计算其值为null的列。如果需要,可以在@Query参数中添加一个大小写。由于它是分别处理每一行的,因此我添加了一个临时表,因此您只需打一次数据库即可。
<AzureFunctionsVersion>v2</AzureFunctionsVersion>
答案 2 :(得分:4)
让我们尝试一些不同的方法。
获取所有值作为参数/值:
1)收集要在动态SQL中使用的表和列的列表:
DROP TABLE IF EXISTS #Base;
;WITH SchemaData AS (
SELECT t.name AS [TableName],c.name AS [ColumnName],c.column_id AS [ColumnOrderID]
FROM sys.tables t
INNER JOIN sys.columns c ON c.object_id = t.object_id
)
SELECT t.TableName
,STUFF((SELECT ',CONVERT(NVARCHAR(MAX),' + QUOTENAME([ColumnName]) + ') AS ' + QUOTENAME([ColumnName])
FROM SchemaData a WHERE (a.TableName = t.TableName) FOR XML PATH(''),TYPE).value('(./text())[1]','NVARCHAR(MAX)'),1,1,'') AS [SelectClause]
,STUFF((SELECT ',' + QUOTENAME([ColumnName]) FROM SchemaData a WHERE (a.TableName = t.TableName) FOR XML PATH(''),TYPE).value('(./text())[1]','NVARCHAR(MAX)'),1,1,'') AS [UnpivotClause]
INTO #Base
FROM SchemaData t
GROUP BY t.TableName
;
2)获取临时表中的所有数据
DROP TABLE IF EXISTS #Result;
CREATE TABLE #Result(TableName NVARCHAR(255),ColumnName NVARCHAR(255),[Value] NVARCHAR(MAX));
DECLARE @TableName NVARCHAR(255),@SelectClause NVARCHAR(MAX),@UnpivotClause NVARCHAR(MAX);
DECLARE crPopulateResult CURSOR LOCAL FAST_FORWARD READ_ONLY FOR SELECT b.TableName,b.SelectClause,b.UnpivotClause FROM #Base b;
OPEN crPopulateResult;
FETCH NEXT FROM crPopulateResult INTO @TableName,@SelectClause,@UnpivotClause;
DECLARE @dSql NVARCHAR(MAX);
WHILE @@FETCH_STATUS = 0
BEGIN
SELECT @dSql = N' INSERT INTO #Result(TableName,[ColumnName],[Value])
SELECT up.TableName,up.Param AS [ColumnName],up.[Value]
FROM (
SELECT ''' + @TableName + N''' AS [TableName]
,' + @SelectClause + N'
FROM ' + QUOTENAME(@TableName) + N'
) a
UNPIVOT(Value FOR Param IN (' + @UnpivotClause + N')) up
';
EXEC sp_executesql @stmt = @dSql;
FETCH NEXT FROM crPopulateResult INTO @TableName,@SelectClause,@UnpivotClause;
END
CLOSE crPopulateResult;
DEALLOCATE crPopulateResult;
3)任何过滤器都可以应用于#Results,包括表名称,列名称,数据过滤器等:
SELECT r.TableName,r.ColumnName,COUNT(*) AS [CountValue],COUNT(DISTINCT r.[Value]) AS [DistinctCountValue]
FROM #Result r
--
--WHERE r.ColumnName = 'file_id' AND r.[Value] = '1'
--
GROUP BY r.TableName,r.ColumnName
ORDER BY r.TableName,r.ColumnName
;
答案 3 :(得分:2)
要将此查询与where子句一起使用,只需要在表名后面的结构中放置where子句,因此,如果要对file_id ='1'进行过滤,则可以:
FROM ' + quotename (Table_Schema) + '.' + quotename (Table_Name) +'where file_id =''1'' '
答案 4 :(得分:2)
您可以添加一个@where
变量,并将其与您的大并集结构(作为select ... from cols
的一部分)连接起来。例如:
declare @SQL nvarchar(max)
declare @where nvarchar(max) = ' where file_id = 1'
set @SQL = ''
;with cols as (
select Table_Schema, Table_Name, Column_Name, Row_Number() over(partition by Table_Schema, Table_Name
order by ORDINAL_POSITION) as RowNum
from INFORMATION_SCHEMA.COLUMNS
)
select @SQL = @SQL + case when RowNum = 1 then '' else ' union all ' end
+ ' select ''' + Column_Name + ''' as Column_Name, count(distinct ' + quotename (Column_Name) + ' ) As DistinctCountValue,
count( '+ quotename (Column_Name) + ') as CountValue FROM ' + quotename (Table_Schema) + '.' + quotename (Table_Name)
+ @where
from cols
where Table_Name = 'table_name' --print @SQL
execute (@SQL)
请注意,如果要搜索字符串,则需要在@where
中转义单引号。例如,declare @where nvarchar(max) = ' where state = ''CT'''
。