【问题标题】:Merge Null Values in Merge Command在合并命令中合并空值
【发布时间】:2020-10-30 09:49:15
【问题描述】:

我必须在 node js windows 服务中合并多个数据库表。
所以我决定写一个方法来为此创建一个 sql 字符串。

这是我的方法:

function createSQLString(targetTable, sourceTable, columns){
let sql = "MERGE " + targetTable + " AS TARGET USING " + sourceTable + " AS SOURCE \n";

switch (sourceTable) {
    //.....some other cases.....
    case 'communications':
        sql += "ON (" +
            " TARGET.[id] = SOURCE.[id]" +
            " AND TARGET.[business_partner_id] = SOURCE.[business_partner_id]" +
            " AND TARGET.[fiscalYear] = SOURCE.[fiscalYear]" +
            ") \n";
        break;
    default:
        // other stuff
}

// if row was found and values are not equal
sql += "WHEN MATCHED AND (";
    columns.forEach(column => {
        sql += "SOURCE.[" + column.COLUMN_NAME + "] <> TARGET.[" + column.COLUMN_NAME + "] OR ";
    })
    sql = sql.substring(0, sql.length -4) + "\n";
sql += ") THEN UPDATE SET ";
    columns.forEach(column => {
        sql += "TARGET.[" + column.COLUMN_NAME + "] = SOURCE.[" + column.COLUMN_NAME + "],"
    })
    sql = sql.substring(0, sql.length -1) + "\n";

// if no match in target insert
sql += "WHEN NOT MATCHED BY TARGET THEN INSERT (";
    columns.forEach(column => {
        sql += "[" + column.COLUMN_NAME + "], ";
    })
    sql = sql.substring(0, sql.length -2) + "\n";
sql += ") VALUES ("
    columns.forEach(column => {
        sql += "SOURCE.[" + column.COLUMN_NAME + "], ";
    })
    sql = sql.substring(0, sql.length -2) + "\n";
sql += ")\n"

// if there is a record in target but not in source
sql += "WHEN NOT MATCHED BY SOURCE \n THEN DELETE;"
return sql;
}

这种方法和创建的 sql 字符串效果很好,在一种情况下除外。
如果源表列中的值为NULL,则不会更新目标。
这是该案例的屏幕截图(表 1 目标,表 2 源):

为什么不更新以及如何解决?

编辑:这里是生成的sql字符串:

MERGE communication AS TARGET USING communication_cache AS SOURCE 
ON (
TARGET.[id] = SOURCE.[id] AND 
TARGET.[fiscalYear] = SOURCE.[fiscalYear] AND 
TARGET.[clientId] = SOURCE.[clientId]
) 
WHEN MATCHED AND (
SOURCE.[id] <> TARGET.[id] OR 
SOURCE.[address_type] <> TARGET.[address_type] OR 
SOURCE.[is_correspondence_address] <> TARGET.[is_correspondence_address] OR 
SOURCE.[is_main_post_office_box_address] <> TARGET. 
[is_main_post_office_box_address] OR 
SOURCE.[is_main_street_address] <> TARGET.[is_main_street_address] OR 
SOURCE.[is_management_address] <> TARGET.[is_management_address] OR 
SOURCE.[business_partner_id] <> TARGET.[business_partner_id] OR 
SOURCE.[fiscalYear] <> TARGET.[fiscalYear]
) 
THEN UPDATE SET 
TARGET.[id] = SOURCE.[id],
TARGET.[address_type] = SOURCE.[address_type],
TARGET.[is_correspondence_address] = SOURCE.[is_correspondence_address],
TARGET.[is_main_post_office_box_address] = SOURCE. 
[is_main_post_office_box_address],
TARGET.[is_main_street_address] = SOURCE.[is_main_street_address],
TARGET.[is_management_address] = SOURCE.[is_management_address],
TARGET.[business_partner_id] = SOURCE.[business_partner_id],
TARGET.[fiscalYear] = SOURCE.[fiscalYear]
WHEN NOT MATCHED BY TARGET THEN INSERT (
[id], [address_type], [is_correspondence_address], 
[is_main_post_office_box_address], [is_main_street_address], 
[is_management_address], [business_partner_id], [fiscalYear]
) VALUES (SOURCE.[id], SOURCE.[address_type], SOURCE. 
[is_correspondence_address], SOURCE.[is_main_post_office_box_address], 
SOURCE.[is_main_street_address], SOURCE.[is_management_address], SOURCE. 
[business_partner_id], SOURCE.[fiscalYear]
)
WHEN NOT MATCHED BY SOURCE 
THEN DELETE

【问题讨论】:

  • 你能告诉我们最终形成的 SQL 字符串吗?这将有助于更好地分析。

标签: sql node.js sql-server sql-update sql-insert


【解决方案1】:

NULL 在任何 RDBMS 中都是一种特殊情况。

当您在 WHEN MATCHED 子句中写下以下条件时:

SOURCE.[is_correspondence_address] <> TARGET.[is_correspondence_address] OR 

它没有考虑 NULL 进行比较 - 任何与 NULL 的比较都会导致 NULL。

如果你想处理 NULL 比较,你可以写下面的子句(假设 is_correspondence_address 是 VARCHAR/NVARCHAR 数据类型):

    ISNULL(SOURCE.[is_correspondence_address],'NOTDEFINED') 
      <> ISNULL(TARGET.[is_correspondence_address], 'NOTDEFINED') OR

【讨论】:

    【解决方案2】:

    虽然您可以通过添加另一个条件来检查违规列的无效性来解决问题,但我建议您只需将AND 条件分配给WHEN MATCHED 子句。

    这一次极大地简化了查询,并且不会改变结果。

    至于性能,我看到很多情况下这不会降低性能,甚至可能会提高性能:对每一行执行所有这些OR 检查是昂贵的,并且无论它们是否更改都重写值可能只是快点。此外,MERGE 语句的构建方式是,如果其中任何一个值发生了变化,您将重写所有值。

    另一个好处是,这涵盖了所有可能具有 nul 值的其他列的边缘情况(另一方面,您需要再次修改现有查询以处理该问题)。

    所以:

    MERGE communication AS t USING communication_cache AS s
    ON (t.[id] = s.[id] AND t.[fiscalYear] = s.[fiscalYear] AND t.[clientId] = s.[clientId]) 
    WHEN MATCHED 
    THEN UPDATE SET 
        t.[id] = s.[id],
        t.[address_type] = s.[address_type],
        t.[is_correspondence_address] = s.[is_correspondence_address],
        t.[is_main_post_office_box_address] = s. 
        [is_main_post_office_box_address],
        t.[is_main_street_address] = s.[is_main_street_address],
        t.[is_management_address] = s.[is_management_address],
        t.[business_partner_id] = s.[business_partner_id],
        t.[fiscalYear] = s.[fiscalYear]
    WHEN NOT MATCHED BY TARGET THEN INSERT (
        [id], [address_type], [is_correspondence_address], 
        [is_main_post_office_box_address], [is_main_street_address], 
        [is_management_address], [business_partner_id], [fiscalYear]
    ) VALUES (
        s.[id], s.[address_type], s.[is_correspondence_address], 
        s.[is_main_post_office_box_address], s.[is_main_street_address], 
        s.[is_management_address], s. [business_partner_id], s.[fiscalYear]
    )
    WHEN NOT MATCHED BY SOURCE THEN DELETE
    

    请注意,我使用了较短的表别名(st),这使得查询更短,并且在某种程度上更具可读性。

    【讨论】:

      猜你喜欢
      • 2019-04-12
      • 2016-01-10
      • 2012-09-12
      • 1970-01-01
      • 2016-04-10
      • 2011-03-31
      • 1970-01-01
      • 2011-11-08
      • 2019-04-18
      相关资源
      最近更新 更多