J'aimerais utiliser cette requête mysql complexe dans mon modèle joomla pour générer un scénario et compter les entrées dans une plage de dates spécifique.
Ma requête SQL est:
SELECT T.calendar, COUNT(DATE(M.date)) as number
FROM (
SELECT (CURDATE() - INTERVAL c.number DAY) AS calendar
FROM
(
SELECT singles + tens + hundreds number FROM
(
SELECT 0 singles
UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
) singles
JOIN
(
SELECT 0 tens
UNION ALL SELECT 10 UNION ALL SELECT 20 UNION ALL SELECT 30
UNION ALL SELECT 40 UNION ALL SELECT 50 UNION ALL SELECT 60
UNION ALL SELECT 70 UNION ALL SELECT 80 UNION ALL SELECT 90
) tens
JOIN
(
SELECT 0 hundreds
UNION ALL SELECT 100 UNION ALL SELECT 200 UNION ALL SELECT 300
UNION ALL SELECT 400 UNION ALL SELECT 500 UNION ALL SELECT 600
UNION ALL SELECT 700 UNION ALL SELECT 800 UNION ALL SELECT 900
) hundreds
ORDER BY number DESC
) c
WHERE c.number BETWEEN 0 AND 182
) AS T
LEFT JOIN table_goals AS M ON T.calendar = DATE(M.date)
GROUP BY T.calendar`
Comment puis-je convertir cela en une requête Joomla JDatabase "normale"?
Vous pouvez simplement l'exécuter de la même manière qu'une requête simple.
db->setQuery($my_big_query);
$row = $db->loadObject();
Bien sûr, il serait très difficile de traduire l'instruction SQL pour la construire avec l'API de requête $ query-> select (...) -> from (...).
Il est préférable de simplement traiter la requête avec une grande chaîne, en évitant les goulots d'étranglement PHP).
$query[] = 'SELECT ..';
$query[] = 'FROM ..';
$query[] = 'FROM ..';
$query[] = '(SELECT ..';
...
$query = implode(' ', query);
CONSEIL: N'oubliez pas de définir le mode Grand choix:
$db = JFactory::getDbo();
$db->setQuery('SET sql_big_selects=1');
$db->query();
Je constate que l’inconvénient flagrant de votre bloc de code SQL original est qu’il génère une table dérivée de 1 000 lignes, puis de le pruner aux 182 lignes souhaitées. Il s’agit d’une approche indirecte qui oblige simplement SQL à travailler plus dur que nécessaire. . (Je trouve que c'est une approche intelligente, peu importe - je n'aurais pas envisagé de le faire de cette façon.)
Je recommanderai de générer votre table dérivée constituée uniquement des valeurs de date requises via php, puis LEFT JOIN
, GROUP BY
Et COUNT()
vos données table_goals
Pour un nettoyage. , gérable, morceau de code direct. Ce qui suit fournira le résultat souhaité sans ORDER BY
, WHERE
, ni une sous-requête commentée en dur.
$db = JFactory::getDBO();
try {
$days_ago = 182;
for ($x = $days_ago; $x >= 0; --$x) {
$unions[] = "SELECT " . date("'Y-m-d'", strtotime("-$x day")) . ($x == $days_ago ? " AS `date`" : "");
}
$derived_table = implode(' UNION ', $unions);
$query = $db->getQuery(true)
->select("A.`date`, COUNT(B.`date`) AS `count`")
->from("($derived_table) A")
->leftJoin("table_goals B ON A.`date` = DATE(B.`date`)")
->group("A.`date`");
// echo $query->dump();
$db->setQuery($query);
if (!$results = $db->loadAssocList()) {
echo "Logic Error - Check the derived table generating code";
} else {
foreach ($results as $row) {
echo "<div>{$row['date']} -> {$row['count']}</div>";
}
}
} catch (Exception $e) {
echo "Syntax Error While Getting Timeline Data"; // , $e->getMessage(); // <- don't show getMessage() to the public
}
Quelques notes:
date ASC
, J'exécute une boucle de décrémentation for
.($x == $days_ago)
De la ligne $union
Réduit le fardeau de la requête en attribuant uniquement l'alias de colonne au premier SELECT
. Le SELECT
s suivant héritera du nom de la colonne. .date()
.qn()
pour générer des backticks sur les noms de colonne codés en dur. Ceci est une préférence personnelle pour réduire le gonflement du code. Cette requête ne comporte aucune insécurité, mais vous pouvez appeler qn()
sur tout si vous le souhaitez.DATE()
sur votre table_goals.date
En supposant qu'il s'agit d'un champ de type DATETIME
. S'il s'agit déjà d'un type DATE
, vous pouvez supprimer cet appel dans la ligne leftJoin()
.LEFT JOIN
Est essentielle pour la logique de requête. Si vous utilisez un INNER JOIN
, Vous disqualifierez les lignes où il n'y a pas de valeur de date "pouvant être jointe" - cela signifierait en fait que toutes les lignes à compte zéro disparaîtraient ET si vous vouliez ce genre de résultat, alors la table dérivée doit être entièrement omise de la logique de code.Lorsque j'ai testé l'extrait ci-dessus aujourd'hui (2018-06-19), voici la requête vidée et les résultats affichés:
Query Dump:
SELECT A.`date`, COUNT(B.`date`) AS `count`
FROM (SELECT '2017-12-19' AS `date` UNION SELECT '2017-12-20' UNION SELECT '2017-12-21'
UNION SELECT '2017-12-22' UNION SELECT '2017-12-23' UNION SELECT '2017-12-24'
UNION SELECT '2017-12-25' UNION SELECT '2017-12-26' UNION SELECT '2017-12-27'
UNION ... more rows ...
UNION SELECT '2018-06-15' UNION SELECT '2018-06-16' UNION SELECT '2018-06-17'
UNION SELECT '2018-06-18' UNION SELECT '2018-06-19') A
LEFT JOIN table_goals B ON A.`date` = DATE(B.`date`)
GROUP BY A.`date`
Sortie:
2017-12-19 -> 50
2017-12-20 -> 10
2017-12-21 -> 0
2017-12-22 -> 99
... more rows ...
2018-06-16 -> 382
2018-06-17 -> 152
2018-06-18 -> 20
2018-06-19 -> 0