【问题标题】:Why does FireDAC ignore index name?为什么 FireDAC 忽略索引名称?
【发布时间】:2019-07-01 19:09:55
【问题描述】:

我正在尝试使用 FireDAC 在 SQL Server 数据库中创建一个表。但是,FireDAC 没有使用我提供的索引名称,而是使用了错误的索引名称,从而引发了异常并且没有创建表。 我做错了吗?如果没有,是否有解决方法?

请注意,我将有效的数据库架构名称 cnf 用于 TableName我特别需要在架构中创建表

最简单的测试用例:

var
  Connection: TFDConnection;
  Table: TFDTable;
begin
  Connection := TFDConnection.Create(nil);
  Table := TFDTable.Create(nil);
  try
    Connection.Params.Add ('DriverID=MSSQL');
    Connection.Params.Add ('OSAuthent=No');
    Connection.Params.Add ('User_Name=sa');
    Connection.Params.Add ('Password=XXXXXX');
    Connection.Params.Add ('Server=DAVE-DELL\MSSQLSERVER2016');
    Connection.Params.Add ('Database=PROJECT_DB');
    Connection.Params.Add ('MARS=No');

    Connection.Open;
    Table.Connection := Connection;

    Table.TableName := 'cnf.TestTable';
    Table.FieldDefs.Add ('TableID', ftAutoInc, 0, true);
    Table.FieldDefs.Add ('Field1', ftInteger, 0, true);
    Table.FieldDefs.Add ('Field2', ftstring, 100, true);

    Table.IndexDefs.Add ('PK_XYZ', 'TableID', [ixPrimary]);   // should use this index name!

    Table.CreateTable (true);

  finally
    Table.Free;
    Connection.Free;
  end;

end;

引发异常:

[FireDAC][Phys][ODBC][Microsoft][SQL Server Native Client 11.0][SQL Server]Incorrect syntax near '.'.

运行 SQL Server Profiler 显示 FireDAC 正在尝试使用以下 SQL 代码创建索引:

ALTER TABLE temp.TestTable ADD CONSTRAINT [cnf].[PK_TestTable] PRIMARY KEY (TableID)

当然,[cnf].[PK_TestTable] 在 T-SQL 中不是一个有效的索引名称,这是问题的症结所在。

  • 如果我删除Table.IndexDefs.Add 行,则表会正确创建,但没有索引。
  • 如果我将该行替换为以下内容,则会出现同样的问题:

    with Table.IndexDefs.AddIndexDef do begin
      Name := 'PK_XYZ';
      Options := [ixPrimary];
      Fields := 'TableID';
    end;
    
  • 如果我将设置表名替换为以下内容,则会出现同样的问题:

    Table.TableName := 'TestTable';
    Table.SchemaName := 'cnf';
    

为什么它使用自己的(错误的)索引名称,而不是我给它的名称?(即PK_XYZ

  • Embarcadero® Delphi 10.1 柏林版 24.0.25048.9432
  • SQL Server 2016 (SP2-CU4) - 13.0.5233.0 (X64)

【问题讨论】:

  • 如果您使用 MS OleDB 驱动程序而不是 ODBC 驱动程序,您会得到相同的行为吗?
  • @Martyn 一个好建议 - 有机会我会检查的。
  • 基于下面 Peter 的广泛回答,为什么不在这种情况下避免使用 FD 索引创建例程?在ExecSQL('Alter Table ...') 调用中创建索引
  • @JacalarRick 谢谢 - 我现在已经做到了,直到我可以进行更多实验。

标签: sql-server delphi firedac


【解决方案1】:

我做错了吗?
为什么它使用它自己的(错误的)索引名称,而不是我给它的名称?

你似乎做的一切都恰到好处。问题在于生成的 SQL 命令,因为您已对其进行了跟踪。 SQL Server doesn't allow schema name in constraint name 使用 ALTER TABLE 添加约束时。以这种方式创建的约束会自动成为相关表架构的一部分,但是您应该稍后在引用约束时使用架构名称:

SELECT OBJECT_ID('cnf.PK_XYZ')

现在哪里出了问题? FireDAC 使用TFDPhysCommandGenerator 及其祖先为特定的DBMS 生成SQL 命令。您对CreateTable 方法的调用导致对TFDPhysCommandGenerator.GetCreatePrimaryKey 的调用,它负责为主键生成SQL。它还包含以下代码:

sTab := GetFrom;
FConnMeta.DecodeObjName(sTab, rName, nil, [doUnquote]);
rName.FObject := 'PK_' + rName.FObject;
Result := 'ALTER TABLE ' + sTab + ' ADD CONSTRAINT ' +
  FConnMeta.EncodeObjName(rName, nil, [eoQuote, eoNormalize]) + ' PRIMARY KEY (';

此代码的作用是,它将您的完全限定表名 (sTab) 拆分为部分 (rName) 将 'PK_' 添加到表名并连接部分 (EncodeObjName ) 返回完全限定名称,然后将其用作主键的约束名称。现在我们可以清楚地看到命令生成器忽略了您的索引名称并生成了错误的 T-SQL。这可能是一个错误,也可能只是一个不受支持的功能。 EMBT 必须对此做出决定。我建议将其报告为错误。

有解决办法吗?

是的,您可以挂钩有问题的方法,也可以在自己的派生类中覆盖它。实现这些都不是微不足道的,由于法律问题,我不会在这里扩展它,因为我必须复制原始的 FireDAC 代码。

至于语法错误,在DecodeObjName 之后将这些行添加到 'TFDPhysCommandGenerator.GetCreatePrimaryKey' 实现将解决问题:

rName.FCatalog := '';
rName.FSchema := '';
rName.FBaseObject := '';
rName.FLink := '';

修复约束名称会比这更麻烦,因为该方法只接收索引列名称作为参数,并且无法明显访问原始IndexDefs,您可以使用索引名称作为主键约束名称。从那里获得对索引名称的访问还可以让您摆脱将表名称解码/编码为索引名称。但是,此过程对于 SQL Server 以外的其他 DMBS 可能是必不可少的。

PS:如果所有问题中只有一半是这样写的……谢谢你提出这个精彩的问题。

【讨论】:

  • 感谢您的精彩回答!我从未接触过 FireDAC 的内部部分,所以这很有帮助。
猜你喜欢
  • 2010-11-01
  • 2012-11-09
  • 1970-01-01
  • 2021-12-21
  • 2014-09-28
  • 2014-03-31
  • 2011-04-13
  • 1970-01-01
  • 2021-02-01
相关资源
最近更新 更多