web-dev-qa-db-ja.com

既存のテーブルを生成された日付のテーブルと結合し、各日付の行をカウントしますか?

Joomlaモデルでこの複雑なmysqlクエリを使用して、タイムラインを生成し、特定の日付範囲内のエントリをカウントしたいと思います。

私のSQLクエリは:

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`

これを「通常の」Joomla JDatabaseクエリに変換するにはどうすればよいですか?

4
MyFault

単純なクエリと同じ方法で実行できます。

    db->setQuery($my_big_query);
    $row = $db->loadObject();

もちろん、SQLステートメントを変換してQuery API $ query-> select(...)-> from(...)で作成することは非常に困難です。

PHPパフォーマンスのボトルネックを回避して、大きな文字列でクエリを処理することをお勧めします。

    $query[] = 'SELECT ..';
    $query[] = 'FROM ..';
    $query[] = 'FROM ..';
    $query[] = '(SELECT ..';
    ...
    $query = implode(' ', query);

ヒント:大きな選択モードを設定することを忘れないでください:

    $db = JFactory::getDbo();
    $db->setQuery('SET sql_big_selects=1');
    $db->query();
3
Anibal

元のsqlコードブロックの明らかな欠点は、1000行の派生テーブルを生成し、それをプルーニングして目的の182行に戻すことです-これは間接的なアプローチであり、SQLを必要以上に難しくします。 (私はそれが賢明なアプローチであると思います-私はそのようにすることを考えなかったでしょう。)

Php、次に_LEFT JOIN_、_GROUP BY_、およびCOUNT()_table_goals_データを使用して、クリーンで管理しやすい日付値のみで構成される派生テーブルを生成することをお勧めします。直接的なコード。以下は、_ORDER BY_、WHERE、またはハードコードされた詳細なサブクエリなしで、意図した結果を提供します。

_$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
}
_

いくつかのメモ:

  • 結果セットの順序は_date ASC_である必要があるため、デクリメントforループを実行しています。
  • _($x == $days_ago)_行の_$union_条件は、最初のSELECTに列エイリアスを割り当てるだけでクエリの膨張を減らします-後続のSELECTsは列名を継承します。
  • 注意して見てください。date()の最初のパラメーターに一重引用符を書いています。
  • ハードコーディングされた列名にバックティックを生成するためにqn()を呼び出さないことを選択しています。これは、コードの膨張を減らすための個人的な好みです。このクエリには問題はありませんが、必要に応じてすべてに対してqn()を呼び出すことができます。
  • _table_goals.date_をDATETIMEタイプのフィールドであると想定して、DATE()を呼び出しています。すでにDATEタイプの場合は、leftJoin()行でその呼び出しを削除できます。
  • _LEFT JOIN_の使用は、クエリロジックにとって重要です。 _INNER JOIN_を使用すると、「結合可能な」日付値がなかった行が失格になります。これは、すべてのゼロカウント行が非表示になることを意味し、そのような結果が必要な場合は、派生テーブルは、コードロジックから完全に除外する必要があります。

今日(2018-06-19)に上記のスニペットをテストしたところ、これはダンプされたクエリと表示された結果行です。

クエリダンプ:

_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`
_

出力:

_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