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クエリに変換するにはどうすればよいですか?
単純なクエリと同じ方法で実行できます。
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();
元の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
に列エイリアスを割り当てるだけでクエリの膨張を減らします-後続のSELECT
sは列名を継承します。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
_