2012-04-25 10 views
5

はじめに...
私はこの質問に遭遇しました:Difference between 2 adjacent fields - Date - PHP MYSQLと目標を達成しようとしていました。純粋なMySQL。
もう一つの質問(Subtracting one row of data from another in SQL)は、MySQLと似たようなものを作る方法を理解する助けになりました。解決策はまだ固定値または仮定されたデータの順序のいずれかに捨てられているので、問題を解決しませんでしたが、方法論を理解するのに役立ちました。
もう1つの質問()が、次/前の行から値を取得する方法を説明しています。これはまだいくつかの固定値に依存していますが、このテクニックの使い方を学びました。MySQLのdate diffの反復クエリ - 合理化クエリまたは最適化データ構造

  1. 主キー(id)が上昇し、許可される "穴" を命じた:

    CREATE TABLE `foo` (
        `id` int(11) NOT NULL AUTO_INCREMENT, 
        `dateof` date NOT NULL, 
        PRIMARY KEY (`id`) 
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
    
    id | dateof 
    -----+------------ 
        1 | 2012-01-01 
        2 | 2012-01-02 
        11 | 2012-01-04 
        12 | 2012-01-01 
        13 | 2012-01-02 
        14 | 2012-01-09 
    111 | 2012-01-01 
    112 | 2012-01-01 
    113 | 2012-01-01 
    

    は、2つの前提があります。

    は、私は、このテーブルfooを持っていると言います。
  2. dateofの各日付の日付は有効です。意味は「NULL」で、デフォルト値はありません(0000-00-00)。 私はすべての行を反復処理し、前のエントリで渡された日数を計算したい、これを受信するために:私は、このソリューションに来た私が学んだすべてで
id | date  | days_diff 
-----+------------+----------- 
    1 | 2012-01-01 |  0 
    2 | 2012-01-02 |  1 
    11 | 2012-01-04 |  2 
    12 | 2012-01-01 | -3 
    13 | 2012-01-02 |  1 
    14 | 2012-01-09 |  7 
111 | 2012-01-01 | -8 
112 | 2012-01-01 |  0 
113 | 2012-01-01 | 30 

(たとえばソリューション1 、別のものがあるので):

SELECT 
    f.id, 
    DATE_FORMAT(f.dateof, '%b %e, %Y') AS date, 
    (SELECT DATEDIFF(f.dateof, f2.dateof) 
     FROM foo f2 
     WHERE f2.id = (
      SELECT MAX(f3.id) FROM foo f3 WHERE f3.id < f.id 
     ) 
    ) AS days_diff 
FROM foo f; 

(ここではfiddleはhttp://sqlfiddle.com/#!2/099fc/3)。

これは、魅力的なように機能します。データベースには数個のエントリしか存在しません。多くの場合にはさらに悪化します:

EXPLAIN: 
id select_type  table type possible_keys key  key_len ref rows Extra 
1 PRIMARY   f  ALL NULL   NULL NULL NULL 17221 
2 DEPENDENT SUBQUERY f2 eq_ref PRIMARY  PRIMARY 4  func 1  Using where 
3 DEPENDENT SUBQUERY f3 index PRIMARY  PRIMARY 4  NULL 17221 Using where; Using index 

18031行:期間:8.672秒。フェッチ:228.515

私はdateof列にインデックスを追加することを考えた:

CREATE TABLE `foo` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `dateof` date DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `dateof` (`dateof`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

...と小さな改善獲得:

EXPLAIN: 
id select_type  table type possible_keys key  key_len ref rows Extra 
1 PRIMARY   f  index NULL   dateof 4  NULL 18369 Using index 
2 DEPENDENT SUBQUERY f2 eq_ref PRIMARY  PRIMARY 4  func 1  Using where 
3 DEPENDENT SUBQUERY f3 index PRIMARY  dateof 4  NULL 18369 Using where; Using index 

18031行:期間:8.406秒。フェッチ:219.281

私は、InnoDBに比べてMyISAMの利点について、どこかで読んでいることを思い出しました。だから私は、MyISAMテーブルに変更:

ALTER TABLE `foo` ENGINE = MyISAM; 

18031行:期間:5.671秒。フェッチ:151.610

確かにそれは良いが、まだ遅いです。

Iは、別のアルゴリズム(溶液2)と試みた:

SELECT 
    f.id, 
    DATE_FORMAT(f.dateof, '%b %e, %Y') AS date, 
    (SELECT DATEDIFF(f.dateof, f2.dateof) 
    FROM foo f2 
    WHERE f2.id < f.id 
    ORDER BY f2.id DESC 
    LIMIT 1 
) AS days_diff 
FROM foo f; 

...しかし、それも遅かった:

18031行:持続時間:15.609 秒。フェッチ:184.656


このタスクをより速く行っ持つためには、このクエリまたはデータ構造を最適化するための他の方法はありますか?

+0

私はあなたのニーズにより適切なデータ構造が異なるかもしれないと思います。このデータをどのように使用しようとしているか少し詳しくお聞かせください。 – eggyal

+0

@eggyal特に何もありません。私は役に立つかもしれない何かを学ぼうとしているだけです:) – bostaf

答えて

5

適度なサイズのテーブルであっても、アプローチが非常に遅いことは驚くことではありません。

理論的には、LAG分析関数を使用してO(n)時間の結果を計算することは可能ですが、残念ながらMySQLではサポートされていません。しかし、あなたは、変数を使用してMySQLでLAGをエミュレートすることができます。これは、より速く、あなたがやろうとしているものよりも数桁にする必要があり

SELECT 
    id, 
    DATE_FORMAT(f.dateof, '%b %e, %Y') AS date, 
    DATEDIFF(dateof, @prev) AS days_diff, 
    @prev := dateof 
FROM FOO, (SELECT @prev := NULL) AS vars 
ORDER BY id 

+0

彼はCROSS APPLYタイプステートメントの恩恵を受けているかもしれませんが、それは悲しいことにMySQLでも利用できません。 MySQLがなぜそんなに人気があるのか​​思い出してください。 –

+3

ニースの答えは、BTW。ここにそれが動作していることを示すフィドルのリンクです - http://sqlfiddle.com/#!2/099fc/5 –

+1

このトリックは華麗です。クエリは私のサンプルデータセットで即座に実行され、約1秒で200万行が実行されます。ソリューションに感謝します。特にテクニックのおかげで、便利です。 – bostaf