代码之家  ›  专栏  ›  技术社区  ›  char m

为什么dbcommandbuilder(oracle)会生成奇怪的where子句来更新命令?

  •  1
  • char m  · 技术社区  · 14 年前

    我在oracle db中有一个表holidayhome,它在id上有唯一的db索引(我在代码中没有以任何方式为adapter/table/dataset指定这个,不知道是否应该/可以)。

    dbdataadapter.selectcommand如下:

    SELECT Id, ExtId, Label, Location1, Location2, Location3, Location4, 
    ClassId, X, Y, UseType 
    FROM HolidayHome
    

    但是由dbcommandbuilder生成的updatecommand有一个非常奇怪的where子句:

    UPDATE HOLIDAYHOME SET ID = :p1, EXTID = :p2, LABEL = :p3, LOCATION1 = :p4, 
    LOCATION2 = :p5, LOCATION3 = :p6, LOCATION4 = :p7, CLASSID = :p8, X = :p9, 
    Y = :p10, USETYPE = :p11 
    WHERE ((ID = :p12) AND ((:p13 = 1 AND EXTID IS NULL) OR (EXTID = :p14)) AND 
    ((:p15 = 1 AND LABEL IS NULL) OR (LABEL = :p16)) AND 
    ((:p17 = 1 AND LOCATION1 IS NULL) OR (LOCATION1 = :p18)) AND 
    ((:p19 = 1 AND LOCATION2 IS NULL) OR (LOCATION2 = :p20)) AND 
    ((:p21 = 1 AND LOCATION3 IS NULL) OR (LOCATION3 = :p22)) AND 
    ((:p23 = 1 AND LOCATION4 IS NULL) OR (LOCATION4 = :p24)) AND 
    (CLASSID = :p25) AND (X = :p26) AND (Y = :p27) AND (USETYPE = :p28))
    

    所有这些字段都有:

    ((:p17 = 1 AND LOCATION1 IS NULL) OR (LOCATION1 = :p18))
    

    在Oracle数据库中定义如下:

    LOCATION1 VARCHAR2(30)
    

    所以它们允许空值。

    代码如下所示:

            static bool CreateInsertUpdateDeleteCmds(DbDataAdapter dataAdapter)
            {
                DbCommandBuilder builder = _trgtProvFactory.CreateCommandBuilder();
                builder.DataAdapter = dataAdapter;
    
                // Get the insert, update and delete commands.
                dataAdapter.InsertCommand = builder.GetInsertCommand();
                dataAdapter.UpdateCommand = builder.GetUpdateCommand();
                dataAdapter.DeleteCommand = builder.GetDeleteCommand();
            }
    

    怎么办?更新命令简直是疯了。

    感谢并致以最良好的问候:Matti

    2 回复  |  直到 14 年前
        1
  •  2
  •   uvita    14 年前

    我真的不知道它们的用途((:px=1和xxx为空)或(xxx=:py)),但是commandbuilder确实生成了一个where子句来检查正在更新的行在您加载后是否已更改。例如,如果加载包含值(c1、c2、c3、…、cn)的行r1,并使用c3'更改c3的值,则update命令文本具有where子句,该子句检查行的所有原始值(例如,where c1=c1和c2=c2和…)。如果update命令影响0行,则表示在加载该行和更新该行之间的时间内,其他人已更新了该行,并且该命令抛出了一个dbconcurrencyException。我知道你可以改变这种行为(不记得具体是怎么改变的)。

    所以这就是update命令中where子句的主要原因。

        2
  •  0
  •   MtwStark    8 年前

    这个问题与如何在datatable中表示数据库空值以及如何测试数据库中列的值是否仍然为空有关。

    在sql中,null不是一个值,而是一个状态..因此,不能像这样测试SQL列的空值: WHERE MyColumn = NULL ,此测试将始终返回false

    所以你需要两个不同的测试 WHERE 检查实际列状态是否仍然相同。

    datarows同时保留旧值和新值,因此update命令应为:

    UPDATE MyTable 
    SET KeyColumn = KeyDatacolumn.NewValue, OtherColumn = OtherDataColumn.NewValue
    WHERE KeyColumn = KeyDatacolumn.OldValue AND OtherColumn = OtherDataColumn.OldValue
    

    注意,othercolumn上的where条件只需要避免覆盖其他人对同一记录的更新

    但是,如前所述,如果othercolumn是空的,我们不能简单地测试 WHERE OtherColumn = OtherDataColumn.OldValue

    因此update命令将是:

    UPDATE MyTable 
    SET KeyColumn = KeyDatacolumn.Value, OtherColumn = OtherDataColumn.NewValue
    WHERE
        (KeyColumn = KeyDatacolumn.OldValue) AND  
        (
            (OtherColumn = OtherDataColumn.OldValue)
            OR
            (
                (OtherDataColumn.OldValue.Equals(DBNull))
                AND
                (OtherColumn IS NULL)
            )
        )
    

    您可以在othercolumn上读取条件,比如“where othercolumn has the same value it before OR (它是空的 AND 仍然为空。“

    因此,对于可以为空的列,每列将使用两个不同的参数,第一个参数将作为 DataColumn.OldValue.Equals(DBNull) 而第二个将作为 IIF(DataColumn.OldValue.Equals(DBNull), "NULL", DataColumn.OldValue)

    我希望能帮上忙
    当做