0

フィールド(id,letter,date)といくつかのデータを含むテーブルがあります。

1 A 2012-01-01
2 B NULL
3 C NULL
4 D 2012-01-15

NULL 値に最も近い非 NULL 値の平均日付を入力したいと考えています。そのように:

1 A 2012-01-01
2 B 2012-01-08
3 C 2012-01-08
4 D 2012-01-15

または、おそらく、そのように:

1 A 2012-01-01
2 B 2012-01-08
3 C 2012-01-11
4 D 2012-01-15

どちらのバリエーションも素晴らしいです。MySQL に実装する簡単な方法はありますか?

前もって感謝します

UPD テーブルはかなり大きく、約 700.000 のレコードと、説明されているような約 50.000 のギャップがあります。

UPD2 少しすっきり: テーブルは次のようになります:

1 A 2012-01-01
2 B NULL
3 C NULL
4 D 2012-01-15
5 E NULL
6 F 2012-01-17
7 G NULL
8 H NULL
9 I 2012-01-20

期待される結果は次のようになります。

1 A 2012-01-01
2 B **2012-01-08**
3 C **2012-01-08**
4 D 2012-01-15
5 E **2012-01-16**
6 F 2012-01-17
7 G **2012-01-18**
8 H **2012-01-18**
9 I 2012-01-20

(アスタリスクは、変更された値に注意することです)。ありがとう

UPD3 みんなありがとう。しかし、単純な式で日付を計算する別の方法でそれを行うだけです。 )) + 分(日付)

4

2 に答える 2

1

クエリ#1

SELECT id,letter,IFNULL(date,dt) date FROM mytable,
(SELECT DATE(mindate + INTERVAL (secdiff/2) SECOND) dt
FROM (SELECT mindate,UNIX_TIMESTAMP(maxdate)
- UNIX_TIMESTAMP(mindate) secdiff
FROM (SELECT MIN(date) mindate FROM mytable) N,
(SELECT MAX(date) maxdate FROM mytable) X) AA) A;

サンプルデータ

mysql> DROP TABLE IF EXISTS mytable;
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE mytable
    -> (
    ->    id int not null auto_increment,
    ->    letter char(1),
    ->    `date` date,
    ->    primary key (id)
    -> );
Query OK, 0 rows affected (0.07 sec)

mysql> INSERT INTO mytable (letter,date) VALUES
    -> ('A','2012-01-01'),('B',NULL),('C',NULL),('D','2012-01-15');
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> SELECT * FROM mytable;
+----+--------+------------+
| id | letter | date       |
+----+--------+------------+
|  1 | A      | 2012-01-01 |
|  2 | B      | NULL       |
|  3 | C      | NULL       |
|  4 | D      | 2012-01-15 |
+----+--------+------------+
4 rows in set (0.00 sec)

mysql>

クエリ#1が実行されました

mysql> SELECT id,letter,IFNULL(date,dt) date FROM mytable,
    -> (SELECT DATE(mindate + INTERVAL (secdiff/2) SECOND) dt
    -> FROM (SELECT mindate,UNIX_TIMESTAMP(maxdate)
    -> - UNIX_TIMESTAMP(mindate) secdiff
    -> FROM (SELECT MIN(date) mindate FROM mytable) N,
    -> (SELECT MAX(date) maxdate FROM mytable) X) AA) A;
+----+--------+------------+
| id | letter | date       |
+----+--------+------------+
|  1 | A      | 2012-01-01 |
|  2 | B      | 2012-01-08 |
|  3 | C      | 2012-01-08 |
|  4 | D      | 2012-01-15 |
+----+--------+------------+
4 rows in set (0.00 sec)

mysql>

QUERY#2(クリーナーバージョン)

このクエリは、UNIXタイムスタンプの平均を使用します。すべての日付がNULLの場合、今日の日付が使用されます。

SELECT id,letter,IFNULL(date,dt) date FROM mytable,
(
    SELECT IF(K=0,DATE(NOW()),avgdt) dt FROM
    (SELECT DATE(FROM_UNIXTIME(AVG(UNIX_TIMESTAMP(date))))
    avgdt FROM mytable) AA,
    (SELECT COUNT(date) K FROM mytable) BB
) A;

クエリ#2が実行されました

mysql> SELECT id,letter,IFNULL(date,dt) date FROM mytable,
    -> (
    ->     SELECT IF(K=0,DATE(NOW()),avgdt) dt FROM
    ->     (SELECT DATE(FROM_UNIXTIME(AVG(UNIX_TIMESTAMP(date))))
    ->     avgdt FROM mytable) AA,
    ->     (SELECT COUNT(date) K FROM mytable) BB
    -> ) A;
+----+--------+------------+
| id | letter | date       |
+----+--------+------------+
|  1 | A      | 2012-01-01 |
|  2 | B      | 2012-01-08 |
|  3 | C      | 2012-01-08 |
|  4 | D      | 2012-01-15 |
+----+--------+------------+
4 rows in set (0.05 sec)

mysql>

試してみる !!!

于 2013-03-22T18:30:54.293 に答える
1

T次のようなテーブルがあるとします。

CREATE TABLE T(
    id INT,
    time DATETIME
);

次のクエリは、各 NULL レコードの境界を示します。

SELECT T.Id
     , MAX(T1.Time) as MinDate
     , MIN(T2.Time) as MaxDate     
  FROM T
INNER JOIN T T1 ON T1.Id < T.Id
               AND T.time IS NULL 
               AND NOT T1.time IS NULL
INNER JOIN T T2 ON T2.id > T.id
               AND T.time IS NULL
               AND NOT T2.time IS NULL
GROUP BY Id

出力は次のようになります。

Id  MinDate     MaxDate
2   2012-01-01  2012-01-15
3   2012-01-01  2012-01-15

したがって、次のステップは、この結果セットの値を使用して更新を行い、NULL を平均などで更新することです。

UPDATE T
INNER JOIN 
(
   SELECT T.Id, MAX(T1.Time) as MinTime, MIN(T2.Time) as MaxTime
     FROM T
   INNER JOIN T T1 ON T1.id < T.id
                 AND T.time IS NULL 
                 AND NOT T1.time IS NULL
   INNER JOIN T T2 ON T2.id > T.id
                 AND T.time IS NULL
                 AND NOT T2.time IS NULL    
   GROUP BY T.ID) T3
 ON T3.id = T.id  
 SET T.time = FROM_UNIXTIME((UNIX_TIMESTAMP(T3.MinTime) + UNIX_TIMESTAMP(T3.MaxTime)) / 2)
 WHERE T.time IS NULL

ここで SQLFiddle を操作する

于 2013-03-22T18:22:05.493 に答える