【问题标题】:Table A references table B, how do I insert (using Perl and DBI and PostgreSQL)表 A 引用表 B,如何插入(使用 Perl 和 DBI 和 PostgreSQL)
【发布时间】:2023-04-09 18:58:01
【问题描述】:

考虑表 A 引用表 B 的情况:

create table tableA
(
   id INT NOT NULL PRIMARY KEY,
   information VARCHAR(32)
);
create table tableB
(
   id INT NOT NULL PRIMARY KEY,
   tableAid INT NOT NULL REFERENCES tableA(id),
   other_information VARCHAR(32)
):

请注意,我是用 Perl 编写代码,数据库是 PostgreSQL。

所以,我有 2 个 tableB 条目与单个 tableA 条目相关联。我想说的是:

use DBI;
my $dbh = DBI->connect("DBI:Pg:dbname=mydb;host=localhost","userid","password",('RaiseError' -> 1));

my $otherinfo = "other info, Table B";
my $moreotherinfo = "more other info, table B";
my info = "table A info";
my $insertitA = $dbh->prepare("insert into tableA (information) values (?)");
my $insertitB = $dbh->prepare("insert into tableB (tableAid,other_information) values (?,?)");
my $nrowsA = $insertitA($info);
my $tableAidreference = ????;
my $nrowsB = $insertitB($tableAidreference, $otherinfo);
my $nrowsB2 = $insertitB($tableAidreference, $moreotherinfo);

我在哪里可以得到$tableAidreference?我必须搜索tableA 吗?

【问题讨论】:

    标签: sql postgresql insert foreign-keys dbi


    【解决方案1】:

    为了后续INSERT 的目的而单独调用nextval('seq_name') 的方法已过时且效率非常低。它需要到服务器的额外往返。不要使用这个。有更好的选择:

    -> sqlfiddle

    首先,我修改了您的测试设置:

    CREATE TABLE tbl_a
    (  tbl_a_id serial NOT NULL PRIMARY KEY,
       information text
    );
    CREATE TABLE tbl_b
    (  tbl_b_id serial NOT NULL PRIMARY KEY,
       tbl_a_id int NOT NULL REFERENCES tbl_a(tbl_a_id),
       other_information text
    );
    
    • 不要使用id 作为列名,它不是描述性的。一些不太聪明的 ORM 会这样做,这是一种反模式。

    • 如果可以避免的话,不要在 PostgreSQL 中使用混合大小写的标识符。

    • 使用serial 类型作为代理主键。

    然后,使用INSERT 命令的RETURNING 子句在一个data-modifying CTE(需要PostgreSQL 9.1 或更高版本)中完成所有操作:

    WITH x AS (
        INSERT INTO tbl_a (information)
        VALUES ('foo')
        RETURNING tbl_a_id
        )
    INSERT INTO tbl_b (tbl_a_id, other_information)
    SELECT tbl_a_id, 'bar'
    FROM   x
    RETURNING tbl_a_id, tbl_b_id; -- optional, if you want IDs back
    

    一次往返服务器,如果需要,您可以取回两个新 ID - 而不是 三次 往返。


    如何将它与 DBI 一起使用?

    使用 DBD::Pg 模块:

    $SQL = q{ WITH x AS (
        INSERT INTO tbl_a (information)
        VALUES (?) 
        RETURNING tbl_a_id
        )
    INSERT INTO tbl_b (tbl_a_id, other_information)
    SELECT tbl_a_id, 'bar'
    FROM   x
    RETURNING tbl_a_id, tbl_b_id};
    $answer = $dbh->prepare($SQL);
    $sth->execute('foo');
    $tbl_a_id = $answer->fetch()->[0];
    $tbl_b_id = $answer->fetch()->[1];
    

    未经测试。有一个完整的example how to do it in the DBD::Pg manual

    【讨论】:

    • 所以,如果我说$doit = $dbh->prepare ("INSERT INTO tbl_a (information) VALUES (?) RETURNING tbl_a_id"); $ans = $doit->execute('other'); print "$ans";,我会得到“1”,表示受影响的行数。如何取回 tbl_a_id?
    • @rustycar:我添加了一个示例和我的答案的链接。
    • 为什么你认为nextval 已经过时了?它仍然是在不进行实际插入的情况下获取序列值的唯一方法。
    • @a_horse_with_no_name:我并不是说nextval() 本身已经过时。我是说分别在id 中查询INSERT 的工作流程已经过时。这是在 Postgres 8.2 引入 RETURNING 子句之前的方法。到服务器的一次往返胜过两次(在这种情况下为三次)。我澄清了我的措辞。
    【解决方案2】:

    这取决于您如何处理主键。通常,您最好的选择是通过从序列中选择值来自己处理增量。如果我们假设tableA.id 随序列递增,那么您可以通过执行如下 SQL 语句从数据库中执行额外的选择:

    SELECT NEXTVAL('seq_name'); <-- where seq_name is the name of the sequence you create
    

    您将选择该值,然后使用从数据库中检索到的值在下面修改后的插入中设置id 参数:

    my $insertitA = $dbh->prepare("insert into tableA (id, information) values (?, ?)");
    

    由于您已明确选择该值,因此您也可以将其插入到引用表中。

    【讨论】:

    • 这些方法不是最新的。
    猜你喜欢
    • 2012-12-22
    • 1970-01-01
    • 1970-01-01
    • 2013-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-25
    • 1970-01-01
    相关资源
    最近更新 更多