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

问题描述 投票:11回答:5

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

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

sql sql-server
5个回答
8
投票

首先获取列并使用stuff以这种方式生成select:

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

4
投票

下面的查询创建了一个包含所有列名的表,并使用while循环选择要使用的WHERE子句的计数。这应该对任何表都非常灵活;只需更新顶部变量。请注意,这不会计算其值为null的列。如果这是您想要的,您可以将案例添加到@Query参数。因为它分别处理每一行,所以我在临时表中添加了一次,所以你只能点击一次db。

IF OBJECT_ID('tempdb..##SourceValues') IS NOT NULL
    DROP TABLE ##SourceValues
DECLARE @Schema VARCHAR(50) = 'SomeSchema'
DECLARE @Table VARCHAR(50) = 'SomeTable'
DECLARE @WhereClause VARCHAR(MAX) = ' Some WHERE clause'
DECLARE @ColumnName VARCHAR(50)
DECLARE @ProcessedRows TABLE(ColumnName VARCHAR(50), DistinctCount INT)
DECLARE @Columns TABLE(RowNumber INT,  ColumnName VARCHAR(100)) 
INSERT INTO @Columns SELECT ROW_NUMBER() OVER(ORDER BY COLUMN_NAME DESC),  COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @Table
DECLARE @Count INT = (SELECT MAX(RowNumber) FROM @Columns)
DECLARE @Counter INT = 0
DECLARE @DistinctCount INT 
DECLARE @Query NVARCHAR(MAX)
EXEC('SELECT * INTO ##SourceValues FROM ' + @Table +' (NOLOCK)')
WHILE @Counter < @Count
BEGIN 
    SET @Counter += 1
    SET @ColumnName = (SELECT ColumnName FROM @Columns WHERE RowNumber = @Counter)
    SET @Query = 'SELECT @OutPut = COUNT(' + @ColumnName + ') FROM ' + @Schema + '.' + ' ##SourceValues ' + @WhereClause
    EXECUTE sp_executesql @Query, N'@Output INT OUT', @DistinctCount OUT
    INSERT INTO @ProcessedRows(ColumnName, DistinctCount) VALUES (@ColumnName, @DistinctCount)
END
SELECT * FROM @ProcessedRows

4
投票

让我们尝试一些不同的方法。

将所有值视为Param / Value:

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
;

2
投票

要在带有此查询的where子句中使用它,您只需将where子句放在表名后的构造中,因此如果要在file_id ='1'上进行过滤,那么您将拥有:

FROM ' + quotename (Table_Schema) + '.' + quotename (Table_Name) +'where file_id =''1'' '

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

© www.soinside.com 2019 - 2024. All rights reserved.