我很难接受一些非常直截了当的事情。我有一个SQL Server数据库,我正在尝试使用空字符串更新不可为空的varchar或nvarchar字段。我知道这是可能的,因为空字符串''
不与NULL
相同。但是,使用TADOQuery
,我不允许这样做。
我正在尝试像这样更新现有记录:
ADOQuery1.Edit;
ADOQuery1['NonNullFieldName']:= '';
//or
ADOQuery1.FieldByName('NonNullFieldName').AsString:= '';
ADOQuery1.Post; //<-- Exception raised while posting
如果字符串中有任何内容,即使只是一个空格,它也可以正常保存。但是,如果它是一个空字符串,它就会失败:
不可为空的列无法更新为Null。
但它不是空的。它是一个空字符串, 可以正常工作。我发誓我过去曾经多次通过空字符串。
为什么我会收到此错误,我该怎么做才能解决它?
其他详情:
SQLOLEDB.1
nvarchar(MAX) NOT NULL
答案 0 :(得分:12)
我可以使用SS2014,OLEDB驱动程序和下面的代码重现您报告的问题 西雅图以及使用MAX创建表作为列大小和特定数字(在我的情况下为4096)时的行为差异。我以为我会发布这个作为替代方案 回答是因为它不仅展示了如何系统地研究这种差异 但也要确定为什么出现这种差异(以及将来如何避免这种差异)。
请参考并执行下面的代码,如所写的,即使用UseMAX
定义
活性
开启&#34;使用调试DCU&#34;在执行代码之前的项目选项中,立即执行
揭示所描述的异常发生在Data.Win.ADODB
第4920行
Recordset.Fields[TField(FModifiedFields[I]).FieldNo-1].Value := Data
TCustomADODataSet.InternalPost
的和Debug评估窗口显示了这一点
此时Data
为Null
。
接下来,请注意
update jdtest set NonNullFieldName = ''
在没有投诉(Command(s) completed successfully.
)的SSMS2014查询窗口中执行,所以似乎是
{49}行Data
Null
导致问题的原因是,&#34;为什么?&#34;
首先要注意的是表单的标题显示为ftMemo
接下来,注释掉UseMAX
define,重新编译并执行。结果:没有例外
请注意,表单的标题现在显示为ftString
。
这就是原因:使用特定数字表示列大小意味着
RTL检索的表元数据导致客户端Field
被创建
作为TStringField
,您可以通过字符串赋值语句设置其值。
OTOH,当您指定MAX时,生成的客户端Field
的类型为ftMemo,
这是Delphi的BLOB类型之一,当你分配时
对于ftMemo字段的字符串值,您受Data.DB.Pas中的代码的支配,它使用TBlobStream
对记录缓冲区进行所有读取(和写入)操作。问题在于,据我所知,在经过大量实验和跟踪代码之后,TMemoField使用BlobStream的方式无法正确区分将字段内容更新为&#39;&#39;&#39;并将字段的值设置为Null
(如System.Variants中所示)。
简而言之,每当您尝试将TMemoField的值设置为空字符串时,实际发生的情况是字段的状态设置为Null,这就是导致q中的异常的原因。 AFAICS,这是不可避免的,所以无论如何也没有明显的解决方法。
我没有调查ftMemo
和ftString
之间的选择是由Delphi RTL代码还是它所依赖的MDAC(Ado)层做出的:我希望它实际上由{ {1}} TAdoQuery使用。
QED。请注意,这种系统的调试方法已经揭示了这一点 问题&amp;原因是很少的努力和零试验和错误,这是 我在q的评论中试图提出的建议。
另一点是,这个问题可以完全没有被追踪 求助于服务器端工具,包括SMSS分析器。没有必要使用分析器来检查客户端发送到服务器的内容 因为没有理由认为服务器返回错误 不对。这证实了我在客户端开始调查所说的话。
此外,使用RecordSet
ed Sql动态创建的表,通过简单观察应用程序的两次运行,可以在一个步骤中有效地隔离问题。
代码
IfDef
答案 1 :(得分:1)
在数据类型中使用MAX
时会出现问题。 varchar(MAX)
和nvarchar(MAX)
都利用此行为。删除MAX
并将其替换为大号(例如5000
)时,它会允许空字符串。