非 null 列的 SQL 查询

Sql query for non-null columns

提问人:DBA_AV 提问时间:10/30/2023 更新时间:10/31/2023 访问量:52

问:

我正在尝试编写一个查询来返回表的每一列和非空值的计数,结构为

        | C1 | C2 | C*
Count() 

对于我正在使用的单个列

SELECT COUNT(NULLIF(E.Id,''))
  FROM Employee as E

我无法理解如何在每列上递归执行 Nullif,而不必对每个列名称进行硬编码。

sql-server 数据库

评论

0赞 Thom A 10/30/2023
上述方法不太可能适用于非基于字符串的数据类型的列。
1赞 Thom A 10/30/2023
“我无法思考如何在每一列上递归执行 Nullif,而不必对每个列名称进行硬编码。”如果你不想对它进行硬编码,你将不得不使用动态 SQL。通常,我发现诸如此类的需求是 XY 问题
0赞 GuidoG 10/30/2023
你读过这个答案
0赞 siggemannen 10/30/2023
不要因为你做不到而束手无策,除非你想使用动态 sql,这可能比复制粘贴或使用 excel 需要更多的时间,而且肯定比提出问题需要更多的时间。
1赞 Salman A 10/30/2023
复制粘贴和正则表达式替换列的名称需要几秒钟。为什么要避免硬编码?

答:

0赞 Yitzhak Khabinsky 10/31/2023 #1

请尝试以下解决方案。

无论所讨论的表有多少列,以及列具有哪些数据类型,它都是通用的。

并且没有动态 SQL。

SQL算法

USE tempdb;
GO

DROP TABLE IF EXISTS #tmpTable;

-- DDL and sample data population, start
CREATE TABLE #tmpTable  (ID INT IDENTITY PRIMARY KEY, Col_1 VARCHAR(20), Col_2 VARCHAR(20), Col_3 VARCHAR(20), Col_4 VARCHAR(20));
INSERT INTO #tmpTable (Col_1, Col_2, Col_3, Col_4) VALUES
(NULL, 'Nike', 'Adidas', NULL),
(NULL, 'Nike', 'Adidas', 'Asics'),
(NULL, 'Asics', NULL, 'Asics'),
(NULL, 'Nike', NULL, 'Adidas');
-- DDL and sample data population, end

;WITH rs AS
(
    SELECT c.value('local-name(.)', 'sysname') AS column_name
    FROM #tmpTable AS t
    CROSS APPLY (SELECT t.* FOR XML PATH(''), TYPE) AS t1(x)
    CROSS APPLY x.nodes('/*') AS t2(c)
)
(
SELECT name AS column_name, NULL AS column_counter
FROM tempdb.sys.columns
WHERE object_id = object_id('tempdb..#tmpTable')
   AND is_nullable = 1
EXCEPT
SELECT column_name, null
FROM rs
)
UNION ALL
SELECT column_name, COUNT(*) AS column_counter
FROM rs
GROUP BY column_name;

输出

column_name column_counter
Col_1
Col_2 4
Col_3 2
Col_4 3
编号 4
1赞 Zorkolot 10/31/2023 #2

正如用户 @Thom A 在评论中指出的那样,您正在寻找动态 SQL 解决方案。 通常,您可以使用 sys.all_columns 查看数据库中的所有列详细信息。

同样,可以使用 sys.tables 查看所有表,并使用 sys.schemas 查看所有架构(其中默认架构为“dbo”)。

尝试:。SELECT * FROM sys.all_columns; SELECT * FROM sys.tables; SELECT * FROM sys.schemas

sys.tables 和 sys.columns 都联接在相同的列名 object_id 上。Sys.tables 联接到schema_id上的 sys.schemas。

动态 sql 通常是与变量的字符串连接。我创建了一个示例,该示例通过条件聚合执行您要求的操作:

DECLARE @tableName varchar(100) = 'Loan_dtl'
DECLARE @schemaName varchar(100) = 'dbo'
DECLARE @sql varchar(max) = 'SELECT '

SELECT @sql = CONCAT(@sql, 'COUNT(CASE WHEN ', C.name, ' IS NULL THEN 1 END) AS [', C.name, '], ')       
  FROM sys.tables T
    INNER JOIN sys.all_columns C ON T.object_id = C.object_id
    INNER JOIN sys.schemas S ON S.schema_id = T.schema_id
 WHERE T.name = @tableName AND S.name = @schemaName

--remove last comma in STUFF and add the FROM clause
SET @sql = CONCAT(STUFF(@sql, len(@sql), 1, ''), ' FROM ', @schemaName, '.', @tableName)
EXEC (@sql)