【问题标题】:Format a date according to ADO provider根据 ADO 提供程序格式化日期
【发布时间】:2011-01-13 20:37:17
【问题描述】:

我有一个使用 ADO 的 Delphi 2010 应用程序来支持可以是 SQL Server 或 MS Access 的数据库。使用参数化查询将 SQL 发送到数据库时,日期表示的差异得到了正确处理。但我偶尔也需要形成动态 SQL 并将其发送到数据库。

有没有办法让 TADOConnection 将我的日期格式化为适合当前数据库的文本字符串,或者询问连接以了解我应该如何格式化日期?否则,我将无法构建提供者名称和日期格式化函数的表格。

【问题讨论】:

    标签: sql-server delphi ms-access delphi-2010 ado


    【解决方案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) 的函数构造,因此不存在注入风险。
    猜你喜欢
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-14
    • 2019-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多