如何使用 dbExpress 从 Firebird 获取表描述(字段和类型)
Posted
技术标签:
【中文标题】如何使用 dbExpress 从 Firebird 获取表描述(字段和类型)【英文标题】:How can I get the table description (fields and types) from Firebird with dbExpress 【发布时间】:2012-08-17 16:50:34 【问题描述】:我编写了一个工具,用于使用 TSQLConnection 的 GetTableNames 和 GetFieldNames 方法显示数据库结构。如何获取类似于以下列表的每个字段名称的类型(这是构建表所需的 DDL 的一部分)?
TABLE: ARTICLES
ID INTEGER NOT NULL
PRINTED SMALLINT DEFAULT 0
ACADEMIC SMALLINT
RELEVANCE SMALLINT
SOURCE VARCHAR(64) CHARACTER SET WIN1251 COLLATE WIN1251
NAME VARCHAR(128) CHARACTER SET WIN1251 COLLATE WIN1251
FILENAME VARCHAR(128) CHARACTER SET WIN1251 COLLATE WIN1251
NOTES VARCHAR(2048) CHARACTER SET WIN1251 COLLATE WIN1251
【问题讨论】:
尝试寻找Detailed table field info
或查看this FAQ
。
@TLama:详细的表字段信息为我提供了必要的线索 - 迭代系统表肯定比使用一条记录打开查询并确定该查询的字段类型要好得多。我怎么奖励你?也许通过代码示例将您的评论变成答案。
谢谢!我会发布答案,但我无法验证结果(我只是希望文章中的查询有效:-),这就是我发表评论的原因。随意发布并接受您自己的答案。将它保留在这里会很有帮助,因为我们不知道我提到的网站会存在多久。
DBX 是关于对不同引擎的兼容性... TBXTable 有属性 ValueType[const Ordinal: TInt32]: TDBXValueType read GetValueType;将 reault 转换为特定于数据库的脚本可能是单独的任务。对于基本的 FB 类型,TIBExtract 组件可能会起作用。
如果您想绝对使用 "SELECT * FROM rdb$ ..." ,以下解决方案不适合您。但是如果你想要一个简单快速的方法,你可以使用带有“CreateProcess(nil, Pchar('isql.exe' +' '+ Params)”的 isql.exe 和 Params 指向一个文件,例如 myTableFieldinfo.sql。所有信息您需要在文件 myTableFieldinfo.sql 中可用
【参考方案1】:
这是不完整的(因为我从未使用过 Firebird 数组数据类型)并且没有经过太多测试,但也许它会给你一个很好的起点:
SELECT
RF.RDB$FIELD_NAME FIELD_NAME,
CASE F.RDB$FIELD_TYPE
WHEN 7 THEN
CASE F.RDB$FIELD_SUB_TYPE
WHEN 0 THEN 'SMALLINT'
WHEN 1 THEN 'NUMERIC(' || F.RDB$FIELD_PRECISION || ', ' || (-F.RDB$FIELD_SCALE) || ')'
WHEN 2 THEN 'DECIMAL'
END
WHEN 8 THEN
CASE F.RDB$FIELD_SUB_TYPE
WHEN 0 THEN 'INTEGER'
WHEN 1 THEN 'NUMERIC(' || F.RDB$FIELD_PRECISION || ', ' || (-F.RDB$FIELD_SCALE) || ')'
WHEN 2 THEN 'DECIMAL'
END
WHEN 9 THEN 'QUAD'
WHEN 10 THEN 'FLOAT'
WHEN 12 THEN 'DATE'
WHEN 13 THEN 'TIME'
WHEN 14 THEN 'CHAR(' || (TRUNC(F.RDB$FIELD_LENGTH / CH.RDB$BYTES_PER_CHARACTER)) || ') '
WHEN 16 THEN
CASE F.RDB$FIELD_SUB_TYPE
WHEN 0 THEN 'BIGINT'
WHEN 1 THEN 'NUMERIC(' || F.RDB$FIELD_PRECISION || ', ' || (-F.RDB$FIELD_SCALE) || ')'
WHEN 2 THEN 'DECIMAL'
END
WHEN 27 THEN 'DOUBLE'
WHEN 35 THEN 'TIMESTAMP'
WHEN 37 THEN 'VARCHAR(' || (TRUNC(F.RDB$FIELD_LENGTH / CH.RDB$BYTES_PER_CHARACTER)) || ')'
WHEN 40 THEN 'CSTRING' || (TRUNC(F.RDB$FIELD_LENGTH / CH.RDB$BYTES_PER_CHARACTER)) || ')'
WHEN 45 THEN 'BLOB_ID'
WHEN 261 THEN 'BLOB SUB_TYPE ' || F.RDB$FIELD_SUB_TYPE
ELSE 'RDB$FIELD_TYPE: ' || F.RDB$FIELD_TYPE || '?'
END FIELD_TYPE,
IIF(COALESCE(RF.RDB$NULL_FLAG, 0) = 0, NULL, 'NOT NULL') FIELD_NULL,
CH.RDB$CHARACTER_SET_NAME FIELD_CHARSET,
DCO.RDB$COLLATION_NAME FIELD_COLLATION,
COALESCE(RF.RDB$DEFAULT_SOURCE, F.RDB$DEFAULT_SOURCE) FIELD_DEFAULT,
F.RDB$VALIDATION_SOURCE FIELD_CHECK,
RF.RDB$DESCRIPTION FIELD_DESCRIPTION
FROM RDB$RELATION_FIELDS RF
JOIN RDB$FIELDS F ON (F.RDB$FIELD_NAME = RF.RDB$FIELD_SOURCE)
LEFT OUTER JOIN RDB$CHARACTER_SETS CH ON (CH.RDB$CHARACTER_SET_ID = F.RDB$CHARACTER_SET_ID)
LEFT OUTER JOIN RDB$COLLATIONS DCO ON ((DCO.RDB$COLLATION_ID = F.RDB$COLLATION_ID) AND (DCO.RDB$CHARACTER_SET_ID = F.RDB$CHARACTER_SET_ID))
WHERE (RF.RDB$RELATION_NAME = :TABLE_NAME) AND (COALESCE(RF.RDB$SYSTEM_FLAG, 0) = 0)
ORDER BY RF.RDB$FIELD_POSITION;
【讨论】:
此代码可能有效,但在我使用的 Firebird 版本 (1.5) 中,我收到一条关于第 35 行的错误消息,即 IIF(COALESCE(RF.RDB$NULL_FLAG, 0 ) = 0 部分。 这可能是因为 Firebird 1.5 不支持IIF
。您可以将其替换为功能等效的 CASE
语句:CASE COALESCE(RF.RDB$NULL_FLAG, 0) WHEN 0 NULL ELSE 'NOT NULL' END
。
我接受您的回答,但有两个警告:(1)上面引用的行在“NULL”之前缺少“THEN”一词,应该引用“NULL”; (2) 也没有 TRUNC 函数,所以我将删除该关键字并在程序本身中执行 TRUNC。【参考方案2】:
使用对 RDB$ 表的直接访问。例如:
SELECT * FROM rdb$relations
将为您提供数据库中所有表的列表。
SELECT
*
FROM
rdb$relation_fields rf JOIN rdb$fields f
ON f.rdb$field_name = rf.rdb$field_source
WHERE
rf.rdb$relation_name = :RN
将生成给定表的所有字段的列表 带有字段类型的信息。参数 RN 是表的名称。
使用 RDB$tables 中的信息可以轻松构建 DDL 陈述。下面的查询为您提供了一个提示:
SELECT
TRIM(rf.rdb$field_name) || ' ' ||
IIF(rdb$field_source LIKE 'RDB$%',
DECODE(f.rdb$field_type,
8, 'INTEGER',
12, 'DATE',
37, 'VARCHAR',
14, 'CHAR',
7, 'SMALLINT'),
TRIM(rdb$field_source)) ||
IIF((rdb$field_source LIKE 'RDB$%') AND (f.rdb$field_type IN (37, 14)),
'(' || f.rdb$field_length || ')',
'') ||
IIF((f.rdb$null_flag = 1) OR (rf.rdb$null_flag = 1),
' NOT NULL', '')
FROM
rdb$relation_fields rf JOIN rdb$fields f
ON f.rdb$field_name = rf.rdb$field_source
WHERE
rf.rdb$relation_name = '<put_your_table_name_here>'
【讨论】:
参数RN的值是多少? 在单词 JOIN 之后有一个小的拼写错误 - 应该是 rdb$fields。无论如何,查询没有返回我正在寻找的内容。我会投票赞成答案,但这不是答案。【参考方案3】:通过TLama提供的链接,我找到了自己的解决方案,和上面的解决方案有些相似,但更简单。
SELECT R.RDB$FIELD_NAME AS field_name,
CASE F.RDB$FIELD_TYPE
WHEN 7 THEN 'SMALLINT'
WHEN 8 THEN 'INTEGER'
WHEN 9 THEN 'QUAD'
WHEN 10 THEN 'FLOAT'
WHEN 11 THEN 'D_FLOAT'
WHEN 12 THEN 'DATE'
WHEN 13 THEN 'TIME'
WHEN 14 THEN 'CHAR'
WHEN 16 THEN 'INT64'
WHEN 27 THEN 'DOUBLE'
WHEN 35 THEN 'TIMESTAMP'
WHEN 37 THEN 'VARCHAR'
WHEN 40 THEN 'CSTRING'
WHEN 261 THEN 'BLOB'
ELSE 'UNKNOWN'
END AS field_type,
F.RDB$FIELD_LENGTH AS field_length,
CSET.RDB$CHARACTER_SET_NAME AS field_charset
FROM RDB$RELATION_FIELDS R
LEFT JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME
LEFT JOIN RDB$CHARACTER_SETS CSET ON F.RDB$CHARACTER_SET_ID = CSET.RDB$CHARACTER_SET_ID
WHERE R.RDB$RELATION_NAME= :p1
ORDER BY R.RDB$FIELD_POSITION
p1 是作为参数传递给查询的表名。
在上下文中,我有一个树视图,它的节点是给定数据库的表名;对于每个节点,子节点是字段及其定义。
sqlcon.GetTableNames (dbTables); // sqlcon is the TSQLConnection
tv.items.Clear;
for i:= 1 to dbTables.count do
begin
node:= tv.items.Add (nil, dbTables[i - 1]);
with qFields do // the above query
begin
params[0].asstring:= dbTables[i - 1];
open;
while not eof do
begin
tv.items.addchild (node, trim (fieldbyname ('field_name').asstring) + ', ' +
trim (fieldbyname ('field_type').asstring) + ', ' +
fieldbyname ('field_length').asstring + ', ' +
fieldbyname ('field_charset').asstring);
next
end;
close
end
end;
这是正在运行的程序的屏幕截图。我意识到格式与我引用的 DDL 不同,但每个字段的含义很明显(至少对我而言,这是我私人使用的程序)。
【讨论】:
@TOndrej:怎么了?也许不完整。错误的是字符集应该是 Win1255 但我在定义数据库时并不知道更好,我知道现在更改为时已晚。 与我的回答比较。在某些情况下,您会得到错误的数据类型,以及多字节字符集字段的错误长度。不过,它可能会巧合地为您的特殊情况报告正确的值。【参考方案4】:我对第一个选项进行了一点改动以支持按字段计算,添加 field_position 并制作了一个视图以使其更容易。
CREATE VIEW TABLES (
TABLE_NAME,
FIELD_NAME,
FIELD_POSITION,
FIELD_TYPE,
FIELD_NULL,
FIELD_CHARSET,
FIELD_COLLATION,
FIELD_DEFAULT,
FIELD_CHECK,
FIELD_DESCRIPTION
)
AS
SELECT
RF.RDB$RELATION_NAME,
RF.RDB$FIELD_NAME FIELD_NAME,
RF.RDB$FIELD_POSITION FIELD_POSITION,
CASE F.RDB$FIELD_TYPE
WHEN 7 THEN
CASE F.RDB$FIELD_SUB_TYPE
WHEN 0 THEN 'SMALLINT'
WHEN 1 THEN 'NUMERIC(' || F.RDB$FIELD_PRECISION || ', ' || (-F.RDB$FIELD_SCALE) || ')'
WHEN 2 THEN 'DECIMAL'
END
WHEN 8 THEN
CASE F.RDB$FIELD_SUB_TYPE
WHEN 0 THEN 'INTEGER'
WHEN 1 THEN 'NUMERIC(' || F.RDB$FIELD_PRECISION || ', ' || (-F.RDB$FIELD_SCALE) || ')'
WHEN 2 THEN 'DECIMAL'
END
WHEN 9 THEN 'QUAD'
WHEN 10 THEN 'FLOAT'
WHEN 12 THEN 'DATE'
WHEN 13 THEN 'TIME'
WHEN 14 THEN 'CHAR(' || (TRUNC(F.RDB$FIELD_LENGTH / CH.RDB$BYTES_PER_CHARACTER)) || ') '
WHEN 16 THEN
CASE F.RDB$FIELD_SUB_TYPE
WHEN 0 THEN 'BIGINT'
WHEN 1 THEN 'NUMERIC(' || F.RDB$FIELD_PRECISION || ', ' || (-F.RDB$FIELD_SCALE) || ')'
WHEN 2 THEN 'DECIMAL'
END
WHEN 27 THEN 'DOUBLE'
WHEN 35 THEN 'TIMESTAMP'
WHEN 37 THEN
IIF (COALESCE(f.RDB$COMPUTED_SOURCE,'')<>'',
'COMPUTED BY ' || CAST(f.RDB$COMPUTED_SOURCE AS VARCHAR(250)),
'VARCHAR(' || (TRUNC(F.RDB$FIELD_LENGTH / CH.RDB$BYTES_PER_CHARACTER)) || ')')
WHEN 40 THEN 'CSTRING' || (TRUNC(F.RDB$FIELD_LENGTH / CH.RDB$BYTES_PER_CHARACTER)) || ')'
WHEN 45 THEN 'BLOB_ID'
WHEN 261 THEN 'BLOB SUB_TYPE ' || F.RDB$FIELD_SUB_TYPE
ELSE 'RDB$FIELD_TYPE: ' || F.RDB$FIELD_TYPE || '?'
END FIELD_TYPE,
IIF(COALESCE(RF.RDB$NULL_FLAG, 0) = 0, NULL, 'NOT NULL') FIELD_NULL,
CH.RDB$CHARACTER_SET_NAME FIELD_CHARSET,
DCO.RDB$COLLATION_NAME FIELD_COLLATION,
COALESCE(RF.RDB$DEFAULT_SOURCE, F.RDB$DEFAULT_SOURCE) FIELD_DEFAULT,
F.RDB$VALIDATION_SOURCE FIELD_CHECK,
RF.RDB$DESCRIPTION FIELD_DESCRIPTION
FROM RDB$RELATION_FIELDS RF
JOIN RDB$FIELDS F ON (F.RDB$FIELD_NAME = RF.RDB$FIELD_SOURCE)
LEFT OUTER JOIN RDB$CHARACTER_SETS CH ON (CH.RDB$CHARACTER_SET_ID = F.RDB$CHARACTER_SET_ID)
LEFT OUTER JOIN RDB$COLLATIONS DCO ON ((DCO.RDB$COLLATION_ID = F.RDB$COLLATION_ID) AND (DCO.RDB$CHARACTER_SET_ID = F.RDB$CHARACTER_SET_ID))
WHERE (COALESCE(RF.RDB$SYSTEM_FLAG, 0) = 0)
ORDER BY RF.RDB$FIELD_POSITION
;
【讨论】:
以上是关于如何使用 dbExpress 从 Firebird 获取表描述(字段和类型)的主要内容,如果未能解决你的问题,请参考以下文章