-1

(SQL Server 2012、Delphi XE2、DataSet、DBGrid 和 adoquery)

我有一个包含这些列的简单表:

id entering, exiting, description

entering以及exiting获取默认值 0 的字段

创建新记录时的这些默认值不会出现在 Delphi 中。假设我们只有 5 个描述字段到进入夏季的字段。当录制没有问题时。正常的 şartlarda 不会出现短缺。您可以继续对其他记录进行操作。

但是你输入你刚刚输入的值添加的字段并不是什么新鲜事,而且在尝试记录的过程中会出现错误。0 似乎已在断开连接的区域打开,不再是错误。

这些默认值很少出现而不是错误。

我想知道我如何解决这个问题?

4

1 回答 1

0

如果您的问题是如何从服务器获取默认列值,下面的代码显示了在服务器是 MS Sql 服务器的情况下执行此操作的两种方法,以及如何通过其OnNewRecord事件将它们提供给表中的新行。 OnNewRecord是数据集事件,您应该在插入记录时使用它们来设置记录的字段值。

为了最大程度地减少误解的可能性,代码是自包含的:它创建一个新表,其中包含一个 Identity 列和另外三个具有默认值的列,然后使用两种不同的方法向其中添加数据行。

注意: Delphi 的 TField 及其后代有一个AutoGenerateValue属性,将其设置arDefault应该从服务器检索默认列/字段值。在 Delphi7 时代,执行此操作的服务器端功能是 AFAIK 仅在 DBTables 单元中为长期过时的 BDE 实现的。特别是 Delphi 的 ADO 数据集类型不支持它。我没有检查是否AutoGenerateValue适用于其他更新的 Delphi 版本和数据集类型。

另外,请注意创建表的 Sql 语句之前调用了 DROP TABLE,为了避免第一次执行时出错,您需要暂时注释掉 DROP TABLE 部分。

代码:

unit adodefaultsu;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, DBCtrls, Grids, DBGrids, DB, ADODB, Variants, StdCtrls;

type
  TForm1 = class(TForm)
    ADOConnection1: TADOConnection;
    ADOQuery1: TADOQuery;
    DataSource1: TDataSource;
    DBGrid1: TDBGrid;
    DBNavigator1: TDBNavigator;
    Button1: TButton;
    ADOQuery2: TADOQuery;
    ADOQuery1id: TAutoIncField;
    ADOQuery1f1: TIntegerField;
    ADOQuery1f2: TIntegerField;
    ADOQuery1description: TStringField;
    DataSource2: TDataSource;
    DBGrid2: TDBGrid;
    ADODataSet1: TADODataSet;
    procedure FormCreate(Sender: TObject);
    procedure ADOQuery1NewRecord(DataSet: TDataSet);
    procedure Button1Click(Sender: TObject);
  private
    function GetColumnDefault(Field: TField): Variant;
    function GetColumnDefault2(Field: TField): Variant;
  public
    procedure CreateAndOpenTable;
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

const

  scSqlCreateTable =
   'DROP TABLE Table1;'#13#10
   + 'CREATE TABLE [dbo].[Table1]('#13#10
   + '  [id] [int] IDENTITY(1,1) NOT NULL,'#13#10
   + '  [f1] [int] NOT NULL CONSTRAINT [DF_Table1_f1]  DEFAULT ((1)),'#13#10
   + '  [f2] [int] NOT NULL CONSTRAINT [DF_Table1_f2]  DEFAULT ((2)),'#13#10
   + '  [description] [varchar](40) default('''')'#13#10
   + ') ON [PRIMARY]';

  scSqlInsertRow = 'INSERT table1 (f1, description) VALUES(5, ''first'')';

  scSqlSelect = 'select * from table1';

  scSqlGetColumnDefault =
  'SELECT object_definition(default_object_id) AS definition'#13#10
   + 'FROM   sys.columns'#13#10
   + 'WHERE  name      =''%s'''#13#10
   + 'AND    object_id = object_id(''dbo.%s'')';


procedure TForm1.FormCreate(Sender: TObject);
begin
    if not AdoDataSet1.Active then
       AdoConnection1.OpenSchema(siColumns, VarArrayOf(['MATest', 'dbo', 'Table1']), EmptyParam, AdoDataSet1);

end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  CreateAndOpenTable;
end;


function ExtractDefaultValue(Field : TField; const Input : String) : String;
begin
  //  The Input value is the default value for the column, surrounded by two
  //  sets of parentheses and, in the case of a Char column, by leading and
  //  trailing single-quote characters

  Result := Input;

  // remove leading and trailing parentheses
  while (Result <> '') and (Result[1] = '(') do
    Result := Copy(Result, 2, Length(Result) - 2);

  // for string fields, remove leading and trailing single-quotes
  if Field.DataType in [ftString] then begin
    if (Result <> '') and (Result[1] = '''') then
      Result := Copy(Result, 2, Length(Result) - 2);
  end;
end;

function TForm1.GetColumnDefault(Field : TField) : Variant;
var
  Sql : String;
  sValue : String;
begin
  try
    //  First, contruct and execute a query to retrieve the default value
    //  for the Field's server column
    Sql := Format(scSqlGetColumnDefault, [Field.FieldName, 'Table1']);
    if AdoQuery2.Active then
      AdoQuery2.Close;
    AdoQuery2.Sql.Text := Sql;
    AdoQuery2.Open;

    //  WARNING: The following code has only been tested with Int and VarChar() columns
    //  and may require extra code for other column types

    sValue := AdoQuery2.Fields[0].AsString;
    Result := ExtractDefaultValue(Field, sValue);
    Field.Value := Result;
  finally
    AdoQuery2.Close;
  end;
end;

function TForm1.GetColumnDefault2(Field : TField) : Variant;
var
  Sql : String;
  sValue : String;
begin

  //  First, open the schema for the server via the AdoConnection
  if not AdoDataSet1.Active then
     AdoConnection1.OpenSchema(siColumns, VarArrayOf(['MATest', 'dbo', 'Table1']), EmptyParam, AdoDataSet1);

  //  WARNING: The following code has only been tested with Int and VarChar() columns
  //  and may require extra code for other column types

  if not AdoDataSet1.Locate('COLUMN_NAME', Field.FieldName, [loCaseInsensitive]) then
    raise Exception.CreateFmt('GetColumnDefault2: Field % not found in schema', [Field.FieldName]);

  sValue := AdoDataSet1.FieldByName('COLUMN_DEFAULT').AsString;

  Result := ExtractDefaultValue(Field, sValue);
  Field.Value := Result;
end;

procedure TForm1.ADOQuery1NewRecord(DataSet: TDataSet);
begin
  GetColumnDefault2(DataSet.FieldByName('f1'));
  GetColumnDefault2(DataSet.FieldByName('f2'));
  GetColumnDefault2(DataSet.FieldByName('description'));
end;

procedure TForm1.CreateAndOpenTable;
begin
  AdoQuery1.SQL.Text := scSqlCreateTable;
  AdoQuery1.ExecSQL;

  AdoQuery1.SQL.Text := scSqlInsertRow;
  AdoQuery1.ExecSQL;

  AdoQuery1.Sql.Text := scSqlSelect;
  AdoQuery1.Open;

  //  Next, insert a second row by the equivalent of clicking the
  //  DbNavigator's '+' button

  AdoQuery1.Insert;
  AdoQuery1.Post;
end;

end.

上面的代码使用第二种方法来获取列默认值,即从服务器的数据库模式中检索它们。要使用此代码,请将 OnNewRecord 代码更改为引用GetColumnDefault2而不是GetColumnDefault. 这种方法可能更可取,因为它只涉及获取一次默认值,然后 AdoDataSet1 缓存它们。

上面的代码是为 Delphi7 编写的,但也适用于更高版本。

如果这回答了您的问题,我将尝试编辑您的问题以使其更清晰。

最后,在没有警告 Sql Injection 风险的情况下,这些天似乎没有涉及在客户端应用程序中更改 Sql 的 Delphi 答案是完整的 - 请参阅https://en.wikipedia.org/wiki/Sql_injection

于 2016-02-01T12:33:13.037 回答