【问题标题】:Create a field in Firebird which displays data from another table在 Firebird 中创建一个显示来自另一个表的数据的字段
【发布时间】:2023-03-31 23:13:02
【问题描述】:

我没有找到在 Firebird 数据库中创建“查找列”的有效解决方案。

这是一个例子:

表 1:订单 [订单 ID] [客户 ID] [客户名称]

表 2:客户 [ID] [姓名]

当我运行SELECT * FROM ORDERS 时,我想获取 OrderID、CustomerID 和 CustomerName....但是 CustomerName 应该通过在“Customer”表的“ID”列中查找“CustomerID”来自动计算,返回内容“名称”列。

【问题讨论】:

  • 提示:INNER JOIN.
  • 有没有办法通过创建表来做到这一点,这样以后就不需要在每个sql命令中加入表了?对不起这个愚蠢的问题,但我是一个绝对的 sql 初学者
  • 您将创建一个视图,该视图可用作选择语句的表。
  • 谢谢,这解决了我的问题

标签: sql firebird


【解决方案1】:

Firebird 有 calculated fields (generated always as/computed by),它们允许从其他表中进行选择(与此答案的早期版本相反,它声明 Firebird 不支持此功能)。

但是,我建议您改用视图,因为我认为它的性能更好(尚未对此进行验证,因此如果性能很重要,我建议您测试一下)。

使用视图

常见的方法是定义一个基表和一个随附的view,在查询时收集必要的数据。人们会从视图中查询,而不是使用基表。

create view order_with_customer
as
select orders.id, orders.customer_id, customer.name
from orders
inner join customer on customer.id = orders.customer_id;

或者您可以跳过视图并在您自己的查询中使用上述连接。

替代方案:计算字段

我将其标记为替代方案而非主要解决方案,因为我认为使用视图将是更可取的解决方案。

要使用计算字段,您可以使用以下语法(注意查询周围的双括号):

create table orders (
    id integer generated by default as identity primary key,
    customer_id integer not null references customer(id),
    customer_name generated always as ((select name from customer where id = customer_id))
)

customer 表的更新将自动反映在orders 表中。

据我所知,此选项的性能低于使用连接时(如视图示例中所用),但您可能想自己测试一下。

带功能的FB3+

使用 Firebird 3,您还可以使用触发器创建计算字段,这使得表达式本身更短。

为此,请创建一个从客户表中选择的function

create function lookup_customer_name(customer_id integer) 
    returns varchar(50)
as
begin
    return (select name from customer where id = :customer_id);
end

然后创建表为:

create table orders (
    id integer generated by default as identity primary key,
    customer_id integer not null references customer(id),
    customer_name generated always as (lookup_customer_name(customer_id))
);

customer 表的更新将自动反映在orders 表中。选择大量记录时,此解决方案可以相对较慢,因为该功能将单独为每行执行,这比执行连接效率较低。

替代方案:使用触发器

但是,如果您想在插入(或更新)时使用来自另一个表的信息来更新该表,您可以使用 trigger

我将使用 Firebird 3 作为我的答案,但它也应该翻译成早期版本(有一些细微差别)。

所以假设一个表customer

create table customer (
    id integer generated by default as identity primary key,
    name varchar(50) not null
);

带有样本数据:

insert into customer(name) values ('name1');
insert into customer(name) values ('name2');

还有一张桌子orders

create table orders (
    id integer generated by default as identity primary key,
    customer_id integer not null references customer(id),
    customer_name varchar(50) not null
)

然后定义一个触发器:

create trigger orders_bi_bu 
    active before insert or update
    on orders
as
begin
    new.customer_name = (select name from customer where id = new.customer_id);
end

现在当我们使用:

insert into orders(customer_id) values (1);

结果是:

id  customer_id  customer_name
1   1            name1

更新:

update orders set customer_id = 2 where id = 1;

结果:

id  customer_id  customer_name
1   2            name2

触发器的缺点是更新customer 表中的名称不会自动反映在orders 表中。您需要自己跟踪这些依赖关系,并在 customer 上创建更新后触发器来更新依赖记录,这可能导致更新/锁定冲突。

【讨论】:

  • 非常感谢。这是非常有用的。根据您的建议,我将使用视图。
  • @Arioch'The Firebird 不允许在 2.5、3.0 和 4.0 中使用它;我没有往后看。虽然看了你的例子,但我没有尝试使用双括号。
  • 马克,这在 FB 2.1 中有效:-D / 当然,语法很古怪。您只需要记住,computed-by 列在列声明括号内采用expression。并且将单数 select stmt 也转换为 expression 通过将其放在括号内(考虑 update ... set a = (select...) - 这是相同的原理)。您只需要连接点并部署 BOTH paren。对 - 一个用于列 decl,一个用于 expression 转换 /// 当然,我不会推荐它用于实际使用。 VIEW 是正确的起始方向
  • @Arioch'The Just 检查,在 Firebird 3 双括号中也可以使用。将修改我的答案。
  • 也许最好突出显示连接和按选择计算方法之间的行为/结果集差异,而不是单一的查找结果,而是有很多行或没有。
【解决方案2】:

这里不需要复杂的查找字段。 无需在 Table1 上添加持久字段 [CustomerName]。

正如 Gordon 所说,一个简单的 Join 就足够了:

Select T1.OrderID,  T2.ID,  T2.Name
From Customers T2 
Join Orders T1 On T1.IDOrder = T2.ID

也就是说,如果你想在 SQL 中使用查找字段(就像我们在数据集上做的那样),你可以使用类似的东西:

Select T1.OrderID,  T2.ID,  
( Select T3.YourLookupField From T3 where (T3.ID = T2.ID)  )
From Customers T2 Join Orders T1 On T1.IDOrder = T2.ID

问候。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-06
    • 1970-01-01
    • 1970-01-01
    • 2013-05-08
    • 1970-01-01
    • 2023-03-23
    相关资源
    最近更新 更多