如何基于sql server中的where条件获取表中所有列的值的不同计数?

时间:2019-03-29 08:16:15

标签: sql sql-server

我有一个记录包含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)

我正在使用动态查询,因为我还需要将该查询重新用于其他表。

5 个答案:

答案 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'''