【问题标题】:Prefetch related row or nothing with DBIx::Class, maybe with OUTER LEFT JOIN?使用 DBIx::Class 预取相关行或什么都没有,也许使用 OUTER LEFT JOIN?
【发布时间】:2014-07-30 16:59:40
【问题描述】:

我想从带有DBIx::Class 的表中检索行,并从同一个表中预取相应的行,其中列具有特定的其他值。我需要从计划 A 中获取所有分配(以复制它们)并从计划 B 中检索所有相应的分配。

我已经制作了如下所示的测试表:

CREATE TABLE tasks (
    id INTEGER
);

CREATE TABLE schedules (
    id INTEGER
);

CREATE TABLE assignments (
    id INTEGER,
    scheduleId INTEGER,
    taskId INTEGER,
    worker TEXT,
    FOREIGN KEY (scheduleId) REFERENCES schedules(id),
    FOREIGN KEY (taskId) REFERENCES tasks(id)
);

时间表 1 有一些作业,时间表 2 有一些作业:

INSERT INTO tasks (id) VALUES (1);
INSERT INTO tasks (id) VALUES (2);
INSERT INTO schedules (id) VALUES (1);
INSERT INTO schedules (id) VALUES (2);
INSERT INTO assignments (id,scheduleId,taskId,worker) VALUES (1,1,1,"Alice");
INSERT INTO assignments (id,scheduleId,taskId,worker) VALUES (2,1,2,"Bob");
INSERT INTO assignments (id,scheduleId,taskId,worker) VALUES (3,2,1,"Charly");

这是一些返回所需结果的 SQL:

SELECT * FROM assignments AS a1
LEFT OUTER JOIN assignments AS a2 ON
    a2.scheduleId = 2 AND
    a2.taskId = a1.taskId
WHERE a1.scheduleId = 1;

在 SQLite 中,这按预期工作:结果显示计划 1 中的每个分配和计划 2 中的相应分配都有一行。

id|scheduleId|taskId|worker|id|scheduleId|taskId|worker
1|1|1|Alice|3|2|1|Charly
1|1|2|Bob|NULL|NULL|NULL|NULL

到目前为止,我对 DBIx::Class 的尝试不起作用。这是作业类的样子:

package MyApp::Schema::Result::Assignment;

...

__PACKAGE__->has_many(
    inAllSchedules => 'MyApp::Schema::Result::Assignment',
    {
        'foreign.taskId' => 'self.taskId',
    }
);

以下代码正确连接了行,但仅返回计划 1 中的行,这些行实际上具有计划 2 中的相应行:

my $assignments = $schema->resultset('Assignment')->search({
    'inAllSchedules.scheduleId' => 2,
}, {
    prefetch => 'inAllSchedules',
});

此代码也正确地连接了行并返回没有连接行的行,但我不知道如何过滤连接的行。我不想检索附表 3 等的行,或者只是 any 其他行 ...

my $assignments = $schema->resultset('Assignment')->search(undef, {
    join_type => 'left outer',
    prefetch => 'inAllSchedules',
});

我不能写具体的关系,因为调度A或B的ID当然只在运行时给出。

如何生成给定的 SQL 代码或以干净的方式检索数据?

【问题讨论】:

    标签: sql perl join dbix-class


    【解决方案1】:

    这看起来像是custom join conditions 的案例。以下解决方案适用于您的有限示例,但可能需要针对您的实际应用进行调整。

    __PACKAGE__->has_many(
        'inAllSchedules' => "MyApp::Schema::Result::Assignment",
        sub {
            my $args = shift;
    
            return {
                "$args->{foreign_alias}.taskId" => { '-ident' => "$args->{self_alias}.taskId" },
                "$args->{foreign_alias}.id" => { '<>' => { '-ident' => "$args->{self_alias}.id" } },
            };
        }
    );
    

    你会这样使用它:

    my $assignments = $schema->resultset("Assignment")->search({
        'me.scheduleId'             => 1,
        'inAllSchedules.scheduleId' => [ 2, undef ],
    },
    {
       'prefetch' => "inAllSchedules",
    });
    

    【讨论】:

    • 您的答案看起来简单而有希望,但这不起作用。将 ID 与NULL 进行比较似乎意味着存在一行。创建的 SQL 读起来像 SELECT me.*, inAllSchedules.* FROM assignments me LEFT JOIN assignments inAllSchedules ON inAllSchedules.taskId = me.taskId WHERE ( ( ( inAllSchedules.scheduleId IS NULL OR inAllSchedules.scheduleId = 2 ) AND me.scheduleId = 1 ) )。添加 join_type =&gt; "left outer" 并没有什么不同。
    • 好的,我已将您的答案标记为已接受,因为它可以正常工作。我还了解了-ident。两个问题: 1. 从 coderef 返回 ON 子句时,简单语法 self.column_nameforeign.column_name 是否无效?为什么 DBIx::Class 将查询参数放在 ON 子句中?实际上条件是像以前一样定义的。我看不出到底有什么不同,我想了解一下。你能解释一下吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-18
    • 1970-01-01
    • 2018-07-03
    • 2011-08-20
    • 2011-12-22
    • 2018-02-28
    • 2012-08-07
    相关资源
    最近更新 更多