列を持つInnoDBテーブル 'idtimes'(MySQL 5.0.22-log)があります。
`id` int(11) NOT NULL,
`time` int(20) NOT NULL, [...]
複合一意キー
UNIQUE KEY `id_time` (`id`,`time`)
したがって、IDごとに複数のタイムスタンプがあり、タイムスタンプごとに複数のIDがある場合があります。
すべてのエントリと、各エントリが存在する場合は次に長い時間を取得するクエリを設定しようとしているので、たとえば次のように返す必要があります。
+-----+------------+------------+
| id | time | nexttime |
+-----+------------+------------+
| 155 | 1300000000 | 1311111111 |
| 155 | 1311111111 | 1322222222 |
| 155 | 1322222222 | NULL |
| 156 | 1312345678 | 1318765432 |
| 156 | 1318765432 | NULL |
+-----+------------+------------+
今のところ私は今のところ:
SELECT l.id, l.time, r.time FROM
idtimes AS l LEFT JOIN idtimes AS r ON l.id = r.id
WHERE l.time < r.time ORDER BY l.id ASC, l.time ASC;
もちろん、これは最初の行だけでなく、r.time> l.timeのすべての行を返します...
私は次のような副選択が必要になると思います
SELECT outer.id, outer.time,
(SELECT time FROM idtimes WHERE id = outer.id AND time > outer.time
ORDER BY time ASC LIMIT 1)
FROM idtimes AS outer ORDER BY outer.id ASC, outer.time ASC;
しかし、現在の時刻を参照する方法がわかりません(上記は有効なSQLではないことを知っています)。
単一のクエリでこれを行うにはどうすればよいですか(そして、テーブルを一度に1行ずつステップ実行して最後の値を記憶することに依存する@variablesを使用したくない)。
JOINを行うことは、必要になるかもしれない1つのことです。
SELECT l.id, l.time, r.time FROM
idtimes AS l LEFT JOIN idtimes AS r ON l.id = r.id
私は外部結合が意図的であり、nullを取得したいと思います。詳細は後ほど。
WHERE l.time < r.time ORDER BY l.id ASC, l.time ASC;
あなたはrが欲しいだけです。 l.timeより長い(MIN)時間の最も短い行。これは、サブクエリが必要な場所です。
WHERE r.time = (SELECT MIN(time) FROM idtimes r2 where r2.id = l.id AND r2.time > l.time)
次にヌルに。 「次に高い時間がない」場合、SELECT MIN()はnull(またはそれより悪い)と評価され、それ自体は決して何にも等しくないため、WHERE句が満たされることはなく、「最高時間」 IDごとに、結果セットに表示されることはありません。
JOINを削除し、スカラーサブクエリをSELECTリストに移動することで解決します。
SELECT id, time,
(SELECT MIN(time) FROM idtimes sub
WHERE sub.id = main.id AND sub.time > main.time) as nxttime
FROM idtimes AS main
私は常にサブクエリの使用を避けますSELECT
ブロックまたはFROM
ブロックのいずれかで使用します。コードが「ダーティ」になり、効率が低下することがあります。
それを行うためのよりエレガントな方法は次のとおりです。
これは、JOIN
between idtimesテーブル自体を使用して行うことができ、結合を同じidとtimesに制限します現在の行のtimeよりも。
現在の行の行よりも大きいtimesがない行を除外しないようにするには、LEFT JOIN
を使用する必要があります。
SELECT
i1.id,
i1.time AS time,
i2.time AS greater_time
FROM
idtimes AS i1
LEFT JOIN idtimes AS i2 ON i1.id = i2.id AND i2.time > i1.time
あなたが述べたように、問題はnext_timeがtimeより大きい複数の行があることです。
+-----+------------+--------------+
| id | time | greater_time |
+-----+------------+--------------+
| 155 | 1300000000 | 1311111111 |
| 155 | 1300000000 | 1322222222 |
| 155 | 1311111111 | 1322222222 |
| 155 | 1322222222 | NULL |
| 156 | 1312345678 | 1318765432 |
| 156 | 1318765432 | NULL |
+-----+------------+--------------+
これらの不要な行のすべてをフィルタリングする最良の方法は、time(より大きい)とgreater_time(より小さい)の間にtimesがあるかどうかを調べることです。 )このためid。
SELECT
i1.id,
i1.time AS time,
i2.time AS next_time,
i3.time AS intrudor_time
FROM
idtimes AS i1
LEFT JOIN idtimes AS i2 ON i1.id = i2.id AND i2.time > i1.time
LEFT JOIN idtimes AS i3 ON i2.id = i3.id AND i3.time > i1.time AND i3.time < i2.time
ops、まだfalse next_timeがあります!
+-----+------------+--------------+---------------+
| id | time | next_time | intrudor_time |
+-----+------------+--------------+---------------+
| 155 | 1300000000 | 1311111111 | NULL |
| 155 | 1300000000 | 1322222222 | 1311111111 |
| 155 | 1311111111 | 1322222222 | NULL |
| 155 | 1322222222 | NULL | NULL |
| 156 | 1312345678 | 1318765432 | NULL |
| 156 | 1318765432 | NULL | NULL |
+-----+------------+--------------+---------------+
このイベントが発生する行をフィルタリングし、以下のWHERE
制約を追加します
WHERE
i3.time IS NULL
Voilà、私たちは必要なものを持っています!
+-----+------------+--------------+---------------+
| id | time | next_time | intrudor_time |
+-----+------------+--------------+---------------+
| 155 | 1300000000 | 1311111111 | NULL |
| 155 | 1311111111 | 1322222222 | NULL |
| 155 | 1322222222 | NULL | NULL |
| 156 | 1312345678 | 1318765432 | NULL |
| 156 | 1318765432 | NULL | NULL |
+-----+------------+--------------+---------------+
4年経ってもまだ回答が必要です。
内部選択なしで、min()
およびGROUP BY
から必要なものを取得することもできます。
SELECT l.id, l.time, min(r.time)
FROM idtimes l
LEFT JOIN idtimes r on (r.id = l.id and r.time > l.time)
GROUP BY l.id, l.time;
私はほぼオプティマイザがこれをとにかくアーウィン・スムートの答えと同じものに変えるという多額のお金を賭けるでしょう、そしてそれがより明確であるかどうかは議論の余地がありますが、完全性のためにあります...
ソリューションを提示する前に、それはきれいではないことに注意してください。テーブルに_AUTO_INCREMENT
_列があると、はるかに簡単になります(そうですか?)
_SELECT
l.id, l.time,
SUBSTRING_INDEX(GROUP_CONCAT(r.time ORDER BY r.time), ',', 1)
FROM
idtimes AS l
LEFT JOIN idtimes AS r ON (l.id = r.id)
WHERE
l.time < r.time
GROUP BY
l.id, l.time
_
説明:
(id, time)
_の組み合わせ(一意であることがわかっている)が確実に取得されます。(l.id, l.time)
_ごとに、_r.time
_より大きいfirst _l.time
_を取得します。これは、_r.time
_を介して最初のトークンをスライスすることにより、GROUP_CONCAT(r.time ORDER BY r.time)
を介して_SUBSTRING_INDEX
_ sを最初に順序付けするときに発生します。幸運を祈ります。このテーブルが大きい場合、良いパフォーマンスは期待できません。