【问题标题】:How to implement (or simulate) subtype polymorphism in PostgreSQL 9.2?如何在 PostgreSQL 9.2 中实现(或模拟)子类型多态性?
【发布时间】:2012-11-26 11:59:16
【问题描述】:

伙计们,我目前正在尝试回答一个大问题:“如何自己在 PostgreSQL 中实现(或至少模拟)子类型多态性”,因为我意识到 PostgreSQL 对继承的支持在某种程度上是未定义的。到目前为止,尽可能简单明了,这是我的方法:

CREATE TABLE TelephoneNumber (
   ID serial PRIMARY KEY,
   countryCode CountryCodes_e NOT NULL,
   operatorPrefix OperatorPrefixes_e,
   townPrefix TownPrefixes_e,
   coreNumber text NOT NULL
);

CREATE TABLE CellPhoneNumber (
   cellPhoneNumber_ID serial PRIMARY KEY,
   countryCode CountryCodes_e,
   operatorPrefix OperatorPrefixes_e,
   coreNumber text NOT NULL
);

CREATE TABLE HomePhoneNumber (
   homePhoneNumber_ID serial PRIMARY KEY,
   countryCode CountryCodes_e,
   townPrefix TownPrefixes_e,
   coreNumber text NOT NULL
);

.
.
.

TABLE Operation (
   ...
   dateAndTime timestamp,
   receiverPhoneNumber int8 REFERENCES TelephoneNumber
   ...
);

是的,我知道这个解决方案可能看起来很糟糕,因为我将相同的信息复制到不同的表中,但这就是重点:我正在整个表层次结构中实现一致性,模拟子表的属性继承(因为 PostgreSQL 赢得了'不要让我将父表 TelephoneNumber 引用到外部 Operation 表中)。我想要做的是在父表中插入一行,然后将该行传播到正确的 CellPhoneNumber 或 HomePhoneNumber 子表中。 (请注意,在我的例子中,子表的行数(每行 1 行)少于其父表)。

接下来,我写了一个触发函数:

CREATE OR REPLACE FUNCTION insertTelephoneNumberIntoProperTable_FNC() RETURNS TRIGGER AS $$
    BEGIN
        IF (NEW.operatorPrefix = 'N/A' AND NEW.townPrefix <> 'N/A') THEN
            EXECUTE 'INSERT INTO HomePhoneNumber VALUES(' || NEW.telephoneNumber_ID || ', ' || NEW.countryCode::CountryCodes_e || ', ' || NEW.townPrefix || ', ' || NEW.coreNumber|| ');' USING NEW;
            RETURN NEW;
        ELSE
            IF (NEW.operatorPrefix <> 'N/A' AND NEW.townPrefix = 'N/A') THEN
                EXECUTE 'INSERT INTO CellPhoneNumber VALUES(' || NEW.telephoneNumber_ID || ', ' || NEW.countryCode::CountryCodes_e || ', ' || NEW.operatorPrefix || ', ' || NEW.coreNumber|| ');' USING NEW;
                RETURN NEW;
            END IF;
        END IF;

        RAISE EXCEPTION 'ERROR: Row is not valid!';
    END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER insertTelephoneNumberIntoProperTable_TRG 
BEFORE INSERT ON TelephoneNumber
FOR EACH ROW
EXECUTE PROCEDURE insertTelephoneNumberIntoProperTable_FNC();

这将拦截 INSERTs INTO TelephoneNumber 表并确定实际存储的是哪种电话号码:如果 operatorPrefix 等于“N/A”(即存储在 'OperatorPrefixes_e' 和 'TownPrefixes_e' 枚举中的特殊值)和 townPrefix 不是,那么我确定它是一个 HomePhoneNumber 实例。这同样适用于 CellPhoneNumber 实例。

问题是,当我INSERT INTO TelephoneNumber VALUES(DEFAULT, '39', 'N/A', '010', '7708124');,我们都知道它是一个 HomePhoneNumber 实例时,Postgres 抱怨它不能从 int 转换为 enum,指向值 '39'(意大利国际前缀,顺便说一句)。如果我将前缀列的类型更改为,例如,文本 Postgres 最终将不再抱怨,但值仅作为触发器的效果正确存储到“父”TelephoneNumber 表中,但不在“派生” HomePhoneNumber 表,这表明 EXECUTE 语句根本没有效果。所以,我真正的问题是:

  1. 为什么不能在 EXECUTE 语句中将这些值转换为枚举值? (我担心我做错了)
  2. 为什么,即使将前缀列的类型更改为文本,传播到子表也没有效果?

我在尝试将 int 值转换为枚举值时得到的确切错误是:

ERROR:  column "countrycode" is of type countrycodes_e but expression is of type integer
LINE 1: INSERT INTO HomePhoneNumber VALUES(1, 39, 010, 7708124);
                                              ^
HINT:  You will need to rewrite or cast the expression.
QUERY:  INSERT INTO HomePhoneNumber VALUES(1, 39, 010, 7708124);
CONTEXT:  PL/pgSQL function inserttelephonenumberintopropertable_fnc() line 4 at EXECUTE statement

如您所见,我尝试在 EXECUTE 语句中转换枚举值,但没有成功。希望你能帮助我,因为它是 35 小时。我醒着与 pgAdmin III 战斗... D:

PS:我知道我必须编写更多的函数和触发器来保证整个层次结构的引用完整性;以上只是一个示例,以便了解采取哪条路线。提前谢谢大家!

【问题讨论】:

  • 我认为您在示例 create table 语句中缺少 inherits 子句。而且我不会将国家/地区前缀存储为枚举,它们应该放入自己的表中
  • 根据 INHERITS 子句,IMVHO 对我的情况没有多大帮助,不是吗?我的意思是,这样做的唯一显着效果是将子行存储到父表中,这正是我试图在函数中模拟的行为。我使用函数而不是 INHERITS 子句的原因是因为这样我可以通过不同的属性来构建子表,这对我来说是一个优势。
  • 关于枚举,是的,这可能是一个解决方案,但我仍然无法解释为什么将相关属性存储为简单文本而不是枚举值会阻止传播正常工作,因为我在问题 2 中提出。:/
  • 通过表继承,您可以将差异放入子表中,并将公共属性放入父表中。尽管在您的情况下,唯一的共同属性是 countryCode - 那时可能不值得。
  • 当我说“差异”时,我的意思是“设置减法”。 :)

标签: postgresql inheritance casting enums int


【解决方案1】:

为什么不能在 EXECUTE 语句中将这些值转换为枚举值?

枚举值始终是字符文字(需要用单引号括起来)。

所以你需要做这样的事情:

EXECUTE 'INSERT INTO HomePhoneNumber VALUES(' 
         || NEW.telephoneNumber_ID 
         || ', ''' || NEW.countryCode || ''', ' 
         || NEW.townPrefix || ', ' || NEW.coreNumber|| ');' USING NEW;

注意表达式中添加的单引号,以使“国家代码”成为字符文字。

【讨论】:

  • 太棒了!这个已解决的问题 #1,现在 TelephoneNumber 表已填充,但 HomePhoneNumber 和 CellPhoneNumber 尚未填充,因此问题 #2 仍处于打开状态。我认为这是因为不可能通过触发器修改任何不是“引用表”的表:这就是我没有将函数中的 INSERT 仅仅写为“INSERT INTO ..”的原因,认为EXECUTE 可以作为一种解决方法,但我错了。
猜你喜欢
  • 1970-01-01
  • 2012-09-06
  • 1970-01-01
  • 1970-01-01
  • 2017-07-07
  • 2015-11-26
  • 2013-02-19
  • 1970-01-01
  • 2018-07-18
相关资源
最近更新 更多