2

DBIx::Class で境界検索を実行しようとしていますが、まだ成功していません。

生成したいSQLは次のようになります。

SELECT 
    zip,
    6371 * ACos( Cos(RADIANS(Lat)) * Cos(RADIANS(USERLAT)) * Cos(RADIANS(USERLNG) - RADIANS(Lng)) + Sin(RADIANS(Lat)) * Sin(RADIANS(USERLAT)) ) AS Distance
FROM 
    geopc
WHERE 
    6371 * ACos( Cos(RADIANS(Lat)) * Cos(RADIANS(USERLAT)) * Cos(RADIANS(USERLNG) - RADIANS(Lng)) + Sin(RADIANS(Lat)) * Sin(RADIANS(USERLAT)) ) <= DISTANCE
ORDER BY 
    Distance

USERLAT、USERLNG、および DISTANCE は変数である必要があり、Web リクエストを介して送信されます。

私のDBIx::クラスの結果:

use utf8;
package MyApp::Models::Schema::Result::Geopc;

use strict;
use warnings;

use base 'DBIx::Class::Core';

__PACKAGE__->table("geopc");

__PACKAGE__->add_columns(
  "id",
  { data_type => "bigint", is_nullable => 0, is_auto_increment => 1 },
  "country",
  { data_type => "varchar", is_nullable => 0, size => 2 },
  "language",
  { data_type => "varchar", is_nullable => 0, size => 2 },
  "iso2",
  { data_type => "varchar", is_nullable => 0, size => 6 },
  "region1",
  { data_type => "varchar", is_nullable => 0, size => 60 },
  "region2",
  { data_type => "varchar", is_nullable => 0, size => 60 },
  "region3",
  { data_type => "varchar", is_nullable => 0, size => 60 },
  "region4",
  { data_type => "varchar", is_nullable => 0, size => 60 },
  "zip",
  { data_type => "varchar", is_nullable => 0, size => 10 },
  "city",
  { data_type => "varchar", is_nullable => 0, size => 60 },
  "area1",
  { data_type => "varchar", is_nullable => 0, size => 80 },
  "area2",
  { data_type => "varchar", is_nullable => 0, size => 80 },
  "lat",
  { data_type => "double precision", is_nullable => 0 },
  "lng",
  { data_type => "double precision", is_nullable => 0 },
  "tz",
  { data_type => "varchar", is_nullable => 0, size => 30 },
  "utc",
  { data_type => "varchar", is_nullable => 0, size => 10 },
  "dst",
  { data_type => "varchar", is_nullable => 0, size => 1 },
);
__PACKAGE__->set_primary_key('id');

私はグーグルで調べましたが、これを処理する良い方法が見つかりませんでした。どんな助けでも大歓迎です。

私はMySQLを使用しています...

4

3 に答える 3

1

私は同じ問題を抱えていました: 私はcompanieswhichを持っているbelongs_to addressので、住所has_many companies- 近隣の会社を見つける必要があったので、Adressモデルを使用して:

__PACKAGE__->add_columns(
  "id",
  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
  "country",
  { data_type => "varchar", is_nullable => 0, size => 64 },
  "county",
  { data_type => "varchar", is_nullable => 1, size => 45 },
  "city",
  { data_type => "varchar", is_nullable => 0, size => 64 },
  "street",
  { data_type => "varchar", is_nullable => 0, size => 128 },
  "street_no",
  { data_type => "varchar", is_nullable => 1, size => 24 },
  "apartment_no",
  { data_type => "varchar", is_nullable => 1, size => 24 },
  "extra",
  { data_type => "varchar", is_nullable => 1, size => 128 },
  "lat",
  { data_type => "decimal", is_nullable => 1, size => [10, 7] },
  "long",
  { data_type => "decimal", is_nullable => 1, size => [10, 7] },
);

get_neighbour_companiesそのモデルにメソッドを実装しました:

sub get_neighbour_companies{
  my ( $self, $args ) = @_;

  my $distance = $args->{distance} // 15;

  my $geo_clause = sprintf('( 6371 * acos( cos( radians(%s) ) * cos( radians( me.lat ) ) * cos( radians( me.`long` ) - radians(%s) ) + sin( radians(%s) ) * sin( radians( me.lat ) ) ) ) AS distance', $self->lat, $self->long, $self->lat );

  my $rs = $self->result_source->schema->resultset('Address')
    ->search_rs(
      {
        'companies.company_type_id' => ( $args->{company_type_id} // 1 ), #defaults to 'orderer' type
      },
      {
        prefetch => { 'companies' => 'address' },
        select => [ 'id', \$geo_clause ],
        as     => [qw/ id distance /],
        having => { distance => { '<=' => $distance } },
        order_by => 'distance',
      }
    );

  my @companies;
  while ( my $address = $rs->next ){
    my @comps = $address->companies()->all;
    next unless @comps;

    foreach my $company ( @comps ) {
        push @companies, {
            company => $company,            
            distance => $address->get_column('distance'),
          };
    }    
  };
  return [ @companies ]; 
}

私はこのように使用しています:

my $customers = $comp->address->get_neighbour_companies({
          distance        => 12,
          company_type_id => 1,
        });

ここでは、から 12 km 以内$customersのリストへの配列参照になります。companies$compcompany

于 2013-03-13T07:39:52.367 に答える
1

このような複雑なクエリの 1 つの解決策は、ビューとして定義することです。これには、関係を定義すると、結合およびプリフェッチが可能になるという利点があります。

計算された「距離」列にを使用する別のもの。'columns' は 'select' と 'as' パラメーターの単なる組み合わせであり、ユーザー エラーの減少につながるより堅牢な API であることが証明されています。検索構文はSQL::Abstractから来ており、リテラル sqlを使用するいくつかの手段を提供することに注意してください。

サブクエリを使用しない最適なソリューションは次のとおりです。

my $param = \[
        '6371 * ACos( Cos(RADIANS(Lat)) * Cos(RADIANS(?)) * Cos(RADIANS(?)' .
        ' - RADIANS(Lng)) + Sin(RADIANS(Lat)) * Sin(RADIANS(?)) )'
        [ USERLAT  => $USERLAT ],
        [ USERLNG  => $USERLNG ],
        [ USERLAT  => $USERLAT ],
    ];

my $geopc = $schema->resultset('Result::Geopc')->search({
        $param => { '<=', $distance },
    }, {
        columns => [
           'zip',
           { distance => $param }
        ],
        order_by => $param,
    });
于 2013-03-12T23:28:58.600 に答える
0

次のようなサブクエリを使用するようにクエリを書き直すことができます。

SELECT zip, Distance
FROM (SELECT zip,
        6371 * ACos( Cos(RADIANS(Lat)) * Cos(RADIANS(USERLAT)) * Cos(RADIANS(USERLNG) - RADIANS(Lng)) + Sin(RADIANS(Lat)) * Sin(RADIANS(USERLAT)) )
        AS Distance
     FROM geopc) AS tmp
WHERE Distance <= DISTANCE
ORDER BY Distance

次に、次のようなものが機能するはずです。

my $geopc = $schema->resultset('Result::Geopc');

my $subquery = $geopc->search({}, {
    select => [
        'zip',
        \[
            '6371 * ACos( Cos(RADIANS(Lat)) * Cos(RADIANS(?)) * Cos(RADIANS(?)' .
            ' - RADIANS(Lng)) + Sin(RADIANS(Lat)) * Sin(RADIANS(?)) )' .
            ' AS Distance',
            [ USERLAT  => $USERLAT ],
            [ USERLNG  => $USERLNG ],
            [ USERLAT  => $USERLAT ],
        ],
    ],
})->as_query;

my $rs = $geopc->search({
    Distance => { '<=' => $DISTANCE },
}, {
    alias => 'geopc2',
    from => [
        { geopc2 => $subquery },
    ],
    select => [ qw(zip Distance) ],
    order_by => 'Distance',
});

このアプローチでは、プレースホルダーと文書化されていない ResultSet 属性を含むリテラル SQL をfrom使用します。from属性のいくつかの使用例は、 DBIx::Class テスト スイートで見つけることができます。この属性は文書化されていないため、将来のバージョンではサポートされない可能性があることに注意してください。

于 2013-03-13T12:37:49.703 に答える