0

行のタイムスタンプと同じ値Durationの前の行の違いである単一のクエリで、このテーブルに設定する方法に頭を悩ませようとしています。SkillTargetID似たような質問をいくつか見つけましたが (これは特に役に立ちました)、たとえば、月の値に基づいて 2 行目がどれくらい離れているかを予測することができました。私のデータの各行について、その「姉妹」行はそれに隣接している場合もあれば、隣接していない場合もあります。

この例のテーブルの縮小版を次に示します。

mysql> select * from testdur order by id;
+----+---------------+--------------+----------+
| id | SkillTargetID | UTC_DateTime | Duration |
+----+---------------+--------------+----------+
|  1 |          5000 |   1323719341 |     NULL |
|  2 |          5010 |   1323719341 |     NULL |
|  3 |          5000 |   1323719342 |     NULL |
|  4 |          5010 |   1323719342 |     NULL |
|  5 |          5000 |   1323719343 |     NULL |
|  6 |          5055 |   1323719345 |     NULL |
|  7 |          5010 |   1323719350 |     NULL |
|  8 |          5010 |   1323719441 |     NULL |
|  9 |          5010 |   1323719444 |     NULL |
| 10 |          5000 |   1323719445 |     NULL |
| 11 |          5055 |   1323719445 |     NULL |
| 12 |          5060 |   1323719445 |     NULL |
| 13 |          5000 |   1323719445 |     NULL |
| 14 |          5010 |   1323719445 |     NULL |
| 15 |          5060 |   1323719446 |     NULL |
| 16 |          5000 |   1323719460 |     NULL |
| 17 |          5000 |   1323719460 |     NULL |
| 18 |          5060 |   1323719500 |     NULL |
+----+---------------+--------------+----------+

テーブルの基本データはこの規則に従います。この例のデータが示すように、 で並べ替えるとid、 の値UTC_DateTimeは常に前の行以上になります。SkillTargetID同じ値を持つ異なる値の順序はUTC_DateTime予測できず、多くの行で同じUTC_DateTimeおよびSkillTargetID(16 と 17 など) になります。

これまでに思いついた最善の試みには、前に関連付けられた行が存在する場合は、それを見つけるためのサブクエリが含まれています (2 番目も選択したUTC_DateTimeので、何が減算されているかがわかります)。

SELECT
 t.id,
 t.SkillTargetID,
 t.UTC_DateTime,
 t2.UTC_DateTime AS UTC_DateTime2,
 (CASE WHEN t2.UTC_DateTime IS NULL THEN 0 ELSE t.UTC_DateTime - t2.UTC_DateTime END) AS Duration
FROM testdur t LEFT JOIN testdur t2
 ON t.SkillTargetID = t2.SkillTargetID
 AND t2.id = (
  SELECT id FROM testdur
  WHERE SkillTargetID = t.SkillTargetID AND id < t.id
  ORDER BY id DESC
  LIMIT 1 )
ORDER BY t.id;
+----+---------------+--------------+---------------+----------+
| id | SkillTargetID | UTC_DateTime | UTC_DateTime2 | Duration |
+----+---------------+--------------+---------------+----------+
|  1 |          5000 |   1323719341 |          NULL |        0 |
|  2 |          5010 |   1323719341 |          NULL |        0 |
|  3 |          5000 |   1323719342 |    1323719341 |        1 |
|  4 |          5010 |   1323719342 |    1323719341 |        1 |
|  5 |          5000 |   1323719343 |    1323719342 |        1 |
|  6 |          5055 |   1323719345 |          NULL |        0 |
|  7 |          5010 |   1323719350 |    1323719342 |        8 |
|  8 |          5010 |   1323719441 |    1323719350 |       91 |
|  9 |          5010 |   1323719444 |    1323719441 |        3 |
| 10 |          5000 |   1323719445 |    1323719343 |      102 |
| 11 |          5055 |   1323719445 |    1323719345 |      100 |
| 12 |          5060 |   1323719445 |          NULL |        0 |
| 13 |          5000 |   1323719445 |    1323719445 |        0 |
| 14 |          5010 |   1323719445 |    1323719444 |        1 |
| 15 |          5060 |   1323719446 |    1323719445 |        1 |
| 16 |          5000 |   1323719460 |    1323719445 |       15 |
| 17 |          5000 |   1323719460 |    1323719460 |        0 |
| 18 |          5060 |   1323719500 |    1323719446 |       54 |
+----+---------------+--------------+---------------+----------+

明らかに、このような UPDATE は、このテーブルが大きくなるにつれて非常に速く厄介になります。ぐるぐる回る前に思いついたのはこれだけです。

UPDATE testdur t SET t.Duration = t.UTC_DateTime - (
 SELECT UTC_DateTime FROM testdur
 WHERE SkillTargetID = t.SkillTargetID AND id < t.id
 ORDER BY id DESC LIMIT 1 );
ERROR 1093 (HY000): You can't specify target table 't' for update in FROM clause

他にどのようなオプションがありますか?

使用したテストデータは次のとおりです。

CREATE TABLE `testdur` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `SkillTargetID` int(10) unsigned NOT NULL,
  `UTC_DateTime` int(10) unsigned NOT NULL,
  `Duration` int(10) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO testdur (SkillTargetID,UTC_DateTime) VALUES (5000,1323719341),(5010,1323719341),(5000,1323719342),(5010,1323719342),(5000,1323719343),(5055,1323719345),(5010,1323719350),(5010,1323719441),(5010,1323719444),(5000,1323719445),(5055,1323719445),(5060,1323719445),(5000,1323719445),(5010,1323719445),(5060,1323719446),(5000,1323719460),(5000,1323719460),(5060,1323719500);

ボーナス - 注文済みのデータが既に含まれている場合、新しい複数行データの挿入中にこれを行うことは可能idですか? 次のような場合:

INSERT INTO testdur (id,SkillTargetID,UTC_DateTime) VALUES
(19,5010,1323719505),
(20,5055,1323719510);

事前に助けてくれてありがとう!

4

1 に答える 1

1

@ mysql 変数を実装することで、おそらくもっと単純化できます。ID を取得するためのプレクエリと、それに関連付けられた次の ID を取得し、元のテーブルに 2 回 (異なるエイリアスで) 結合して適切な値を取得し、必要な計算を実行します...したがって、「LastID " 列は @WasLastID 値が何であれ、それが同じスキル ターゲット ID 資格内にあった場合です。そうでない場合は、ゼロに戻されます。AFTER THAT TEST が適用されると、@WasLastID は現在のレコードの ID を次のエントリのテストの基準として取得します。

select
      t.id,
      t.SkillTargetID,
      t.UTC_DateTime,
      t.UTC_DateTime2,
      if( @WasLastTarget = t.SkillTargetID, @WasLastID, 0 ) LastID,
      @WasLastID := t.ID as tmpLastID,
      @WasLastTarget := t.SkillTargetID as tmpLastTarget
   from
      testdur t,
      ( select @WasLastID = 0,
               @WasLastTarget := 0 ) sqlvars
   order by
      t.skillTargetID,
      t.id

@ 変数のいずれかが処理される前に order by が結果セットに適用されるため、上記のクエリは本質的に次の結果を作成し、@ last ターゲット比較を適用するときにすべてのスキルターゲットが適切に順序付けられるようにします...

    +----+---------------+--------------+--------+------------+---------------+
    | id | SkillTargetID | UTC_DateTime | LastID |  tmpLastID | tmpLastTarget |
    +----+---------------+--------------+--------+------------+---------------+
    |  1 |          5000 |   1323719341 |     0  |      1     | 5000 
    |  3 |          5000 |   1323719342 |     1  |      3     | 5000
    |  5 |          5000 |   1323719343 |     3  |      5     | 5000
    | 10 |          5000 |   1323719445 |     5  |     10     | 5000
    | 13 |          5000 |   1323719445 |    10  |     13     | 5000
    | 16 |          5000 |   1323719460 |    13  |     16     | 5000
    | 17 |          5000 |   1323719460 |    16  |     17     | 5000
-- BREAK BETWEEN SKILL TARGET...
    |  2 |          5010 |   1323719341 |     0  |      2     | 5010
    |  4 |          5010 |   1323719342 |     2  |      4     | 5010
    |  7 |          5010 |   1323719350 |     4  |      7     | 5010
    |  8 |          5010 |   1323719441 |     7  |      8     | 5010
    |  9 |          5010 |   1323719444 |     8  |      9     | 5010
    | 14 |          5010 |   1323719445 |     9  |     14     | 5010
-- BREAK BETWEEN SKILL TARGET...
    |  6 |          5055 |   1323719345 |     0  |      6     | 5055
    | 11 |          5055 |   1323719445 |     6  |     11     | 5055
-- BREAK BETWEEN SKILL TARGET...
    | 12 |          5060 |   1323719445 |     0  |     12     | 5060
    | 15 |          5060 |   1323719446 |    12  |     15     | 5060
    | 18 |          5060 |   1323719500 |    15  |     18     | 5060
    +----+---------------+--------------+--------+------------+---------------+

さて、以上のこととサンプルが提供されたので、次のように拡張できます...

select
      PreQuery.*,
      CASE WHEN t1.UTC_DateTime IS NULL THEN 0 
           ELSE t2.UTC_DateTime - t1.UTC_DateTime END) AS Duration
   from
      ( above full select query ) as PreQuery
         LEFT JOIN testdur t1
            on PreQuery.ID = t1.ID
         LEFT JOIN testdur t2
            on PreQuery.LastID = t2.ID
于 2012-04-11T14:16:00.193 に答える