In short, how do you know which (primary and unique) keys uniquely identify a FireDAC request record?
I am using Delphi 10.1 and FireDAC and Firebird.
I use code generation to create a file containing all the requests of my application. This is an outdated application, so the current database structure is everywhere. The person who designed it did not know (was still studying) when he designed it. Thus, we do not have a coherent system for the unique identification of records. Some of them are in the autoinc int generator. Some have another field for unique identification. Some have combinations. And foreign keys that refer to these fields are also not consistent.
So. Since general database refactoring is not an immediate option, we must deal with it. The best way I could understand is to get all possible keys for a given request. This will allow us to systematically systematically identify all possible ways to uniquely identify the record in the query and table. This will give us an abstraction of the record identifier (mainly a set of keys), which we could pass in our application, and we refactoring.
The first step was to create a virtual database view (table, fields, field types, lengths, keys, foreign keys) with this structure (simplified for readability):
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;
Secondly, I was able to create code from this schema for each application table along with all possible keys. Looks like that:
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 ProviderFlags IDE. - , . , , . , ProviderFlags = pfInKey?
, , ( ) FireDAC?