sql >> Databasteknik >  >> RDS >> Mysql

Komplexa databasfrågor i yii2 med Active Record

Jag antar, baserat på frågan du ställde här du gillade i kommentarer som du angav hela frågan (inga andra fält, som du tog ut bara för att visa exempelkoden)

därför, om du bara behöver de fält som anges i SELECT uttalande, kan du optimera din fråga ganska mycket:

Först och främst går du med med host_machines bara för att länka cameras och events , men har samma nyckel host_machines_idhost_machines på båda, så det behövs inte, kan du direkt:

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

för det andra, sammanfogningen med ispy.staff , det enda fältet som används är idreceptionist i WHERE klausul, det fältet finns i events också så att vi kan släppa det helt

den sista frågan här:

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)

bör mata ut samma poster som den i din fråga, utan några identiska rader
vissa videodubbletter kommer fortfarande att finnas på grund av en till många relation mellan cameras och events

nu till yii-sidan av saken,
du måste definiera några relationer på Videor modell

// 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 och själva sökfunktionen

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 kommer att behandla varje video som en enda post (baserat på pk), vilket innebär att alla videodubbletter tas bort. du kommer att ha enstaka videor, var och en med flera händelser så att du inte kommer att kunna använda 'event_type' och 'event_timestamp' i vyn men du kan deklarera några getters i Video modell för att visa den informationen:

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

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

och vyn använder:

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

redigera :
om du vill behålla videoduplikaterna, deklarera de två kolumnerna från events inuti Video modell

public $event_type, $event_timestamp;

behåll den ursprungliga GridView konfigurera och lägg till en select och indexBy detta till frågan 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++);
    });

uppdatering

en direkt staff relation till Video är för närvarande något problematiskt eftersom det är mer än en tabell bort från det. det finns ett problem om det här

men du lägger till staff tabellen genom att länka den till Event modell,

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

som gör att du kan fråga så här:

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

Filtrering kommer att kräva några små uppdateringar på styrenheten, vyn och en SarchModel
här är 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;
    }
}

styrenhet:

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

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

och utsikten

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. Duplicering som involverar SUM, LEFT JOIN och GROUP BY

  2. SQL Server Escape ett understreck

  3. PHP MySql (1045) Åtkomst nekad för användare

  4. Hur man ansluter till en MySQL-datakälla i Visual Studio