【问题标题】:Check if row already exists, if so tell the referenced table the id检查行是否已经存在,如果存在则告诉引用的表 id
【发布时间】:2010-03-12 11:32:51
【问题描述】:

假设我有一本桌上杂志:

CREATE TABLE magazine
(
  magazine_id integer NOT NULL DEFAULT nextval(('public.magazine_magazine_id_seq'::text)::regclass),
  longname character varying(1000),
  shortname character varying(200),
  issn character varying(9),
  CONSTRAINT pk_magazine PRIMARY KEY (magazine_id)
);

还有一个表问题:

CREATE TABLE issue
(
  issue_id integer NOT NULL DEFAULT nextval(('public.issue_issue_id_seq'::text)::regclass),
  number integer,
  year integer,
  volume integer,
  fk_magazine_id integer,
  CONSTRAINT pk_issue PRIMARY KEY (issue_id),
  CONSTRAINT fk_magazine_id FOREIGN KEY (fk_magazine_id)
      REFERENCES magazine (magazine_id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
);

当前插入:

INSERT INTO magazine (longname,shotname,issn)
VALUES ('a long name','ee','1111-2222');

INSERT INTO issue (fk_magazine_id,number,year,volume)
VALUES (currval('magazine_magazine_id_seq'),'8','1982','6');

现在只有在“杂志”不存在的情况下才可以插入一行。但是,如果存在,则表 'issue' 需要获取已存在的行的 'magazine_id' 才能建立引用。

我该怎么做?

提前谢谢!

【问题讨论】:

    标签: sql postgresql insert reference exists


    【解决方案1】:

    您如何知道杂志是否已经在magazine 表中? issn 列是否定义杂志?如果是,那么它应该是一个主键,或者至少是unique

    最简单的方法是在您的客户端应用程序中检查杂志是否存在,如下所示(在伪代码中):

    function insert_issue(longname, shotname, issn, number,year,volume) {
        /* extensive comments for newbies */
        start_transaction();
        q_get_magazine_id = prepare_query(
          'select magazine_id from magazine where issn=?'
        );
        magazine_id = execute_query(q_get_magazine_id, issn);
        /* if magazine_id is null now then there’s no magazine with this issn */
        /* and we have to add it */
        if ( magazine_id == NULL ) {
          q_insert_magazine = prepare_query(
            'insert into magazine (longname, shotname, issn)
              values (?,?,?) returning magazine_id'
          );
          magazine_id = execute_query(q_insert_magazine, longname, shortname, issn);
          /* we have tried to add a new magazine; */
          /* if we failed (magazine_id==NULL) then somebody else just added it */
          if ( magazine_id == NULL ) { 
            /* other, parerelly connected client just inserted this magazine, */
            /* this is unlikely but possible */
            rollback();
            start_transaction();
            magazine_id = execute_query(q_get_magazine_id, issn);
          }
        }
        /* now magazine_id is an id of magazine, */
        /* added if it was not in a database before, new otherwise */
        q_insert_issue = prepare_query(
          'insert into issue (fk_magazine_id,number,year,volume)
             values (?,?,?,?)'
        );
        execute_query(q_insert_issue, magazine_id, number, year, volume);
        /* we have inserted a new issue referencing old, */
        /* or if it was needed new, magazine */
        if ( ! commit() ) {
          rollback();
          raise "Unable to insert an issue";
        }
    }
    

    如果您只需要在一个查询中执行此操作,那么您可以将此伪代码实现为数据库中的 pl/pgsql 函数,只需 select insert_issue(?, ?, ?, ?, ?, ?)

    【讨论】:

    • 感谢您的回答。我怎么知道杂志是否已经在杂志表中?我不知道该怎么做,但我想你必须检查你要插入的行是否已经存在,也许用 WHERE EXISTS 查询。 magazine_id 应该定义一本杂志,至少我是这样计划的。
    • 我很困惑。请回答一些问题: 1. 可以有两个具有相同 ISSN 的杂志吗? 2. 可以有两个同名的杂志吗? 3. 可以有两本短名相同的杂志吗?
    • 很抱歉让您感到困惑。 1. 不,杂志有唯一的 ISSN。 2.没有杂志是独一无二的。 3. 不。问题是它可能会发生,我插入一个带有杂志 xy 和杂志 ID = 1 的数据集。然后稍后可能会发生绝对相同的杂志 xy 将作为数据集 400 插入到表中。因此杂志 xy 也会有杂志 ID = 400,这是不好的。杂志 xy 应该坚持 magazine_id=1
    • 然后只需将 UNIQUE 约束添加到 longname、shortname 和 issn 列。数据库服务器不允许创建 2 个具有相同名称、issn 或短名称的杂志。还可以考虑通过问自己一个问题来在那里添加 NOT NULL 约束 - 每本杂志是否都必须有名称、ISSN 和短名称,并且它总是已知的。
    • 是的,但是我遇到的问题是,magazine_id=400 的数据集根本不会被引用。理解?问题表需要数据集 400 的 magazine_id=1。UNIQUE 约束只会导致它没有进入数据库。
    【解决方案2】:

    如果您使用的是 PostgreSQL 9.1 或更高版本,您可以执行以下操作:

    WITH ref_key (id) AS (
        WITH ins (id) AS (
           INSERT INTO magazine (longname,shotname,issn)
           VALUES ('a long name','ee','1111-2222')
           RETURNING id
        )
        SELECT id 
          FROM magazine 
          LEFT JOIN ins USING id
         WHERE issn = '1111-2222'
    )
    INSERT INTO INTO issue (fk_magazine_id,number,year,volume)
    SELECT id,'8','1982','6'
      FROM ref_key;
    

    可写 CTE 的 FTW!

    【讨论】:

      【解决方案3】:

      我不确定您是否可以使用 SQL 执行此操作。我知道 Oracle 可以用于触发器,但我认为 SQL 不能。如果我错了,请有人纠正我。

      【讨论】:

      • 对于这个“答案”,我什至不知道该说什么。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-12-04
      • 1970-01-01
      • 2019-11-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多