- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
简而言之,我如何知道哪些(主要和唯一)键唯一标识 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
查询记录?
最佳答案
我认为这不是 FireDAC 问题,而是一个更普遍的问题。
要实现您的目标,您首先需要获取查询(源表和源列)的所有列的元数据,这些列是其源表中唯一键的一部分。 (注意表和列的别名)
然后,按每个“外键”的源表和源列分组,您应该检查查询中是否有其源表的完整键(考虑多列 PK/AK 索引)。
为此,您应该获取每个源表的元数据并尝试匹配每个键列。
如果没有覆盖全键,则查询结果不能唯一(不保证)。
如果覆盖了完整键,则无论如何都必须检查查询结果的唯一性,因为根据查询语法,您可能会在结果中多次重复相同的键(在源表中是唯一的)。
小心!!
具有不同参数的相同查询可能具有唯一性或不唯一性的列。
select t1.PKColumn, t2.PKColumn
from Table1 t1
cross join (
select top @ExternalParameter *
from Table2
) t2
对于 ExternalParameter = 1 t1.PKColumn 在上面的查询中也是唯一的,否则它不会
如您所见,所有这些东西都需要大量额外的工作和 sql 查询..
我不是 Firebird 专家,所以我在 SQLServer 中进行了测试,但我认为您可以使用 Firebird 系统表 (Look here for some example) 获得相同的架构信息
在 SQLServer 中,您可以使用 sys.dm_exec_describe_first_result_set
提取元数据信息:
select column_ordinal, d.name, source_table, source_column, is_part_of_unique_key, is_identity_column, c.object_id, c.column_id
from sys.dm_exec_describe_first_result_set(N'SELECT CUSTOMERS.ID CUSTOMERID, CUSTOMERS.CODE CUSTOMERCODE, GROUPS.ID GROUPID FROM CUSTOMERS INNER JOIN GROUPS ON GROUPS.ID = CUSTOMERS.GROUP_ID',null,1) d
join sys.columns c on c.object_id = OBJECT_ID(d.source_table) and c.name=d.source_column
where (is_hidden=0 and is_part_of_unique_key=1 or is_identity_column=1)
这样您就可以提取有关要测试的列的所有索引的详细信息:
select *
from sys.indexes i
join sys.index_columns c on i.object_id = c.object_id and i.index_id = c.index_id
where c.object_id = @TestObjectID
and exists (
select 1
from sys.index_columns cc
where cc.object_id = c.object_id and cc.index_id = c.index_id and cc.column_id = @TestColumnID
)
在 delphi 中,像这样测试 key 的唯一性:
test_uniqe := 'if exists(select ' + SourceColumnList + ', count(*) from ' + SourceTable + ' group by ' + SourceColumnList + ' having count(*)>1) select 0 uk else select 1 uk
而这只是最简单的案例分析!考虑不是 PK/AK 索引但可能是对源表的唯一引用的标识列,考虑涉及多个键的字段..
这可能会成为一项非常困难的任务..
我认为您最好的选择是介于自动和手动之间。
关于sql - 如何在 TFDQuery 中找到唯一标识记录的数据库键?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45988007/
我需要将文本放在 中在一个 Div 中,在另一个 Div 中,在另一个 Div 中。所以这是它的样子: #document Change PIN
奇怪的事情发生了。 我有一个基本的 html 代码。 html,头部, body 。(因为我收到了一些反对票,这里是完整的代码) 这是我的CSS: html { backgroun
我正在尝试将 Assets 中的一组图像加载到 UICollectionview 中存在的 ImageView 中,但每当我运行应用程序时它都会显示错误。而且也没有显示图像。 我在ViewDidLoa
我需要根据带参数的 perl 脚本的输出更改一些环境变量。在 tcsh 中,我可以使用别名命令来评估 perl 脚本的输出。 tcsh: alias setsdk 'eval `/localhome/
我使用 Windows 身份验证创建了一个新的 Blazor(服务器端)应用程序,并使用 IIS Express 运行它。它将显示一条消息“Hello Domain\User!”来自右上方的以下 Ra
这是我的方法 void login(Event event);我想知道 Kotlin 中应该如何 最佳答案 在 Kotlin 中通配符运算符是 * 。它指示编译器它是未知的,但一旦知道,就不会有其他类
看下面的代码 for story in book if story.title.length < 140 - var story
我正在尝试用 C 语言学习字符串处理。我写了一个程序,它存储了一些音乐轨道,并帮助用户检查他/她想到的歌曲是否存在于存储的轨道中。这是通过要求用户输入一串字符来完成的。然后程序使用 strstr()
我正在学习 sscanf 并遇到如下格式字符串: sscanf("%[^:]:%[^*=]%*[*=]%n",a,b,&c); 我理解 %[^:] 部分意味着扫描直到遇到 ':' 并将其分配给 a。:
def char_check(x,y): if (str(x) in y or x.find(y) > -1) or (str(y) in x or y.find(x) > -1):
我有一种情况,我想将文本文件中的现有行包含到一个新 block 中。 line 1 line 2 line in block line 3 line 4 应该变成 line 1 line 2 line
我有一个新项目,我正在尝试设置 Django 调试工具栏。首先,我尝试了快速设置,它只涉及将 'debug_toolbar' 添加到我的已安装应用程序列表中。有了这个,当我转到我的根 URL 时,调试
在 Matlab 中,如果我有一个函数 f,例如签名是 f(a,b,c),我可以创建一个只有一个变量 b 的函数,它将使用固定的 a=a1 和 c=c1 调用 f: g = @(b) f(a1, b,
我不明白为什么 ForEach 中的元素之间有多余的垂直间距在 VStack 里面在 ScrollView 里面使用 GeometryReader 时渲染自定义水平分隔线。 Scrol
我想知道,是否有关于何时使用 session 和 cookie 的指南或最佳实践? 什么应该和什么不应该存储在其中?谢谢! 最佳答案 这些文档很好地了解了 session cookie 的安全问题以及
我在 scipy/numpy 中有一个 Nx3 矩阵,我想用它制作一个 3 维条形图,其中 X 轴和 Y 轴由矩阵的第一列和第二列的值、高度确定每个条形的 是矩阵中的第三列,条形的数量由 N 确定。
假设我用两种不同的方式初始化信号量 sem_init(&randomsem,0,1) sem_init(&randomsem,0,0) 现在, sem_wait(&randomsem) 在这两种情况下
我怀疑该值如何存储在“WORD”中,因为 PStr 包含实际输出。? 既然Pstr中存储的是小写到大写的字母,那么在printf中如何将其给出为“WORD”。有人可以吗?解释一下? #include
我有一个 3x3 数组: var my_array = [[0,1,2], [3,4,5], [6,7,8]]; 并想获得它的第一个 2
我意识到您可以使用如下方式轻松检查焦点: var hasFocus = true; $(window).blur(function(){ hasFocus = false; }); $(win
我是一名优秀的程序员,十分优秀!