Sql 基于集合的查询,以查找表中至少包含一个空值的所有列

Sql 基于集合的查询,以查找表中至少包含一个空值的所有列,sql,sql-server,sql-server-2008,Sql,Sql Server,Sql Server 2008,我不熟悉SQL和SQL Server 2008 R2。我发现了一种基于游标的方法来查找特定表中包含空值的所有列,但我希望找到一种更简单的基于集合的解决方案 USE QC_TEST DECLARE @colName nvarchar(100) DECLARE @nullCols nvarchar(max) SELECT @colName = c.name, @nullCols = COALESCE(@nullCols+', ','')+@colName FROM sys.tables AS t

我不熟悉SQL和SQL Server 2008 R2。我发现了一种基于游标的方法来查找特定表中包含空值的所有列,但我希望找到一种更简单的基于集合的解决方案

USE QC_TEST

DECLARE @colName nvarchar(100)
DECLARE @nullCols nvarchar(max)

SELECT @colName = c.name, @nullCols = COALESCE(@nullCols+', ','')+@colName
FROM sys.tables AS t
    JOIN sys.columns AS c
        ON t.object_id = c.object_id
WHERE t.name = 'myTable' 
AND 
EXISTS(SELECT * FROM myTable WHERE c.name IS NULL)

SELECT @colName, @nullCols
上述代码当前返回
myTable
中的所有列。如果我将
EXISTS
子句更改为
NOT EXISTS
,它将不返回任何列。结果应该是以逗号分隔的列名字符串,其中至少包含1个空值


感谢您的帮助

您的代码不起作用,因为EXISTS子查询正在检查您的列名是否为null(不能为null)。子查询没有将“WHERE c.name IS NULL”扩展到“WHERE MyColumnName IS NULL”,它只看到c.name='MyColumnName',它不是NULL,并且总是返回false

基于光标的方法可能是为每一列构建一个单独的字符串查询,然后执行该字符串

您可以取消填充该表,并在该结果集中查找空值,即

WITH myTable_unpvt AS (
    SELECT ContactID, FieldName, FieldValue
    FROM 
       (SELECT ContactID
            , cast(Forename as sql_variant) Forename
            , cast(Surname as sql_variant) Surname
            , cast(Extn as sql_variant) Extn
            , cast(Email as sql_variant) Email
            , cast(Age as sql_variant) Age
       FROM myTable) p
    UNPIVOT
       (FieldValue FOR FieldName IN 
          (Forename, Surname, Extn, Email, Age)
    ) AS myTable_unpvt
)

SELECT DISTINCT FieldName FROM myTable_unpvt WHERE FieldValue IS NULL

这有点不合常规,但试试这个:

--set up test table
--the BEGIN TRY and ALTERs allow you to rerun entire script over and over again
BEGIN TRY
    CREATE TABLE YourTable (PK int not null primary key, RowValue1 int null, RowValue2 varchar(5) null, RowValue3 datetime)
END TRY
BEGIN CATCH END CATCH
delete YourTable
ALTER TABLE YourTable ALTER COLUMN RowValue1 int  NULL
ALTER TABLE YourTable ALTER COLUMN RowValue2 varchar(5)  NULL
ALTER TABLE YourTable ALTER COLUMN RowValue3 datetime  NULL

--setup test data
INSERT INTO YourTable VALUES  (1,1111,'AAAA',GETDATE())
INSERT INTO YourTable VALUES  (2,2222,'BBBB',GETDATE())
INSERT INTO YourTable VALUES  (3,3333,'CCCC',GETDATE())
INSERT INTO YourTable VALUES  (4,NULL,'DDDD',GETDATE())
INSERT INTO YourTable VALUES  (5,5555,'EEEE',NULL)

--determine null columns
DECLARE @BadColumns  varchar(50)

BEGIN TRY
    ALTER TABLE YourTable ALTER COLUMN RowValue1 int NOT NULL
END TRY
BEGIN CATCH
    SET @BadColumns=ISNULL(@BadColumns+', ','')+'RowValue1'
END CATCH    
----------------------------------
BEGIN TRY
    ALTER TABLE YourTable ALTER COLUMN RowValue2 varchar(5) NOT NULL
END TRY
BEGIN CATCH
    SET @BadColumns=ISNULL(@BadColumns+', ','')+'RowValue2'
END CATCH  
----------------------------------
BEGIN TRY
    ALTER TABLE YourTable ALTER COLUMN RowValue3 datetime NOT NULL
END TRY
BEGIN CATCH
    SET @BadColumns=ISNULL(@BadColumns+', ','')+'RowValue3'
END CATCH  

--report null columns
SELECT @BadColumns AS [Columns having NULLs]
输出:

Columns having NULLs
--------------------------------------------------
RowValue1, RowValue3

(1 row(s) affected)

如果因为不知道列名而需要动态,可以将类似的命令构建到字符串中,然后执行该命令。

这样可以得到结果

declare @sql varchar(max), @t varchar(max);
set @t = 'your_table';
select @sql = (select stuff(
    (select 
            '] is null union select ''' + c.name + 
            ''' from ' + @t + ' where [' + c.name
        from
            (select
                    c.name
                from sys.tables AS t
                inner join sys.columns AS c
                    ON t.object_id = c.object_id
                where
                    t.name = @t
            ) as c
        for xml path('')
    ), 1, 16, '' ) + '] is null' )
;
exec(@sql);

但是我不知道你真的会考虑得更好。< /P>为什么?什么类型的应用程序需要这个?此外,您还可以将
和c.is_nullable=1
添加到查询中,以仅循环可能有空值的列。这对于在我们将数据移动到生产数据库之前检查供应商提供给我们的数据是必要的。这是标准的质量控制检查。谢谢您的回复。是否可以动态执行此操作?换句话说,我需要在具有不同列的多个表上运行此查询。我希望避免将列名硬编码到SQL代码中。使用游标构建查询是可以接受的。您只需要在表更改时重新运行它,并且任务的大小足够小,不必担心它的速度。@Brian:有关动态的详细信息,请参阅我的答案。结果以行(不是CSV)的形式显示,但这可以通过一个额外的间接级别来修复。这也是一种令人讨厌的(串接字符串…),谢谢你的解决方案。我现在正在SSMS中尝试,但查询结果显示为,错误为:“名称…不是有效标识符。”其中。。。是创建并存储在
@SQL
变量中的长SQL查询,但它是截断的且不完整的。当我计算字符数时,它远小于varchar(max)变量中应允许的8000个字符。你对这个错误有什么想法吗?@Brian:如果表中有很多列,那么这个查询可能会导致一个比
varchar(max)
长的字符串。不确定解决这个问题的最佳方法。您可以通过稍微多做一点工作使其在列块中运行,但是eww。在调用
EXEC@sql
之前,当我
PRINT@sql
时,我得到了整个字符串。
EXEC
语句可以接受的变量大小有限制吗?@Brian:根据SQL Server文档:“在早期版本的SQL Server中,字符串限制为8000字节。这需要连接大字符串以进行动态执行。在SQL Server 2005中,varchar(max)和nvarchar(max)可以指定允许字符串最多为2 GB数据的数据类型。“我认为它应该适用于@sql大于8000字节的
exec(@sql)
。我还将注意到,如果列名中包含
,则会导致中断。