1

所以伙计们,我目前正在尝试回答一个大问题:“如何自己在 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表中)。我想要做的是在父表中插入一行,然后将该行传播到正确的CellPhoneNumberHomePhoneNumber子表中。(请注意,在我的情况下,子表的行数(每个 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 实例的 I 时,Postgres 抱怨它不能从 int 转换为 enum,指向值“39”(意大利国际前缀,顺便说一句)。如果我将前缀列的类型更改为,例如,文本 Postgres 最终将不再抱怨,但值仅作为触发器的效果正确存储到“父”电话号码表中,而不是“派生”的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:我知道我必须编写更多的函数和触发器来保证整个层次结构的引用完整性;以上只是一个示例,以便了解采取哪条路线。提前谢谢大家!

4

1 回答 1

2

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

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

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

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

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

于 2012-11-26T13:32:42.513 回答