sql >> Database >  >> RDS >> Mysql

Complexe databasequery's in yii2 met Active Record

Ik ga ervan uit, op basis van de vraag die je hier hebt gesteld die je leuk vond in opmerkingen die je hebt de hele zoekopdracht gegeven (geen andere velden, die je hebt verwijderd om voorbeeldcode te tonen)

daarom, als u alleen de velden nodig hebt die zijn gespecificeerd in SELECT statement, kunt u uw zoekopdracht behoorlijk optimaliseren:

ten eerste doe je mee met host_machines alleen om cameras te linken en events , maar hebben dezelfde sleutel host_machines_idhost_machines op beide, dus dat is niet nodig, je kunt direct:

    INNER JOIN events events
        ON (events.host_machines_idhost_machines =
            cameras.host_machines_idhost_machines))

ten tweede de join met ispy.staff , het enige gebruikte veld is idreceptionist in WHERE clausule, dat veld bestaat in events zodat we het volledig kunnen laten vallen

de laatste vraag hier:

SELECT videos.idvideo, videos.filelocation, events.event_type, events.event_timestamp
FROM videos videos
    INNER JOIN cameras cameras
        ON videos.cameras_idcameras = cameras.idcameras
    INNER JOIN events events
        ON events.host_machines_idhost_machines =
                cameras.host_machines_idhost_machines
WHERE     (events.staff_idreceptionist = 182)
        AND (events.event_type IN (23, 24))
        AND (events.event_timestamp BETWEEN videos.start_time
               AND videos.end_time)

zou dezelfde records moeten uitvoeren als die in uw vraag, zonder identieke rijen
sommige dubbele video's zullen nog steeds bestaan ​​vanwege een een-op-veel-relatie tussen cameras en events

nu naar de yii-kant van de dingen,
je moet een aantal relaties definiëren in de Video's model

// this is pretty straight forward, `videos`.`cameras_idcameras` links to a 
// single camera (one-to-one)
public function getCamera(){
    return $this->hasOne(Camera::className(), ['idcameras' => 'cameras_idcameras']);
}
// link the events table using `cameras` as a pivot table (one-to-many)
public function getEvents(){
    return $this->hasMany(Event::className(), [
        // host machine of event        =>  host machine of camera (from via call)
        'host_machines_idhost_machines' => 'host_machines_idhost_machines'
    ])->via('camera');
}

de VideoController en de zoekfunctie zelf

public function actionIndex() {
    // this will be the query used to create the ActiveDataProvider
    $query =Video::find()
        ->joinWith(['camera', 'events'], true, 'INNER JOIN')
        ->where(['event_type' => [23, 24], 'staff_idreceptionist' => 182])
        ->andWhere('event_timestamp BETWEEN videos.start_time AND videos.end_time');

    $dataProvider = new ActiveDataProvider([
        'query' =>  $query,
    ]);

    return $this->render('index', [
        'dataProvider' => $dataProvider,
    ]);
}

yii behandelt elke video als een enkele record (gebaseerd op pk), wat betekent dat alle dubbele video's worden verwijderd. je hebt afzonderlijke video's, elk met meerdere evenementen, zodat je 'event_type' niet kunt gebruiken en 'event_timestamp' in de weergave, maar je kunt enkele getters declareren in Video model om die info te tonen:

public function getEventTypes(){
    return implode(', ', ArrayHelper::getColumn($this->events, 'event_type'));
}

public function getEventTimestamps(){
    return implode(', ', ArrayHelper::getColumn($this->events, 'event_timestamp'));
}

en het gebruik van de weergave:

<?= GridView::widget([
    'dataProvider' => $dataProvider,
    'columns' => [
        ['class' => 'yii\grid\SerialColumn'],
        'idvideo',
        'eventTypes',
        'eventTimestamps',
        'filelocation',
        //['class' => 'yii\grid\ActionColumn'],
    ],
]); ?>

bewerken :
als je de dubbele video wilt behouden, declareer dan de twee kolommen van events binnen Video model

public $event_type, $event_timestamp;

behoud de originele GridView setup, en voeg een select . toe en indexBy dit op de vraag in VideoController :

$q  = Video::find()
    // spcify fields
    ->addSelect(['videos.idvideo', 'videos.filelocation', 'events.event_type', 'events.event_timestamp'])
    ->joinWith(['camera', 'events'], true, 'INNER JOIN')
    ->where(['event_type' => [23, 24], 'staff_idreceptionist' => 182])
    ->andWhere('event_timestamp BETWEEN videos.start_time AND videos.end_time')
    // force yii to treat each row as distinct
    ->indexBy(function () {
        static $count;
        return ($count++);
    });

bijwerken

een directe staff relatie tot Video is momenteel enigszins problematisch omdat dat meer dan één tabel verwijderd is. er is een probleem met het hier

u voegt echter de staff . toe tabel door deze te koppelen aan het Evenement model,

public function getStaff() {
    return $this->hasOne(Staff::className(), ['idreceptionist' => 'staff_idreceptionist']);
}

waarmee u als volgt kunt zoeken:

->joinWith(['camera', 'events', 'events.staff'], true, 'INNER JOIN')

Filteren vereist enkele kleine updates op de controller, weergave en een SarchModel
hier is een minimale implementatie:

class VideoSearch extends Video
{
    public $eventType;
    public $eventTimestamp;
    public $username;

    public function rules() {
        return array_merge(parent::rules(), [
            [['eventType', 'eventTimestamp', 'username'], 'safe']
        ]);
    }

    public function search($params) {
        // add/adjust only conditions that ALWAYS apply here:
        $q = parent::find()
            ->joinWith(['camera', 'events', 'events.staff'], true, 'INNER JOIN')
            ->where([
                'event_type' => [23, 24],
                // 'staff_idreceptionist' => 182
                // im guessing this would be the username we want to filter by
            ])
            ->andWhere('event_timestamp BETWEEN videos.start_time AND videos.end_time');

        $dataProvider = new ActiveDataProvider(['query' => $q]);

        if (!$this->validate())
            return $dataProvider;

        $this->load($params);

        $q->andFilterWhere([
            'idvideo'                => $this->idvideo,
            'events.event_type'      => $this->eventType,
            'events.event_timestamp' => $this->eventTimestamp,
            'staff.username'         => $this->username,
        ]);

        return $dataProvider;
    }
}

controleur:

public function actionIndex() {
    $searchModel = new VideoSearch();
    $dataProvider = $searchModel->search(Yii::$app->request->queryParams);

    return $this->render('test', [
        'searchModel'  => $searchModel,
        'dataProvider' => $dataProvider,
    ]);
}

en het uitzicht

use yii\grid\GridView;
use yii\helpers\ArrayHelper;

echo GridView::widget([
    'dataProvider' => $dataProvider,
    'filterModel'  => $searchModel,
    'columns'      => [
        ['class' => 'yii\grid\SerialColumn'],
        'idvideo',
        'filelocation',
        [
            'attribute' => 'eventType',     // from VideoSearch::$eventType (this is the one you filter by)
            'value'     => 'eventTypes'     // from Video::getEventTypes() that i suggested yesterday
            // in hindsight, this could have been named better, like Video::formatEventTypes or smth
        ],
        [
            'attribute' => 'eventTimestamp',
            'value'     => 'eventTimestamps'
        ],
        [
            'attribute' => 'username',
            'value'     => function($video){
                return implode(', ', ArrayHelper::map($video->events, 'idevent', 'staff.username'));
            }
        ],
        //['class' => 'yii\grid\ActionColumn'],
    ],
]);


  1. mysql-query om rijgegevens dynamisch naar kolommen te converteren

  2. Kan Unicode niet invoegen met cx-Oracle

  3. Een database opslaan als een sjabloon in Access 2016

  4. sql server ongeldige objectnaam - maar tabellen worden vermeld in SSMS-tabellenlijst