Таблица содержащая данные типа справочник (список позиций товара к примеру),
имеет связь с таблицей заказов. Связана по полю артикул. Артикул при этом не является primary-key для таблицы заказов, но является ключевым в таблице товаров.
Для выборки данных по JOIN из этих таблиц, делаем так (пример):
В описании таблицы Товаров:
__PACKAGE__->set_primary_key('артикул');
__PACKAGE__->belongs_to('имя_связи_произвольное', 'Класс::Имя_таблицы_заказов','артикул (поле из таблицы заказов)');
В описании таблицы заказов:
__PACKAGE__->set_primary_key('какой-то первичный ключ');
__PACKAGE__->has_many('имя_связи2', 'Класс::Имя_таблицы_товаров', { "foreign.артикул" => "self.артикул" });
В последнем анонимном хэше прописано что связь производится не по primary key (без указания слов foreign и self - связывать будет пытаться по primary).
Теперь работаем с запросами так:
$rs = $db->{connect}->resultset('Таблица_заказов')->search(
{ 'me.любое_поле' => 'критерий_для_поиска' },
{
join => ['имя_связи2'],
'+select' => ['имя_связи2.поле_из_таблицы_товаров'],
'+as' => ['алиас_для_выборки'], }
);
# извлекаем данные
while ( my $r = $rs->next ) {
item => $r->get_column('алиас_для_выборки');# выборка из связанной таблицы
notes => $r->поле_из_таблицы_заказов;# выборка из родительской таблицы
});
}


Еще советик :) Что бы не мучаться и руками не писать все эти классы, можно сделать следующее.
Для начала во всех таблицах правильно создавать foreign keys и использовать DBIx::Class::Schema::Loader, в результате получится замечательно сгенерированные классы. Для примера выше таблица продукции:
CREATE TABLE `products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
А в таблице заказов можно было указать внешний ключ:
CREATE TABLE `orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` int(11) NOT NULL,
`amount` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_orders__product_id` (`product_id`),
CONSTRAINT `fk_orders__product_id` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8
после чего использовать автоматизацию:
#!/usr/bin/perl -w
package OrdersDB;
use strict;
use warnings;
use base qw/DBIx::Class::Schema::Loader/;
__PACKAGE__->loader_options(
use_namespaces => 0
);
1;
package main;
use DBIx::Class::Schema::Loader;
OrdersDB->dump_to_dir('./');
OrdersDB->connection("dbi:mysql:database=test;hostname=localhost", $USER, $PASS);
И в результате после выполнения получим два премилых класса (сгенерированные коменты вырезаны):
package OrdersDB::Product;
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("products");
__PACKAGE__->add_columns(
"id",
{ data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
"name",
{ data_type => "varchar", is_nullable => 0, size => 255 },
);
__PACKAGE__->set_primary_key("id");
__PACKAGE__->has_many(
"orders",
"OrdersDB::Order",
{ "foreign.product_id" => "self.id" },
{ cascade_copy => 0, cascade_delete => 0 },
);
1;
И второй:
package OrdersDB::Order;
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("orders");
__PACKAGE__->add_columns(
"id",
{ data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
"product_id",
{ data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
"amount",
{ data_type => "integer", is_nullable => 0 },
);
__PACKAGE__->set_primary_key("id");
__PACKAGE__->belongs_to(
"product",
"OrdersDB::Product",
{ id => "product_id" },
{ on_delete => "CASCADE", on_update => "CASCADE" },
);
1;
NB. Хочу заметить, что Loader хорошо распознаёт уникальность полей для связывания и правильно использует might_have/has_many/belongs_to.
После чего можно точно так же:
use OrdersDB;
# ...
my @orders = $sch->resultset('Order')->search({
'amount' => { '>=', 2 }
}, {
prefetch => 'products'
});
foreach my $order (@orders)
{
printf "OrderID: %d, ProductName: %s, Amount: %d\n",
$order->id,
$order->product->name,
$order->amount;
}
Мало того, prefetch не обязателен. Даже при указывании join DBIx::Class позволяет использовать подчиненные таблицы по имени связи без использования get_columns. Например, можно спокойно использовать вложенные join-ы.
my @orders = $sch->resultset('Orders')->search({
'product_description.name' => { 'like', '%bana%' },
'owner.location' => { 'in', \@locaion_ids },
'product.some_field' => { '>=', 1 }
},
{
join => {
'products' => {
'product_descriptions' => 'owner'
}
}
};
#...
$order->product->description->owner->name;
$order->product->description->long_desc;
$order->product->code;
$order->amount;
Prefetch в этом плане поможет избежать лишних запросов при доступе к подчиненным таблицам. А параметры '+select' и '+as' используются чаще всего для полей-функций, например:
'+select' => [ { count => 'amount', -as => 'amount_of_products' } ],
'+as' => [qw/prod_amount/],
Вот здесь очень хорошо про это всё написано:
Спасибо за развернутый комментарий.
Но при работе с Loader'ом у меня возникла проблема, точнее он не работал у меня для MSSQL2005 :(... ПОэтому пришлось писать все ручками, благо проект не большой. Для большого - либо мучать дальше лоадер, либо дублировать в mysql базу и с нее считывать лоадером...
Loader хорош, но иногда стоит понимать что к чему в том что он генерит.
Дык Loader генерит всё, как в документации по DBIx::Class. Главное правильно создать базу, указать ключи, и следовать лингвистическим правилам наименований, описанных в документации к Loader-у, тогда проблем не будет. В новой версии они таки ввели namespace-ы, очень удобно при работе с несколькими базами. А так же поправили работу с ключами и наименованиями связей.
2Monks:
С MS SQL не работал, но судя по гуглу изменения поступают достаточно часто, возможно уже поддерживается. Стоит посмотреть вот сюда:
Возможно нужные возможности уже сделаны/пофикшены.
А если проблемы с Loader все еще не решены - докладывайте на #dbix-class@irc.perl.org. Трудно чинить когда все вроде бы работает :)
Хм. Надо попробовать обновиться.
Судя по:
Fix mssql common tests failures with MSSQL 2005 (skip test of datatypes found only on MSSQL 2008)
возможно новая версия будет работать.