(SQL Server 2012, Delphi XE2, DataSet, DBGrid and adoquery(
我有一个简单的表格,其中包含这些列:
id entering, exiting, description
entering
并exiting
字段以获取默认值 0
创建新记录时的这些默认值不会显示在 Delphi 中。假设我们只有 5 个描述字段进入夏季。录制时不是问题。在正常的萨特拉达中没有出现短缺。您可以继续对其他记录进行操作。
但是你输入的值你添加的只是输入字段并不是什么新鲜事,并且错误发生在试图在记录中看不见的过程中。 0 似乎已在连接断开的区域打开,不再是错误。
这些默认值很少出现,而不是错误。
我想知道我如何解决这个问题?
如果您的问题是如何从服务器获取默认列值,下面的代码显示了两种执行此操作的方法,其中服务器是 MS Sql Server,以及如何通过其 OnNewRecord
事件将它们提供给表中的新行。 OnNewRecord
是数据集事件,用于在插入记录时设置记录的字段值。
为了尽量减少误解的可能性,代码旨在自包含:它创建一个新表,其中包含一个 Identity 列和其他三个具有默认值的表,然后使用两种不同的方法向其添加数据行。
注意:Delphi 的 TField 及其后代具有 AutoGenerateValue
属性,将其设置为 arDefault
应该从服务器检索默认列/字段值。 在 Delphi7 时代,执行此操作的服务器端功能仅在 DBTables 单元中实现,用于长期过时的 BDE。 特别是德尔福的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 的 Delphi 答案似乎没有关于 Sql 注入风险的警告 - 请参阅 https://en.wikipedia.org/wiki/Sql_injection。