web-dev-qa-db-ja.com

#__fields_valuesから複数行のデータを収集し、結果セットの各値に特定のエイリアスを割り当てる方法は?

カスタムフィールドのnameusernameおよびvalueを取得する次の(機能する)クエリがあります。

// Get a db connection.
$db = JFactory::getDbo();

// Create a new query object.
$query = $db->getQuery(true);

// Select name and username from USERS table and value from FIELDS_VALUES table.
// Define USERS table as ju
// Define FIELD_VALUES as jfv and match id and item_id to perform an inner join
// Set condition
// Set Order as ascending

$query
    ->select(array('ju.username', 'ju.name', 'jfv.value'))
    ->from($db->quoteName('#__users', 'ju'))
    ->join('INNER', $db->quoteName('#__fields_values', 'jfv') . ' ON (' . $db->quoteName('ju.id') . ' = ' . $db->quoteName('jfv.item_id') . ')')
    ->where($db->quoteName('jfv.value') . ' LIKE ' . $db->quote('C%'))
    ->order($db->quoteName('ju.username') . ' ASC');

// Reset the query using our newly populated query object.
$db->setQuery($query);

// Load results as a list of objects in an array
$results = $db->loadObjectList();
print_r($results);

これは、アパートの建物でユーザーのnameusernamedoorfloorstaircaseとともに表示するために使用されます。この場合、staircase "C"のすべての人々。

テーブル #__fields_valuesはユーザー48

field_id | item_id | value |
============================
2        | 48      | 5     |
============================
3        | 48      | C     |
============================
4        | 48      | 2     |
============================

上記の表では:

  • door値は5
  • staircase値はCです
  • floor値は2

私の問題は、私の出力が次のようになることです:

Array (
    [0] => stdClass Object (
        [username] => C1 [name] => NameOfTheGuy [value] => C
    )...

しかし、私が必要とするのは、floor値ではなく、staircase値を持つことです。

列内の値に基づいてエイリアスを割り当てることはできますか(jvf.staircaseの場合jfv.field_id = 3)?または、私がやりたいことを達成するためのより良い方法はありますか?

1
FollaKY

未加工のピボットクエリ

_SELECT ju.username,
       ju.name,
       MAX(IF(jfv.field_id = 2, jfv.value, NULL)) AS `door`,
       MAX(IF(jfv.field_id = 3, jfv.value, NULL)) AS `staircase`,
       MAX(IF(jfv.field_id = 4, jfv.value, NULL)) AS `floor`
FROM `#__users` AS ju
INNER JOIN `#__fields_values` AS jfv ON ju.id = jfv.item_id
GROUP BY ju.username ASC, ju.name
HAVING MAX(IF(jfv.field_id = 3, jfv.value, NULL)) LIKE 'C%'
_

可能な結果セット:

_| username | name  | door | staircase | floor |
| -------- | ----- | ---- | --------- | ----- |
| FollaKY  | Folla | 5    | C         | 2     |
_

DB Fiddleでデモを表示

PHP/Joomlaコード(未テスト テスト済み):

_$db = JFactory::getDbo();
$juUsername = $db->qn("ju.username");   // Cache this (D.R.Y.)
$juName     = $db->qn("ju.name");       // Cache this (D.R.Y.)
$jfvFieldId = $db->qn("jfv.field_id");  // Cache this (D.R.Y.)
$jfvValue   = $db->qn("jfv.value");     // Cache this (D.R.Y.)

$query = $db->getQuery(true)
    ->select([
        $juUsername,
        $juName,
        "MAX(IF($jfvFieldId = 2, $jfvValue, NULL)) AS " . $db->qn('door'),
        "MAX(IF($jfvFieldId = 3, $jfvValue, NULL)) AS " . $db->qn('staircase'),
        "MAX(IF($jfvFieldId = 4, $jfvValue, NULL)) AS " . $db->qn('floor')
    ])
    ->from($db->qn('#__users', 'ju'))
    ->innerJoin($db->qn('#__fields_values', 'jfv') . ' ON ' . $db->qn('ju.id') . ' = ' . $db->qn('jfv.item_id'))
    ->group([
        "$juUsername ASC",  // declare the sorting order here
        $juName
    ])
    ->having("MAX(IF($jfvFieldId = 3, $jfvValue, NULL) LIKE " . $db->q("C%"));

// echo $query->dump();  // uncomment if you want to confirm the rendered query
try {
    $db->setQuery($query);
    echo "<pre>";
    var_export($db->loadObjectList());
} catch (Exception $e) {
    JFactory::getApplication()->enqueueMessage("Query Syntax Error: " . $e->getMessage(), 'error');  // never show getMessage() to public
}
_

手順の説明:

  1. usersテーブルを_fields_values_テーブルに結合します。これは、users行ごとに1対多の関係です。 _INNER JOIN_の使用は、_LEFT JOIN_とは異なります。これは、_INNER JOIN_が、関連する_fields_values_行が1つもないusers行を省略しているためです。

  2. 同じユーザーに複数の行が表示されないようにするために、_GROUP BY_が実装されています。これにより、ユーザーごとに「集計データ」(つまり、そのユーザーに固有の_fields_values_データのクラウド|マス|クラスター|パイル)が作成されます。

  3. _fields_values_の行固有の値に基づいて、結果セットの対象となるユーザーを決定するには、集計データで比較を実行する必要があります。 HAVING句は、すべての集計データをチェックします。集約行に_field_id_の_3_がない場合、その行にはNULLの値が割り当てられます(このサブプロセスのスコープ内)。 _field_id_が_3_の場合、元のvalueが維持されます。これにより、_field_id_の_3_の行とvalueNULLの行がない限り、valueNULLになります)MAX()-この唯一の値に比較ロジックを適用します。

  4. 結合、グループ化、およびフィルターのすべてが完了したので、今度は実際の結果セットを修正します。この時点では、SELECT句に送信されている「集約データ」がまだありますが、結果セットに「集約データ」を含めて配信することはできません(つまり、行をフラット化する必要があります)。 HAVING句と同じfilter&maxテクニックを使用して、結果セットの各行に対して生成する特定の各列を手動で記述し、ASの後に任意の列エイリアスを割り当てます。できました。


これをコード化する方法はいくつかあります...

  • "ドライ。" (私のスクリプトのコメントとして見つかります)は「Do n't Repeat Yourself」の略です。これは、スクリプトを無駄のないクリーンな状態に保つのに役立つベストプラクティステクニックであり、phpが同じタスクを複数回実行する必要がなくなります。
  • MAX()内のIFステートメントの代わりに_CASE-WHEN_ステートメントが表示される場合がありますが、これらは論理的に交換可能です。
  • 結果セットのソートは、このシナリオでは_GROUP BY_句の内部で実行できますが、_ORDER BY_句を使用して実行することもできます。
  • [〜#〜]なし[〜#〜]$db->qn()呼び出しのは実際にこのクエリに必要なのは、テーブルまたは列の名前にモンキーレンチ文字が含まれていないか、または MySQL予約キーワードのリスト に含まれていないためです。ご覧のとおり、これらのすべての連結と引用呼び出しは構文を大きく膨らませ、タイプミスの読み取りと検索をはるかに困難にします。これが私のプロジェクトの場合、バックティックを生成する呼び出しをすべて削除する傾向があります。また、_C%_などの静的文字列の引用呼び出しも削除しますが、これはすべて個人の好みの問題です。
0
mickmackusa