web-dev-qa-db-fra.com

Joindre une table existante avec une table générée de dates et compter les lignes pour chaque date?

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"?

4
MyFault

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();
3
Anibal

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:

  • Dans la mesure où l'ordre des résultats doit être date ASC, J'exécute une boucle de décrémentation for.
  • La condition ($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 SELECTs suivant héritera du nom de la colonne. .
  • Regardez bien, j'ai écrit les guillemets simples dans le premier paramètre de date().
  • Je choisis de ne pas appeler 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.
  • J'appelle 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().
  • L'utilisation d'un 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
0
mickmackusa