根据 ADO 提供程序格式化日期
Posted
技术标签:
【中文标题】根据 ADO 提供程序格式化日期【英文标题】:Format a date according to ADO provider 【发布时间】:2011-01-13 20:37:17 【问题描述】:我有一个使用 ADO 的 Delphi 2010 应用程序来支持可以是 SQL Server 或 MS Access 的数据库。使用参数化查询将 SQL 发送到数据库时,日期表示的差异得到了正确处理。但我偶尔也需要形成动态 SQL 并将其发送到数据库。
有没有办法让 TADOConnection 将我的日期格式化为适合当前数据库的文本字符串,或者询问连接以了解我应该如何格式化日期?否则,我将无法构建提供者名称和日期格式化函数的表格。
【问题讨论】:
【参考方案1】:您也应该能够在动态 sql 中使用参数。我已经在我自己的 OPF 框架的多个版本中做到了这一点。
只需使用参数编写 SQL 语句,将其作为字符串分配给 TAdoQuery(或 TAdoCommand)的 SQL 文本。然后该组件应解析您的文本并为您设置参数集合。之后,您应该能够将值分配给您的参数并调用 Open 或 Execute...
给你一个想法:
DS := DatasetClass.Create( self.Connection );
try
DS.QueryText := SQL.Text;
FillSelectParams( DS );
DS.Open;
try
...
finally
DS.Close;
end;
finally
DS.Free;
end;
其中 FillSelectParams 调用以下 FillParams 过程:
procedure TSQLDataManager.FillParams(ADS: TCustomDataset);
var
i: integer;
ParamName: string;
Attr: TCustomDomainAttribute;
Ref: TCustomDomainObject;
Value: Variant;
begin
for i := 0 to ADS.ParamCount - 1 do begin
Value := Null;
ParamName := ADS.ParamName[i];
if ParamName = 'Id' then begin
ParamName := 'Identity';
end;
Attr := CDO.AttrByName[ParamName];
if Attr <> nil then begin
Value := ADS.AdjustParamValue( Attr );
end else begin
Ref := CDO.ReferenceByName[ParamName];
if ( Ref <> nil ) and ( Ref.Identity <> C_UnassignedIdentity ) then begin
Value := Ref.Identity;
end;
end;
if Value <> Null then begin
ADS.ParamValue[i] := Value;
end;
end;
end;
在这种情况下,参数值取自自定义域对象 (CDO),但您可以在此处替换您自己的风格。
AdjustParamValue 函数负责几个转换。它已在使用的 TCustomDataSet 类后代的 ADO 版本中实现,以处理具有不同 TDataSet 后代的组件变化,但 SQL 数据库类型在任何地方都没有发挥作用:
function TADODCDataset.AdjustParamValue(Attr: TCustomDomainAttribute): Variant;
begin
if Attr is TIdentityAttribute then begin
if Attr.AsInteger = 0 then begin
Result := Null;
end else begin
Result := Attr.Value;
end;
end else if Attr is TBooleanAttribute then begin
if Attr.AsBoolean then begin
Result := Integer( -1 );
end else begin
Result := Integer( 0 );
end;
end else if Attr is TDateTimeAttribute then begin
if Attr.AsDateTime = 0 then begin
Result := Null;
end else begin
Result := Attr.Value;
end;
end else if Attr is TEnumAttribute then begin
Result := Attr.AsString
end else begin
Result := Attr.Value;
end;
end;
【讨论】:
谢谢,如果我无法调整现有代码,我认为像这样的系统是我必须要做的。我的想法是创建自己的 TParameters 对象(不是由数据集从参数化 SQL 生成的对象),然后将其传递给与 FillParams 方法非常相似的绑定函数。 这就是我最终要做的。【参考方案2】:Larry,我会给你一个 SQL Server 部分的答案。
您可以使用sys.syslanguages
系统视图获取有关安装在sql server中的语言的信息,此视图返回的列之一称为dateformat
,表示日期顺序,例如DMY。
还可以使用@@langid
(它返回当前正在使用的语言的本地语言标识符 (ID)。)函数,您可以编写类似这样的内容来获取 Sql Server 使用的当前日期格式。
select dateformat from sys.syslanguages where langid=@@langid
所以现在你将有一个字符串,你可以在 delphi 中解析它来格式化你的日期。
另一种选择是选择一种 SQL Server 的预定义格式,并在 SQL 语句中使用 CONVERT
函数将字符串转换为日期。
查看这个使用 ISO 格式转换的示例
//The ISO format is yyyymmdd so i can use the FormatDateTime function to convert any TdateTime to a Iso format sdatetiem string
DateStr:=FormatDateTime('YYYYMMDD',Now);
//Now construct the sql server sentence
SqlSentence:=Format('UPDATE MyTable SET DateField=CONVERT(DATETIME,%s,112)',QuotedStr(DateStr));
【讨论】:
啊 - ISO 格式是 YYYY-MM-DD,而不是 YYYYMMDD。 en.wikipedia.org/wiki/ISO_8601 哎呀,我没想到即使在 SQL Server 中也必须处理多种格式。如果在配置为不同语言的系统上运行,它会不接受标准日期吗? @TomTom,查看这个链接msdn.microsoft.com/en-us/library/ms187928.aspx,当我说ISO format
时,我指的是Sql server的112代码。
@Larry 我向您推荐第二个选项,使用CONVERT
函数,将适用于 SQL Server 的任何语言设置。
@TomTom:ISO-8601 是 YYYY-MM-DD 或 YYYYMMDD。 ISO-8601 在编码信息的格式和精度方面非常灵活。 ISO-8601 的关键是参考日期和交换约定(例如,日期总是连续的)。【参考方案3】:
尝试使用 SQL Server 和 Access OLEDB 提供程序可能支持的 ODBC 日期转义序列。例如:
d '2011-01-14'
【讨论】:
如果没有其他原因,我会试一试,以了解它是否有效。【参考方案4】:正如 Marjan 所建议的,您应该始终使用参数。 (尝试搜索 SQL-injection)
使用参数的另一个原因是查询计划可以在 sql server 上重复使用。 如果生成的第一条语句是 SELECT * from customer where created>'2010-12-21' 并且生成的下一条语句是 SELECT * from customer where created>'2010-12-22',则生成/编译查询优化器/计划两次(不同的说法)。但是,如果语句两次都是 SELECT * from customer where created>?,则计划被重用 ->(稍微)在 SQL 服务器上降低压力。
我只是想要一个快速而肮脏的解决方案,我尝试过的所有 SQL(实现)(我没有尝试过 Access)都可以接受并理解 ISO 日期格式(例如,'2010-12-21')
【讨论】:
谢谢,我很清楚 SQL 注入问题。在这种情况下,SQL sn-p 由具有签名 DateWhere(AFrom, ATo: TDate) 的函数构造,因此不存在注入风险。以上是关于根据 ADO 提供程序格式化日期的主要内容,如果未能解决你的问题,请参考以下文章