sql >> Database teknologi >  >> RDS >> Mysql

Komplekse databaseforespørgsler i yii2 med Active Record

Jeg vil antage, baseret på det spørgsmål, du stillede her du kunne lide i kommentarer, at du har angivet hele forespørgslen (ingen andre felter, som du tog ud bare for at vise eksempelkode)

derfor, hvis du kun har brug for de felter, der er angivet i SELECT sætning, kan du optimere din forespørgsel en del:

Først og fremmest deltager du med host_machines kun for at linke cameras og events , men har den samme nøgle host_machines_idhost_machines på begge, så det er ikke nødvendigt, kan du direkte:

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

for det andet joinforbindelsen med ispy.staff , det eneste anvendte felt er idreceptionist i WHERE klausul, det felt findes i events også, så vi kan droppe det fuldstændigt

den sidste forespørgsel her:

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)

skal udsende de samme poster som den i dit spørgsmål, uden nogen identiske rækker
nogle videoduplikater vil stadig eksistere på grund af en til mange relationer mellem cameras og events

nu til yii-siden af ​​tingene,
du skal definere nogle relationer på Videoer 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');
}

VideoController og selve søgefunktionen

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 vil behandle hver video som en enkelt post (baseret på pk), hvilket betyder, at alle videoduplikater fjernes. du vil have enkelte videoer, hver med flere begivenheder, så du vil ikke være i stand til at bruge 'event_type' og 'event_timestamp' i visningen, men du kan erklære nogle getters i Video model for at vise disse oplysninger:

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

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

og visningen brug:

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

rediger :
hvis du vil beholde videoduplikaterne, skal du angive de to kolonner fra events inde i Video model

public $event_type, $event_timestamp;

behold den originale GridView opsætning, og tilføj en select og indexBy dette til forespørgslen inde i 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++);
    });

opdatering

en direkte staff forhold til Video er i øjeblikket noget problematisk, da det er mere end et bord væk fra det. der er et problem om det her

dog tilføjer du staff tabellen ved at linke den til Begivenheden model,

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

der giver dig mulighed for at forespørge sådan her:

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

Filtrering vil kræve nogle små opdateringer på controlleren, view og en SarchModel
her er en minimal implementering:

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;
    }
}

controller:

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

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

og udsigten

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. Hvordan Tanh() virker i PostgreSQL

  2. Sådan installeres MySQL på Debian 8

  3. ORDER BY-elementer skal vises i valglisten, hvis sætningen indeholder en UNION-, INTERSECT- eller EXCEPT-operator (SQL-server)

  4. PHP indlæser ikke php_pgsql.dll på Windows