为什么 DbCommandBuilder (Oracle) 为 UpdateCommand 生成奇怪的 WHERE 子句?
我在oracle db中有一个表HolidayHome,它在Id上有唯一的数据库索引(我没有在代码中以任何方式为适配器/表/数据集指定这一点,不知道我是否应该/可以)。
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))
所有这些具有 like: 的字段
((: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 完全是疯狂的。
谢谢&最好的问候:马蒂
I have a table HolidayHome in oracle db which has unique db index on Id (I haven't specified this in the code in any way for adapter/table/dataset, don't know if i should/can).
DbDataAdapter.SelectCommand is like this:
SELECT Id, ExtId, Label, Location1, Location2, Location3, Location4, ClassId, X, Y, UseType FROM HolidayHome
but UpdateCommand generated by DbCommandBuilder has very weird where clause:
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))
all these fields that have like:
((:p17 = 1 AND LOCATION1 IS NULL) OR (LOCATION1 = :p18))
are defined in oracle db like this:
LOCATION1 VARCHAR2(30)
so they allow null values.
the code looks like this:
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(); }
what to do? The UpdateCommand is utter madness.
Thanks & Best Regards: Matti
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我真的不知道这些 ((:px = 1 AND XXX IS NULL) OR (XXX = :py)) 的目的,但是 CommandBuilder 确实生成了一个 where 子句来检查正在更新的行是否已更改,因为您加载它。例如,如果您加载带有值 (c1, c2, c3,...,cn) 的行 R1,并使用 c3' 更改 c3 的值,则更新命令文本有一个 where 子句,用于检查所有原始值行(例如,其中 C1 = c1 且 C2 = c2 且...)。如果更新命令影响 0 行,则意味着其他人在您加载该行和更新该行之间的时间内更新了该行,并且会引发 DbConcurrencyException。我知道你可以改变这种行为(不记得具体如何改变)。
这就是更新命令中Where 子句的主要原因。
I really don´t know the purpose of those ((:px = 1 AND XXX IS NULL) OR (XXX = :py)), but the CommandBuilder does generate a where clause to check if the row being updated has been changed since you loaded it. For example if you load a row R1 with values (c1, c2, c3,...,cn) and you change the value of c3 with c3' then the update command text has a where clause that checks for all the original values of the row (e.g. where C1 = c1 and C2 = c2 and ...). If the update command affects 0 rows it means that someone else has updated that row in the time between you loaded it and the time you updated it, and it throws a DbConcurrencyException. I know that you can change that behavior (don´t remember exactly how).
So that is the main reason of the Where clause in the update command.
问题与数据库 NULL 在 DataTable 中的表示方式以及如何测试数据库中列的值是否仍然为 NULL 有关。
在 SQL 中,NULL 不是一个值,它是一种状态。所以你不能测试NULL 的 sql 列如下:
WHERE MyColumn = NULL
,此测试将始终返回 false,因此您需要在
WHERE
中进行两个不同的测试来检查实际列状态是否仍然是相同的。DataRows 保留旧值和新值,因此更新命令应该是:
请注意,OtherColumn 上的 WHERE 条件只是为了避免覆盖其他人对同一记录的更新
但是,如前所述,如果 OtherColumn可以为空,我们不能简单地测试
WHERE OtherColumn = OtherDataColumn.OldValue
因此更新命令将是:
您可以读取 OtherColumn 上的条件,例如“其中 OtherColumn 的值与
之前的值相同OR
(它是 NULLAND
它仍然是 NULL )”因此,对于可空列,每列将使用 2 个不同的参数,第一个参数将作为
DataColumn 传递。 OldValue.Equals(DBNull)
和第二个将作为IIF(DataColumn.OldValue.Equals(DBNull), "NULL", DataColumn.OldValue)
传递,我希望是有什么帮助
问候
The problem is related to how database NULLs are represented in your DataTable and how to test if the value of column in the database is still NULL
In SQL, NULL is not a value, it is a state.. so you can't test a sql column for NULL like this:
WHERE MyColumn = NULL
, this test will always return falseso you need two different test in your
WHERE
to check if actual column state is still the same.The DataRows keeps both old and new values so the update command should be:
Note that the WHERE condition on OtherColumn is needed only to avoid to overwrite someone else's updates to same record
But, as said before, if OtherColumn is nullable, we can't simply test
WHERE OtherColumn = OtherDataColumn.OldValue
so the update command will be:
You can read the condition on OtherColumn like "where OtherColumn has the same value it had before
OR
(it was NULLAND
it is still NULL )"So, for nullable columns will be used 2 different parameters for each column, the 1st one will be passed as
DataColumn.OldValue.Equals(DBNull)
and the 2nd one will be passed asIIF(DataColumn.OldValue.Equals(DBNull), "NULL", DataColumn.OldValue)
I hope to have been of any help
regards