为什么DbCommandBuilder(Oracle)会为UpdateCommand生成奇怪的WHERE子句?

时间:2010-03-25 11:55:07

标签: c# ado.net

我在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 db中定义如下:

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

该怎么办? UpdateCommand非常疯狂。

谢谢&最诚挚的问候:马蒂

2 个答案:

答案 0 :(得分:2)

我真的不知道那些((:px = 1 AND XXX IS NULL)或(XXX =:py))的目的,但是CommandBuilder会生成一个where子句来检查正在更新的行是否已被更新你加载后改了。例如,如果您使用值(c1,c2,c3,...,cn)加载行R1并使用c3'更改c3的值,则更新命令文本具有where子句,该子句检查所有原始值行(例如,C1 = c1和C2 = c2和......)。如果更新命令影响0行,则表示其他人在您加载它的时间与更新它之间的时间内更新了该行,并且它会抛出DbConcurrencyException。我知道你可以改变这种行为(不记得具体如何)。

这就是更新命令中Where子句的主要原因。

答案 1 :(得分:0)

问题与DataTable中数据库NULL的表示方式以及如何测试数据库中列的值是否仍为NULL有关

在SQL中,NULL不是一个值,它是一个状态..所以你不能像这样测试一个sql列为NULL:WHERE MyColumn = NULL,这个测试总会返回false

因此您需要在WHERE中进行两项不同的测试,以检查实际的列状态是否仍然相同。

DataRows保留旧值和新值,因此更新命令应为:

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 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上的条件,例如"其中OtherColumn具有与OR之前相同的值(它为NULL AND它仍为NULL)"

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

我希望得到任何帮助 问候