1 行だけを返すことに関心がある場合、これを行う最も簡単な方法は次のとおりです。
SELECT t.*
FROM table_name t
WHERE t.name = '$username'
AND t.theDate >= CAST(DATE_FORMAT(NOW(),'%Y-%m-01') AS DATE)
AND t.theDate < DATE_ADD(DATE_FORMAT(NOW(),'%Y-%m-01'), INTERVAL 1 MONTH)
ORDER BY s.name DESC, s.theDate DESC
LIMIT 1
ORDER BY は、インデックスがある (または先頭の列がある) という前提に基づいています(name,theDate)
。これは、述語 (つまり、WHERE 句の条件) の最も適切なインデックスになります。name
列が何かと等しくなることがわかっているため、実際には列を並べ替える必要はありません... しかし、このように ORDER BY を指定すると、これにより、MySQL がインデックスに対してリバース スキャン操作を実行し、正しい順序で行を返し、ファイル ソート操作を回避する可能性が高くなります。
注:theDate
任意の関数でそれをラップするのではなく、WHERE 句の条件でベア カラムを指定します... ベア カラムと境界付き範囲を指定することで、MySQL がインデックス レンジ スキャン操作を利用できるようにします。この条件を WHERE 句に含める方法は他にもあります。たとえば...
DATE_FORMAT(t.theDate,'%Y-%m') = DATE_FORMAT(NOW(),'%Y-%m')
これは同等の結果を返しますが、このような述語はサージ可能ではありません。つまり、MySQL はこれを満たすためにインデックスの範囲スキャンを実行できない/実行しません。
特定のユーザーの 1 か月の「最も少ない」日付のすべての行を取得する場合 (質問は、1 行だけが必要であることを示しているようには見えません)、その結果を取得する 1 つの方法を次に示します。
SELECT t.*
FROM table_name t
JOIN ( SELECT s.name
, s.theDate
FROM table_name s
WHERE s.name = '$username'
AND s.theDate >= CAST(DATE_FORMAT(NOW(),'%Y-%m-01') AS DATE)
AND s.theDate < DATE_ADD(DATE_FORMAT(NOW(),'%Y-%m-01'), INTERVAL 1 MONTH)
ORDER BY s.name DESC, s.theDate DESC
LIMIT 1
) r
ON r.name = t.name
AND r.theDate = t.theDate
繰り返しますが、MySQL は先頭の列 (name,theDate) を持つインデックス (利用可能な場合) を使用して、述語を満たし、リバース スキャン操作 (ソートを回避) を実行し、JOIN 操作を実行できます。
注: ここでは、'theDate' がデータ型 DATE (時刻コンポーネントなし) であると想定しています。DATETIME または TIMESTAMP の場合、時間コンポーネントの可能性があり、同じ「日付」を持つ行の時間コンポーネントが異なる場合、そのクエリは特定の「日付」値のすべての行を返さない可能性があります。(たとえば、'2012-07-13 17:30' と '2012-07-13 19:55' は異なる日時値です。) これらの行の両方を返したい場合 (どちらも「7 月 13 日」の日付であるため) 、等値テストの代わりに範囲スキャンを行う必要があります。
SELECT t.*
FROM table_name t
JOIN ( SELECT s.name
, s.theDate
FROM table_name s
WHERE s.name = '$username'
AND s.theDate >= CAST(DATE_FORMAT(NOW(),'%Y-%m-01') AS DATE)
AND s.theDate < DATE_ADD(DATE_FORMAT(NOW(),'%Y-%m-01'), INTERVAL 1 MONTH)
ORDER BY s.name DESC, s.theDate DESC
LIMIT 1
) r
ON t.name = r.name
AND t.theDate >= r.theDate
AND t.theDate < DATE_FORMAT(DATE_ADD(r.theDate,INTERVAL 1 DAY),'%Y-%m-%d')
theDate
最後の 2 行に注意してください...現在の月に見つかった「最小」値以上で、かつ翌日の午前 0 時よりも小さい値を持つ行を探しています。