2011-11-17 66 views
0

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

USE QC_TEST 

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

SELECT @colName = c.name, @nullCols = COALESCE(@nullCols+', ','')[email protected] 
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个空值的列名称字符串。

感谢您的帮助

+2

为什么?什么类型的应用程序需要这个?你也可以在你的查询中加入'AND c.is_nullable = 1'来只循环可能有空的列。 –

+1

在将数据移到生产数据库之前,这是从供应商处提供给我们的数据时检查数据所必需的。这是一个标准的质量控制检查。 – Brian

回答

0

您的代码不工作,因为EXISTS子查询只是检查你的列名不为空,它是不可能的。子查询不会将“WHERE c.name IS NULL”展开为“WHERE MyColumnName IS NULL”,它只会看到c.name ='MyColumnName',它不为null并始终返回false。

基于游标的方法可能通过为每列创建一个单独的字符串查询然后删除该字符串。

你可以unpivot的表,并期待在该结果集空值,即

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 
+0

感谢您的回复。是否有可能动态地做到这一点?换句话说,我需要在具有不同列的多个表上运行此查询。我希望避免将列名硬编码到SQL代码中。 – Brian

+0

使用游标构建查询将是可以接受的。你只需要在表更改时重新运行它,并且任务的大小足够小,不用担心速度。 –

+0

@Brian:对于动态,请参阅我的答案。结果在行(不是CSV)中,但可以通过一个额外的间接级别来解决。这也是一种恶心(连接字符串...)。 – Sorpigal

0

这是一个有点标新立异,但试试这个:

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

OUTPUT:

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

(1 row(s) affected) 

如果你需要动态,因为你不知道列名,你可以建立一个类似的命令到一个字符串然后执行。

2

你可以得到一个结果,这样

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); 

但我不知道,你真的考虑好。

+0

感谢您的解决方案。我现在正在SSMS中尝试它,但查询结果为,错误消息如下:“名称...不是有效的标识符。”其中...是创建并存储在@ sql变量中的长SQL查询,但是它是截断的和不完整的。当我计算字符数时,它远小于varchar(max)变量应该允许的8,000个字符。你对这个错误有任何想法吗? – Brian

+0

@Brian:如果表中有很多列,则这个查询可能会导致比'varchar(max)'更长的字符串。不确定最好的解决方法。你可以让它工作在一大堆柱子中,但要多一点工作,但是, – Sorpigal

+0

当我在'EXEC @ sql'调用之前'PRINT @ sql'时,我得到整个字符串。 “EXEC”语句可以接受的变量大小是否有限制? – Brian

相关问题