J'ai la requête (de travail) suivante pour obtenir name
, username
et le value
d'un champ personnalisé:
// 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);
Il est utilisé pour afficher les name
et username
d'un utilisateur avec door
, floor
et staircase
dans un immeuble à appartements. Dans ce cas, toutes les personnes de staircase
"C".
La table #__fields_values
ressemble à ceci pour l'utilisateur 48
:
field_id | item_id | value |
============================
2 | 48 | 5 |
============================
3 | 48 | C |
============================
4 | 48 | 2 |
============================
Dans le tableau ci-dessus:
door
est 5
staircase
est C
floor
est 2
Mon problème est que ma sortie ressemble à ceci:
Array (
[0] => stdClass Object (
[username] => C1 [name] => NameOfTheGuy [value] => C
)...
mais ce dont j'ai besoin, c'est d'avoir la valeur floor
, pas la valeur staircase
.
Est-il possible d'affecter un alias en fonction de la valeur à l'intérieur d'une colonne (disons jvf.staircase
si jfv.field_id = 3
)? Ou existe-t-il une meilleure façon de réaliser ce que je veux faire?
Requête de pivot brut
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%'
Jeu de résultats possible:
| username | name | door | staircase | floor |
| -------- | ----- | ---- | --------- | ----- |
| FollaKY | Folla | 5 | C | 2 |
Code PHP/Joomla (non testé testé):
$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
}
Explication par étapes:
Joignez la table users
à la table fields_values
. Il s'agit d'une relation un-à-plusieurs pour chaque ligne users
. L'utilisation de INNER JOIN
Est différente de LEFT JOIN
Car INNER JOIN
Omettra users
lignes qui n'ont pas au moins une ligne fields_values
À associer.
Pour empêcher plusieurs lignes pour le même utilisateur, GROUP BY
Est implémenté. Cela crée des "données agrégées" (en d'autres termes, un nuage | masse | cluster | pile de fields_values
Données spécifiques à cet utilisateur) pour chaque utilisateur.
Pour déterminer quels utilisateurs sont éligibles pour l'ensemble de résultats en fonction d'une valeur spécifique à la ligne dans fields_values
, Vous devrez effectuer une comparaison sur les données agrégées. La clause HAVING
vérifiera toutes les données agrégées. Si une ligne agrégée n'a pas de field_id
De 3
, Une valeur de NULL
lui est attribuée (dans le cadre de ce sous-processus). Si field_id
Est 3
, L'original value
est conservé. Cela crée (sauf si vous avez une ligne avec field_id
De 3
Et un value
de NULL
- auquel cas chaque value
est NULL
) une seule valeur non NULL à être "récupérée" par MAX()
- appliquez votre logique de comparaison sur cette seule valeur.
Maintenant que toutes les jointures, regroupements et filtres sont terminés, il est temps de corriger le jeu de résultats réel. À ce stade, il reste des "données agrégées" envoyées à la clause SELECT
, mais l'ensemble de résultats ne peut pas être livré avec des "données agrégées" (en d'autres termes, les lignes doivent être aplaties). En utilisant la même technique filter & max que dans la clause HAVING
, écrivez manuellement chaque colonne spécifique que vous souhaitez générer pour chaque ligne du jeu de résultats - attribuez l'alias de colonne que vous souhaitez après AS
. Terminé.
Maintenant, il existe plusieurs façons de coder cela ...
CASE-WHEN
Au lieu des instructions IF
à l'intérieur de MAX()
elles sont logiquement interchangeables.GROUP BY
Dans ce scénario, mais peut également être effectué avec une clause ORDER BY
.$db->qn()
sont en fait nécessaire pour cette requête car aucun des noms de table ou de colonne n'a de caractères monkeywrenching ou n'est sur la liste des mots clés réservés MySQL . Comme vous pouvez le voir, toutes ces concaténations et appels de citations gonflent vraiment la syntaxe et rendent la lecture et la recherche de fautes de frappe beaucoup plus difficiles. Si tel était mon projet, je serais enclin à supprimer tous les appels générateurs de backtick et je supprimerais également les appels de devis sur des chaînes statiques comme C%
... mais tout cela est une question de préférence personnelle.