【发布时间】:2018-02-09 19:07:24
【问题描述】:
简而言之,我如何知道哪些(主键和唯一键)唯一标识 FireDAC 查询记录?
我正在使用 Delphi 10.1 & FireDAC & Firebird。
我正在使用代码生成来生成一个包含我的应用程序的所有查询的文件。它是一个遗留应用程序,因此当前的数据库结构无处不在。设计它的人在设计它时没有知识(仍在学习)。所以我们没有一个连贯的系统来唯一地识别记录。其中一些在 autoinc int 生成器上。有些有另一个字段用于唯一标识。有些有组合。链接到这些字段的外键也不连贯。
所以。由于数据库的整体重构不是一个直接的选择,我们必须处理它。我能想到的最好方法是能够检索给定查询的每个可能的键。这将使我们能够系统地检索我们可以唯一标识查询和表中的记录的所有可能方式。这将为我们提供一个记录标识符(基本上是一组键)的抽象,我们可以在我们的应用程序中传递它,同时我们重构。
第一步是使用这种结构(为便于阅读而简化)生成数据库的虚拟表示(表、字段、字段类型、长度、键、外键):
TTable = class(TCollectionItem)
property Name : string;
property Fields : TFieldCollection;
property Keys : TKeyCollection;
end;
TField = class(TCollectionItem)
property Name : string;
property Table : TTable;
property Type : TFieldType;
property Size : integer;
property Required : Boolean;
end;
TKey = class(TCollectionItem)
property KeyType : TKeyType;
property Name : string;
property Table : TTable;
end;
TKeyType = (ktPrimary, ktUnique, ktForeign);
TKeyCollection = class(TCollection)
end;
TTableCollection = class(TCollection)
end;
TSchema = class(TObject)
property Tables : TTableCollection;
end;
其次,我实现了从这个模式生成代码,为应用程序的每个表生成一个类,以及所有可能的键。看起来像这样:
CUSTOMERS table
---------------
ID int (autoinc, pk PK_CUSTOMERS)
CODE int (uk UK_CODE)
NAME string
GROUP_ID int (fk on GROUPS.ID)
GROUPS table
------------
ID int (autoinc, pk PK_GROUPS)
NAME string
// Generated code (simplified for readability)
TCustomerPk = class(TKey)
property ID : Integer;
end;
TCustomerCode = class(TKey)
property Code : Integer;
end;
TCustomerKeys = class(TKeys)
property CustomerPk : TCustomerPk ...
property CustomerCode : TCustomerCode ...
end;
TCustomers = class(TTableBase)
property ID : TIntegerField;
property Code : TIntegerField;
property Name : TWideStringField;
property Keys : TCustomerKeys;
end;
TGroupsPK = class(TKey)
property ID : Integer;
end;
TGroupsKeys = class(TKeys)
property GroupsPk : TGroupsPk;
end;
TGroups = class(TTableBase)
property ID : TIntegerField;
property NAME : TStringField;
property Keys : TGroupKeys;
end;
现在表格已经完成,我攻击查询。我的目标是知道查询中存在哪些键。
为了知道这个查询有哪些键:
- 获取此查询从中获取数据的表的列表
- 遍历这些表的键
- 检查键的所有字段是否都存在于查询中
示例(为简单起见,跳过外键的大小写):
for lQueryField in lQuery.Fields do
begin
if not lTableList.Contains(lQueryField.Table)
lTableList.Add(lQueryField.Table)
end;
for lQueryTable in lTableList do
begin
for lKey in lQueryTable.Keys do
begin
lFound := true;
for lKeyField in lKey.Fields do
begin
if not lQuery.Fields.Contains(lKeyField) then lFound := false;
end;
if lFound then
// Query has key lKey
end;
end;
使用这种方法,并给出一个TFDQuery
SELECT CUSTOMERS.ID CUSTOMERID, CUSTOMERS.CODE CUSTOMERCODE, GROUPS.ID GROUPID FROM CUSTOMERS INNER JOIN GROUPS ON GROUPS.ID = CUSTOMERS.GROUP_ID
我们可以很容易地生成这段代码,因为我们知道这个查询有PK_CUSTOMERS 键和PK_GROUPS 键使用我们上面的测试:
TCustomersQuery = class(TQueryBase)
property CUSTOMERID : TIntegerField;
property GROUPID : TIntegerField;
property CUSTOMERCODE : TIntegerField;
property CustomerPk : TCustomerPk;
property CustomerCodeUk : TCustomerCode;
property GroupPk : TGroupPk;
end;
现在,我面临的挑战如下。我们知道TCustomersQuery 中的每条记录唯一标识CUSTOMERS 表中的一条记录。换句话说,TCustomersQuery 中的每条记录对应于CUSTOMERS 表中的一条不同的、非重复出现的记录。
但TCustomersQuery 中的每条记录并不能唯一标识GROUPS 表中的非重复记录。
换句话说,PK_CUSTOMERS 和 UK_CODE 键在此查询的上下文中仍然有效,但 PK_GROUPS 无效。
换句话说,PK_CUSTOMERS 和 UK_CODE 键的唯一性在此查询中被传达,而不是 PK_GROUPS 键的唯一性。
我想找出在任意查询的上下文中哪些键仍然有效。
FireDAC 在 IDE 中首次创建查询时会自动获取查询字段 ProviderFlags。它以某种方式知道哪些字段是主键的一部分。我想扩展它以了解适用于此查询的所有主键和唯一键。 我如何知道所有可能具有ProviderFlags = pfInKey 值的字段?
简而言之,我如何知道哪些(主键和唯一键)唯一标识了 FireDAC 查询记录?
【问题讨论】:
-
好问,+1。 FireDAC 的作者有时会在这里回答,所以也许这会引起他的注意,否则这里有一个固定的贡献者,@Victoria,他似乎对 FireDAC 非常了解,可能会提供帮助。
-
这不是 FireDAC 直接支持的(或支持,但仅适用于基表,不适用于连接表)。您的目标是获取 SQL 命令中使用的所有表的主键和唯一约束吗?如果是这样,对您来说是否会出现小幅性能下降(看来您是从命令生成 Delphi 类,所以我猜它不应该)?
-
嗨@Victoria。谢谢回答。是的,你是对的。我的目标是获取 SQL 查询的主键和唯一键。代码是预先生成的,因此最终用户没有性能问题。这里棘手的部分是知道哪个字段(或字段组合)代表查询中的唯一键/主键,并且可以用作唯一记录标识符。我能够使用元数据查询来检索表的键,但在这个级别的查询似乎不存在。
-
深入挖掘,您将能够(无需修改源代码)为获取的字段构建表名列表(但您将无法访问例如其列的关联实体( s) 未在命令输出中列出)并从此列表查询表约束。但请注意,我正在阅读比您更旧的 FireDAC,因此这可能已经改变(尽管我对此有点怀疑,因此 FireDAC 和用户都不需要有关其字段未获取到客户端的表的信息)。跨度>
-
@Victoria 我只对获取 SQL 查询中所有字段都存在的主键和唯一键感兴趣。但更重要的是,知道哪些键“唯一性”在此查询的上下文中仍然有效。类似于查找所有可能具有 ProviderFlags pfInKey 值的字段
标签: sql database delphi code-generation firedac