使用DBIx :: Class预取相关行或不行,可能使用OUTER LEFT JOIN?

时间:2014-07-30 16:59:40

标签: sql perl join dbix-class

我想从带有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',
});

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

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

我无法编写特定关系,因为时间表A或B的ID仅在运行时给出。

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

1 个答案:

答案 0 :(得分:3)

这看起来像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",
});