我可以使用SS2014、OLEDB驱动程序和
Seattle和使用MAX作为列大小和特定数字(在我的例子中是4096)创建表时的行为差异。我想我会把这作为一种选择
答案是因为它不仅显示了如何系统地研究这种差异
但也可以识别
为什么?
出现这种差异(以及如何在将来避免这种差异)。
请参考并执行以下编写的代码,即
UseMAX
定义
忙碌的
在执行代码之前,立即在项目选项中启用“使用调试DCU”
显示所述异常发生在
Data.Win.ADODB
第4920行
Recordset.Fields[TField(FModifiedFields[I]).FieldNo-1].Value := Data
属于
TCustomADODataSet.InternalPost
调试评估窗口显示
Data
此时是
Null
.
接下来,请注意
update jdtest set NonNullFieldName = ''
在SSMS2014查询窗口中执行,无投诉(
Command(s) completed successfully.
),因此
事实是
数据
是
无效的
第4920行是导致问题的原因,下一个问题是“为什么?”
首先要注意的是,表单的标题正在显示
ftMemo
接下来,注释掉
UseMAX公司
定义、重新编译和执行。结果:无异常
请注意,现在正在显示表单的标题
ftString
.
这就是原因:使用特定的数字作为列大小意味着
RTL检索到的表元数据导致客户端
Field
待创建
作为一个
TStringField
,可以通过字符串赋值语句设置其值。
OTOH,指定MAX时,生成的客户端
领域
属于ftMemo类型,
这是Delphi的BLOB类型之一
如果将值设置为ftMemo字段,则数据中的代码将任由您摆布。数据库。Pas,它使用
TBlobStream
. 问题是,据我所知,在经过大量实验和代码跟踪之后,TMemoField使用BlobStream的方式无法正确区分将字段内容更新为“”和将字段值设置为
无效的
(与系统变型相同)。
简而言之,每当您尝试将TMemoField的值设置为空字符串时,实际发生的情况是字段的状态设置为Null,这就是导致q.AFAICS中出现异常的原因。这是不可避免的,因此对我来说,没有明显的解决方法。
我还没有调查
ftMemo公司
和
ftString公司
是由Delphi RTL代码或它所在的MDAC(Ado)层生成的:我希望它实际上是由
RecordSet
TAdoQuery使用。
QED。请注意,这种系统化的调试方法揭示了
问题(&A);只需很少的努力和零尝试和错误
我在q上的评论中试图提出的建议。
另一点是,这个问题完全可以在没有
求助于服务器端工具,包括SMSS探查器。不需要使用探查器来检查客户端发送到服务器的内容
因为没有理由假设服务器返回的错误
不正确。这证实了我所说的从客户端开始调查。
此外,使用动态创建的表,使用
IfDef
通过简单观察应用程序的两次运行,ed Sql可以在一个步骤中有效地隔离问题。
密码
uses [...] TypInfo;
[...]
implementation[...]
const
// The following consts are to create the table and insert a single row
//
// The difference between them is that scSqlSetUp1 specifies
// the size of the NonNullFieldName to 'MAX' whereas scSqlSetUp2 specifies a size of 4096
scSqlSetUp1 =
'CREATE TABLE [dbo].[JDTest]('#13#10
+ ' [ID] [int] NOT NULL primary key,'#13#10
+ ' [NonNullFieldName] VarChar(MAX) NOT NULL'#13#10
+ ') ON [PRIMARY]'#13#10
+ ';'#13#10
+ 'Insert JDTest (ID, [NonNullFieldName]) values (1, ''a'')'#13#10
+ ';'#13#10
+ 'SET ANSI_PADDING OFF'#13#10
+ ';';
scSqlSetUp2 =
'CREATE TABLE [dbo].[JDTest]('#13#10
+ ' [ID] [int] NOT NULL primary key,'#13#10
+ ' [NonNullFieldName] VarChar(4096) NOT NULL'#13#10
+ ') ON [PRIMARY]'#13#10
+ ';'#13#10
+ 'Insert JDTest (ID, [NonNullFieldName]) values (1, ''a'')'#13#10
+ ';'#13#10
+ 'SET ANSI_PADDING OFF'#13#10
+ ';';
scSqlDropTable = 'drop table [dbo].[jdtest]';
procedure TForm1.Test1;
var
AField : TField;
S : String;
begin
// Following creates the table. The define determines the size of the NonNullFieldName
{$define UseMAX}
{$ifdef UseMAX}
S := scSqlSetUp1;
{$else}
S := scSqlSetUp2;
{$endif}
ADOConnection1.Execute(S);
try
ADOQuery1.Open;
try
ADOQuery1.Edit;
// Get explicit reference to the NonNullFieldName
// field to make working with it and investigating it easier
AField := ADOQuery1.FieldByName('NonNullFieldName');
// The following, which requires the `TypInfo` unit in the `USES` list is to find out which exact type
// AField is. Answer: ftMemo, or ftString, depending on UseMAX.
// Of course, we could get this info by inspection in the IDE
// by creating persistent fields
S := GetEnumName(TypeInfo(TFieldType), Ord(AField.DataType));
Caption := S; // Displays `ftMemo` or `ftString`, of course
AField.AsString:= '';
ADOQuery1.Post; //<-- Exception raised while posting
finally
ADOQuery1.Close;
end;
finally
// Tidy up
ADOConnection1.Execute(scSqlDropTable);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Test1;
end;