Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Złożone zapytania do bazy danych w yii2 z Active Record

przyjmę, na podstawie pytania, które zadałeś tutaj podobały Ci się komentarze, które podałeś całe zapytanie (brak innych pól, które wyjąłeś tylko po to, aby pokazać przykładowy kod)

dlatego, jeśli potrzebujesz tylko pól określonych w SELECT oświadczenie, możesz trochę zoptymalizować swoje zapytanie:

po pierwsze, dołączasz za pomocą host_machines tylko po to, by połączyć cameras i events , ale mają ten sam klucz host_machines_idhost_machines na obu, więc nie jest to potrzebne, możesz bezpośrednio:

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

po drugie, połącz z ispy.staff , jedynym używanym polem jest idreceptionist w WHERE klauzula, to pole istnieje w events abyśmy mogli go całkowicie porzucić

ostatnie pytanie tutaj:

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)

powinien wyświetlać te same rekordy, co w pytaniu, bez identycznych wierszy
niektóre duplikaty wideo nadal będą istnieć z powodu relacji jeden do wielu między cameras i events

teraz po yii strony rzeczy,
musisz zdefiniować pewne relacje w filmach 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');
}

Kontroler wideo i sama funkcja wyszukiwania

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 będzie traktować każdy film jako pojedynczy zapis (w oparciu o pk), co oznacza, że ​​wszystkie duplikaty wideo zostaną usunięte. będziesz mieć pojedyncze filmy, każdy z wieloma wydarzeniami, więc nie będziesz mógł użyć 'event_type' i 'event_timestamp' w widoku, ale możesz zadeklarować kilka getterów w Wideo model, aby pokazać te informacje:

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

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

i wykorzystanie widoku:

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

edytuj :
jeśli chcesz zachować duplikaty wideo, zadeklaruj dwie kolumny z events wewnątrz Wideo model

public $event_type, $event_timestamp;

zachowaj oryginalny GridView skonfigurować i dodać select i indexBy to do zapytania w 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++);
    });

aktualizacja

bezpośredni staff związek z Video jest obecnie nieco problematyczny, ponieważ jest więcej niż jeden stół od niego. jest z tym problem tutaj

jednak dodajesz staff tabeli, łącząc ją z Wydarzeniem model,

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

co pozwoli ci na zapytanie w ten sposób:

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

Filtrowanie będzie wymagać kilku drobnych aktualizacji kontrolera, widoku i SarchModel
Oto minimalna implementacja:

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

kontroler:

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

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

i widok

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. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Ostrzeżenie:mysqli_query() oczekuje, że parametr 1 będzie wartością logiczną mysqli

  2. Doprecyzowanie wyników wyszukiwania na podstawie filtrów

  3. MySQL SELECT DISTINCT wiele kolumn

  4. ModuleNotFoundError:Brak modułu o nazwie „mysql”

  5. Automatycznie łap wyjątki dla PDO w PHP