2

PHP/MySQL の使用

現在の月と年の最小日からデータを取得する select ステートメントを作成しようとしています (特定のプレーヤーの「今月」のデータを表示するために使用しています)。月の 1 番目のデータが常に存在するとは限らないため、最初のデータが見つからない場合は、2 番目または 3 番目など、最も小さい月のデータが使用されます。

クエリは次のようにする必要があります。

SELECT * FROM table_name WHERE name = '$username' AND
[...theDate = the least day of data in the current month/year]

1 行のデータが返されます。

これは、「今週」のデータを取得するために使用するクエリです。

SELECT name, data, theDate
FROM table_name
WHERE name = '$username'
AND YEARWEEK(theDate) = YEARWEEK(CURRENT_DATE)
ORDER BY theDate;
4

2 に答える 2

2

これが簡単な解決策です:

SELECT a.*
FROM table_name a
INNER JOIN
(
    SELECT name, MIN(theDate) AS mindate
    FROM table_name
    WHERE MONTH(theDate) = MONTH(CURDATE()) AND
          YEAR(theDate) = YEAR(CURDATE()) AND
          name = '$username'
    GROUP BY name
) b ON a.name = b.name AND a.theDate = b.mindate
ORDER BY theDate

内部副選択は、最初に現在の月と現在の年の最小の日付を選択します。これは、正確に 1 つの結果を返す必要があります。

次に、外側のクエリがこの 1 つの結果に結合されるため、現在の月と年の最初に記録された日の行が取得されます。

プレーヤーごとに 1 日に 1 つの行のみが生成されると仮定すると、さらに単純化できます。

SELECT *
FROM table_name
WHERE MONTH(theDate) = MONTH(CURDATE()) AND
      YEAR(theDate) = YEAR(CURDATE()) AND
      name = '$username'
ORDER BY theDate
LIMIT 1
于 2012-07-14T02:48:47.927 に答える
1

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 時よりも小さい値を持つ行を探しています。

于 2012-07-14T02:42:51.547 に答える