sql >> データベース >  >> RDS >> Mysql

CakePHPで[イベント]->[スケジュール]->[開始日と終了日の間の日付]ですべてのイベントを選択します

    このような状況では、Cakeのアソシエーション、つまりContainableを使用せず、自分で結合を作成する傾向があります。

    $events = $this->Event->find('all', array(
        'joins'=>array(
            array(
                'table' => $this->Schedule->table, 
                'alias' => 'Schedule', 
                'type' => 'INNER', 
                'foreignKey' => false,
                'conditions'=> array(
                    'Schedule.event_id = Event.id',
                ),
            ),
            array(
                'table' => $this->Date->table, 
                'alias' => 'Date', 
                'type' => 'INNER', 
                'foreignKey' => false,
                'conditions'=> array(
                    'Date.schedule_id = Schedule.id',
                ),
            ),
        ),
        'conditions'=>array(
            'Date.start >=' => $start_date,
            'Date.start <=' => $end_date,
        ),
        'order'=>'Event.created DESC',
        'limit'=>5
    ));
    

    少し分厚いですが、私が望む正確なクエリになります。

    更新

    コードを部分的に分割して、どこで改善できるか見てみましょう。最初の部分は、findの準備です。 。コードを短くしようと書き直しました。これが私が思いついたものです:

    // Default options go here
    $defaultOpts = array(
        'start' => date('Y-m-d') . ' 00:00:00',
        'end' => date('Y-m-d') . ' 23:59:59',
        'limit' => 10
    )
    
    // Use default options if nothing is passed, otherwise merge passed options with defaults
    $opts = is_array($opts) ? array_merge($defaultOpts, $opts) : $defaultOpts;
    
    // Initialize array to hold query conditions
    $conditions = array();
    
    //date conditions
    $conditions[] = array(
        "Date.start >=" => $qOpts['start'],
        "Date.start <=" => $qOpts['end'],
    ));
    
    //cities conditions
    if(isset($opts['cities']) && is_array($opts['cities'])) {
        $conditions['OR'] = array();
        $conditions['OR'][] = array('Venue.city_id'=>$opts['cities']);
        $conditions['OR'][] = array('Restaurant.city_id'=>$opts['cities']);
    }
    
    //event types conditions
    //$opts['event_types'] = array('1');
    if(isset($opts['event_types']) && is_array($opts['event_types'])) {
        $conditions[] = 'EventTypesEvents.event_type_id' => $opts['event_types']
    }
    
    //event sub types conditions
    if(isset($opts['event_sub_types']) && is_array($opts['event_sub_types'])) {
        $conditions[] = 'EventSubTypesEvents.event_sub_type_id' => $opts['event_sub_types']
    }
    
    //event sub sub types conditions
    if(isset($opts['event_sub_types']) && is_array($opts['event_sub_sub_types'])) {
        $conditions[] = 'EventSubSubTypesEvents.event_sub_sub_type_id' => $opts['event_sub_sub_types']
    }
    

    ほとんどのORを削除したことに注意してください。これは、conditionsの値として配列を渡すことができるためです。 、CakeはそれをIN(...)にします SQLクエリのステートメント。例:'Model.field' => array(1,2,3) 'Model.field IN (1,2,3)'を生成します 。これはORと同じように機能しますが、必要なコードは少なくなります。したがって、上記のコードブロックは、コードが実行していたのとまったく同じように動作しますが、より短くなります。

    次に、複雑な部分であるfindが登場します。

    通常は、Containableを使用せず、'recursive'=>falseを使用して、強制結合のみを使用することをお勧めします。 。これは通常だと思います 複雑な検索に対処するための最良の方法です。 Associations and Containableを使用すると、Cakeはデータベースに対して複数のSQLクエリ(モデル/テーブルごとに1つのクエリ)を実行しますが、これは非効率的である傾向があります。また、Containableは常に期待される結果を返すとは限りません(試してみたときに気づいたように)。

    しかし あなたの場合は4つあるので 複雑な関連付けが含まれている場合は、混合アプローチが理想的なソリューションになる可能性があります。そうでない場合は、複雑すぎて重複データをクリーンアップできません。 (4つの複雑な関連付けは次のとおりです。イベントhasMany Dates [イベントhasManyスケジュール、スケジュールhasMany Dateを介して]、イベントHABTM EventType、イベントHABTM EventSubType、イベントHABTM EventSubSubType)。したがって、CakeにEventType、EventSubType、およびEventSubSubTypeのデータ取得を処理させ、重複が多すぎないようにすることができます。

    だからここに私が提案するものがあります:必要なすべてのフィルタリングに結合を使用しますが、フィールドに日付と[Sub[Sub]]タイプを含めないでください。モデルが関連付けられているため、CakeはDBに対して追加のクエリを自動的に実行して、これらのデータをフェッチします。封じ込めは必要ありません。

    コード:

    // We already fetch the data from these 2 models through
    // joins + fields, so we can unbind them for the next find,
    // avoiding extra unnecessary queries. 
    $this->unbindModel(array('belongsTo'=>array('Restaurant', 'Venue'));
    
    $data = $this->find('all', array(
        // The other fields required will be added by Cake later
        'fields' => "
            Event.*, 
            Restaurant.id, Restaurant.name, Restaurant.slug, Restaurant.address, Restaurant.GPS_Lon, Restaurant.GPS_Lat, Restaurant.city_id,
            Venue.id, Venue.name, Venue.slug, Venue.address, Venue.GPS_Lon, Venue.GPS_Lat, Venue.city_id,
            City.id, City.name, City.url_name
        ",  
        'joins' => array(
            array(
                'table' => $this->Schedule->table,
                'alias' => 'Schedule',
                'type' => 'INNER',
                'foreignKey' => false,
                'conditions' => 'Schedule.event_id = Event.id',
            ),
            array(
                'table' => $this->Schedule->Date->table,
                'alias' => 'Date',
                'type' => 'INNER',
                'foreignKey' => false,
                'conditions' => 'Date.schedule_id = Schedule.id',
            ),
            array(
                'table' => $this->EventTypesEvent->table,
                'alias' => 'EventTypesEvents',
                'type' => 'INNER',
                'foreignKey' => false,
                'conditions' => 'EventTypesEvents.event_id = Event.id',
            ),
            array(
                'table' => $this->EventSubSubTypesEvent->table,
                'alias' => 'EventSubSubTypesEvents',
                'type' => 'INNER',
                'foreignKey' => false,
                'conditions' => 'EventSubSubTypesEvents.event_id = Event.id',
            ),
            array(
                'table' => $this->Restaurant->table,
                'alias' => 'Restaurant',
                'type' => 'LEFT',
                'foreignKey' => false,
                'conditions' => 'Event.restaurant_id = Restaurant.id',
            ),
            array(
                'table' => $this->City->table,
                'alias' => 'RestaurantCity',
                'type' => 'LEFT',
                'foreignKey' => false,
                'conditions' => 'Restaurant.city_id = city.id',
            ),
            array(
                'table' => $this->Venue->table,
                'alias' => 'Venue',
                'type' => 'LEFT',
                'foreignKey' => false,
                'conditions' => 'Event.venue_id = Venue.id',
            ),
            array(
                'table' => $this->City->table,
                'alias' => 'VenueCity',
                'type' => 'LEFT',
                'foreignKey' => false,
                'conditions' => 'Venue.city_id = city.id',
            ),
        ),
        'conditions' => $conditions,
        'limit' => $opts['limit'],
        'recursive' => 2
    ));
    

    containsを削除しました 、およびそのためにCakeが実行していた追加のクエリの一部。ほとんどの結合はタイプINNERです。 。これは、結合に関係する両方のテーブルに少なくとも1つのレコードが存在する必要があることを意味します。存在しないと、期待よりも少ない結果が得られます。各イベントはレストランで行われると思いますまたは 会場ですが、両方ではありません。そのため、LEFTを使用しました。 それらのテーブル(および都市)用。結合で使用されるフィールドの一部がオプションの場合は、LEFTを使用する必要があります INNERの代わりに 関連する結合について。

    'recursive'=>falseを使用した場合 ここでは、適切なイベントが取得され、データの繰り返しはありませんが、日付と[Sub[Sub]]タイプが欠落しています。 2レベルの再帰により、Cakeは返されたイベントを自動的にループし、イベントごとに、関連するモデルデータをフェッチするために必要なクエリを実行します。

    これはほとんどあなたがやっていたことですが、Containableがなく、いくつかの追加の調整があります。まだ長くて醜くて退屈なコードであることは知っていますが、結局のところ、13のデータベーステーブルが関係しています...

    これはすべてテストされていないコードですが、機能するはずです。



    1. MySQLALTERTABLEがハングする

    2. SQL Server-テーブルの行が更新されるとどうなりますか?

    3. SQL Serverデータベース変更リスナーC#

    4. findAndCountAllの親モデルに影響を与える条件をsequelizeのサブサブ子モデルに追加するにはどうすればよいですか?