根据外键计数条目

时间:2016-12-13 10:41:41

标签: firebird firebird-3.0

我有2个表,让我们称之为T_FATHERT_CHILD,其中每个父亲可以有多个孩子,如下所示:

T_FATHER
--------------------------
ID - BIGINT, from Generator

T_CHILD
-------------------------------
ID - BIGINT, from Generator
FATHER_ID - BIGINT, Foreign Key

现在我想在T_CHILD表中添加一个计数器,以1开头,每个新孩子加1,但不是全局,而是每个父亲,如:

ID | FATHER_ID | COUNTER |
--------------------------
1  | 1          | 1      |
--------------------------
2  | 1          | 2      |
--------------------------
3  | 2          | 1      |
--------------------------

我最初的想法是创建一个before-insert-trigger,它计算给定父亲有多少个孩子,并为计数器加1。这应该可以正常工作,除非同时有2个插入,这将以相同的计数器结束。实际上从未发生过这种情况的可能性很高 - 但是比对不起更好。

我不知道是否可以使用发电机,但不要这么想,因为每个父亲必须有一台发电机。

我当前的方法是使用上述触发器并为FATHER_ID + COUNTER添加唯一索引,以便只有一个同时插入通过。我将不得不处理客户端异常(并重新尝试失败的插入)。

有没有更好的方法直接在Firebird中处理这个?

PS:两张桌子中的任何一张都没有删除,所以这不是问题。

3 个答案:

答案 0 :(得分:2)

即使每个FATHER_ID使用一个生成器,您也无法使用它们,因为生成器不是事务安全的。如果您的交易因任何原因而回滚,那么无论如何都会增加发电机,从而造成间隙。

如果没有删除,我认为您使用唯一约束的方法是有效的。不过我会考虑另一种选择。

您可以决定不存储计数器 - 将计数器存储在数据库中通常是个坏主意。相反,只跟踪广告订单。为此,生成器 可用,因为每个新记录都有更高的值,间隙不会重要。事实上,除了你已经拥有的ID之外,你不需要任何东西。选择时确定编号;对于每个孩子,你想知道有多少孩子有同一个父亲,但ID较低。作为奖励,删除将正常工作。

以下是使用嵌套查询的概念证明:

SELECT ID, FATHER_ID,
       (SELECT 1 + COUNT(*)
        FROM T_CHILD AS OTHERS
        WHERE OTHERS.FATHER_ID = C.FATHER_ID
          AND OTHERS.ID < C.ID) AS COUNTER
FROM T_CHILD AS C

还有窗口功能的选项。它必须有一个派生表来计算最终未被选中的任何行:

SELECT * FROM (
  SELECT ID, FATHER_ID, 
         ROW_NUMBER() OVER(PARTITION BY FATHER_ID ORDER BY ID) AS COUNTER
  FROM T_CHILD
  -- Filtering that wouldn't affect COUNTER (e.g. WHERE FATHER_ID ... AND ID < ...)
)
-- Filtering that would affect COUNTER (e.g. WHERE ID > ...)

这两个选项具有完全不同的性能特征。哪一个(如果有的话)适合您,取决于您的数据大小和访问模式。

答案 1 :(得分:1)

当你尝试使用计算字段和Thijs van Dien的Select解决方案时?

CREATE TABLE T_CHILD(
  ID INTEGER,
  FATHER_ID INTEGER,
  COUNTER COMPUTED BY (
    (SELECT 1 + COUNT(*)
        FROM T_CHILD AS OTHERS
        WHERE OTHERS.FATHER_ID = T_CHILD.FATHER_ID
          AND OTHERS.ID < T_CHILD.ID)
  )
);

答案 2 :(得分:0)

在插入过程中,你应该做一个&#34;选择...计数+ 1&#34;直接在那个领域内。

但我可能会重新考虑首先添加该字段。感觉就像您需要时可以轻松推断的冗余信息。(例如,使用DENSE_RANK http://www.firebirdfaq.org/faq343/