从 table 中的预期列列表中获取缺失的列

Get missing columns from a list of expected columns in a table

测试有一个预期列的列表,用于验证实际 table 是否有它们。断言是如果预期计数和实际计数相等,则所有列都存在。

如何提取缺少的列以放入更有意义的错误消息中?

感谢您的帮助。

-- Verify added columns exist
CREATE PROC [testComplianceMaintenanceReporting].[test FactPropertyLatestRepairJobAgg_ColumnsExist]
AS
BEGIN
    SET NOCOUNT ON

    -- Assemble
    IF OBJECT_ID('tempdb..#TempExpected') IS NOT NULL
    BEGIN
        DROP TABLE #TempExpected
    END
    CREATE TABLE #TempExpected
    (
        Id INT,
        ColumnName NVARCHAR(50)
    )
    INSERT INTO #TempExpected
        VALUES
            (1, 'TenancyKey'),
            (2, 'TenancyHouseholdTypeKey'),
            (3, 'HomeVisitKeyLatest'),
            (4, 'HomeVisitDueStatusKey'),
            (5, 'HousingApplicationKey'),
            (6, 'RepairAreaKeyPropertyDefault'),
            (7, 'ContractKeyPropertyDefaultPMSCService'),
            (8, 'ContractorKeyPropertyDefaultHeadContractor'),
            (9, 'NextPeriodicHomeVisitDate'),
            (10, 'CountOfActiveLeaseAgreements')

    -- Action
    IF OBJECT_ID('tempdb..#TempActual') IS NOT NULL
    BEGIN
        DROP TABLE #TempActual
    END
    SELECT * INTO #TempActual
    FROM
    (
        SELECT
            OBJECT_ID,
            NAME
        FROM SYS.COLUMNS
        WHERE
            OBJECT_ID = OBJECT_ID('dm.Fact_PropertyLatestRepairJobAgg')
            AND NAME IN (SELECT ColumnName FROM #TempExpected)
    ) [TempActual]

    -- Assert
    DECLARE @expectedCount INT = (
        SELECT COUNT(*) FROM #TempExpected
    )
    DECLARE @actualCount INT = (
        SELECT COUNT(*) FROM #TempActual
    )
    EXEC tSQLt.AssertEquals
    @Expected = @expectedCount,
    @Actual = @actualCount,
    @Message = 'There are missing columns'
END
GO

你断言@actualCount 的测试条件的地方我相信你的意思是 INNER JOIN 到 #TempExpected table。这将确认实际值是正确的值。否则,实际行可能不包含预期的 ColumnNames。要创建缺少列名的列表,查询使用 STRING_AGG 和 NOT EXISTS。像这样

    -- Assert
    DECLARE @expectedCount INT = (
        SELECT COUNT(*) FROM #TempExpected
    );
    DECLARE @actualCount INT = (
        SELECT COUNT(*) 
        FROM #TempActual ta
             join #expectedCount ec on ta.ColumnName=ec.ColumnName 
    );
    DECLARE @missing_ColumnName_message varchar(200);
    select @missing_ColumnName_message = (
        select concat('There are missing columns: ', string_agg(ec.ColumnName, ','))
        from #expectedCount ec
        where not exists(select 1
                         from #TempActual ta
                         where ta.ColumnName=ec.ColumnName)
    );
    EXEC tSQLt.AssertEquals
    @Expected = @expectedCount,
    @Actual = @actualCount,
    @Message = @missing_ColumnName_message;

旧版本的 SQL Server 没有 STRING_AGG 可以使用 STUFFFOR XML

-- Assert
    DECLARE @expectedCount INT = (
        SELECT COUNT(*) FROM #TempExpected
    );
    DECLARE @actualCount INT = (
        SELECT COUNT(*) 
        FROM #TempActual ta
             join #expectedCount ec on ta.ColumnName=ec.ColumnName 
    );
    DECLARE @missing_ColumnName_message varchar(200);
    select @missing_ColumnName_message = (
        select concat('There are missing columns: ',
                      (stuff((select ', ' + ec.ColumnName
                       from #expectedCount ec
                       where not exists(select 1
                                        from #TempActual ta
                                        where ta.ColumnName=ec.ColumnName)
                       order by 1
                       for xml path('')),1,1,'')))
    );
    EXEC tSQLt.AssertEquals
    @Expected = @expectedCount,
    @Actual = @actualCount,
    @Message = @missing_ColumnName_message;

感谢@SteveC,这就是我最终得到的结果:

    -- Assert
    DECLARE @expectedCount INT = (
        SELECT COUNT(*) FROM #TempExpected
    )
    DECLARE @actualCount INT = (
        SELECT COUNT(*) 
        FROM #TempActual [TA]
        JOIN #TempExpected [TE] ON TA.Name = TE.ColumnName 
    )
    DECLARE @missingColumns NVARCHAR(MAX)
    SELECT @missingColumns = COALESCE(@missingColumns + ', ' + MissingColumn, MissingColumn) 
        FROM
        (
            SELECT TE.ColumnName [MissingColumn]
            FROM #TempExpected [TE]
            WHERE NOT EXISTS
            (
                SELECT 1
                FROM #TempActual [TA]
                WHERE TA.Name = TE.ColumnName
            )           
        ) [MissingColumns]
    DECLARE @errMsg NVARCHAR(MAX) = (
        SELECT CONCAT('Missing columns: ', @missingColumns)
    )
    EXEC tSQLt.AssertEquals
        @Expected = @expectedCount,
        @Actual = @actualCount,
        @Message = @errMsg

输出:

参考使用 COALESCE:

https://www.mytecbits.com/microsoft/sql-server/concatenate-multiple-rows-into-single-string

tSQLt 已经为此内置了一个断言,它可以验证列名、位置、数据类型和可空性。另外,在我看来一个更优雅的解决方案:

create procedure [UserProfileTests].[test Attribute column structure]
as
begin
    create table [UserProfileTests].[expected]
    (
      AttributeId int not null
    , AttributeName varchar(50) not null
    , DotNetType varchar(100) null
    , Narrative varchar(500) null,
    );

    exec tSQLt.AssertEqualsTableSchema '[UserProfileTests].[expected]', 'UserProfile.Attribute';
end;
go

...您得到的结果将使您能够快速识别什么是 missing/invalid