3

MySQL5を使用していて、現在必要な情報を取得するクエリがありますが、パフォーマンスの点で改善できると思います。

これが私が作成したクエリです(このガイドにほぼ従っています):

SELECT d.*, dc.date_change, dc.cwd, h.name as hub
FROM livedata_dom AS d
      LEFT JOIN ( SELECT dc1.*
        FROM livedata_domcabling as dc1
        LEFT JOIN livedata_domcabling AS dc2
        ON dc1.dom_id = dc2.dom_id AND dc1.date_change < dc2.date_change
        WHERE dc2.dom_id IS NULL
        ORDER BY dc1.date_change desc) AS dc ON (d.id = dc.dom_id)
      LEFT JOIN livedata_hub AS h ON (d.id = dc.dom_id AND dc.hub_id = h.id)
WHERE d.cluster = 'localhost'
GROUP BY d.id;

編集:「domcabling」にnullのdate_changeを持つエントリと、同じ「dom」の日付を持つ別のエントリがある場合に、複数のdomエントリを取得しないようにORDER BY +GROUPBYを使用します。

バズーカでネズミを殺しているような気がします。このクエリは3秒以上かかり、「livedata_dom」と「livedata_domcabling」に約5kのエントリしかありません。また、EXPLAINは、2つのファイルソートが使用されていることを示しています。

+----+-------------+------------+--------+-----------------------------+-----------------------------+---------+-----------------+------+----------------------------------------------+
| id | select_type | table      | type   | possible_keys               | key                         | key_len | ref             | rows | Extra                                        |
+----+-------------+------------+--------+-----------------------------+-----------------------------+---------+-----------------+------+----------------------------------------------+
|  1 | PRIMARY     | d          | ALL    | NULL                        | NULL                        | NULL    | NULL            |    3 | Using where; Using temporary; Using filesort |
|  1 | PRIMARY     | <derived2> | ALL    | NULL                        | NULL                        | NULL    | NULL            |    3 |                                              |
|  1 | PRIMARY     | h          | eq_ref | PRIMARY                     | PRIMARY                     | 4       | dc.hub_id       |    1 |                                              |
|  2 | DERIVED     | dc1        | ALL    | NULL                        | NULL                        | NULL    | NULL            |    4 | Using filesort                               |
|  2 | DERIVED     | dc2        | ref    | livedata_domcabling_dc592d9 | livedata_domcabling_dc592d9 | 4       | live.dc1.dom_id |    2 | Using where; Not exists                      |
+----+-------------+------------+--------+-----------------------------+-----------------------------+---------+-----------------+------+----------------------------------------------+ 

このクエリを変更して効率を上げるにはどうすればよいですか?

ダミーデータ(以下に提供)を使用すると、これは期待される結果です。

+-----+-------+---------+--------+----------+------------+-----------+---------------------+------+-----------+
| id  | mb_id | prod_id | string | position | name       | cluster   | date_change         | cwd  | hub       |
+-----+-------+---------+--------+----------+------------+-----------+---------------------+------+-----------+
| 249 | 47    | 47      |     47 |       47 | SuperDOM47 | localhost | NULL                | NULL | NULL      |
| 250 | 48    | 48      |     48 |       48 | SuperDOM48 | localhost | 2014-04-16 05:23:00 | 32A  | megahub01 |
| 251 | 49    | 49      |     49 |       49 | SuperDOM49 | localhost | NULL                | 22B  | megahub01 |
+-----+-------+---------+--------+----------+------------+-----------+---------------------+------+-----------+

基本的に、「dom」エントリごとに1行必要です。

  1. date_changeが最も高い「domcabling」レコード
    • レコードが存在しない場合は、nullフィールドが必要です
    • 1つのエントリには、domごとにnullのdate_changeフィールドが含まれる場合があります(他のどの日時よりも古いと見なされるnullの日時フィールド)
  2. 「ハブ」の名前。「domcabling」エントリが見つかった場合はnull、それ以外の場合はnull。

CREATE TABLE +3つのテーブルのダミーINSERT:

livedata_dom(約5000エントリ)

CREATE TABLE `livedata_dom` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `mb_id` varchar(12) NOT NULL,
  `prod_id` varchar(8) NOT NULL,
  `string` int(11) NOT NULL,
  `position` int(11) NOT NULL,
  `name` varchar(30) NOT NULL,
  `cluster` varchar(9) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `mb_id` (`mb_id`),
  UNIQUE KEY `prod_id` (`prod_id`),
  UNIQUE KEY `name` (`name`),
  UNIQUE KEY `livedata_domgood_string_7bff074107b0e5a0_uniq` (`string`,`position`,`cluster`)
) ENGINE=InnoDB AUTO_INCREMENT=5485 DEFAULT CHARSET=latin1;

INSERT INTO `livedata_dom` VALUES (251,'49','49',49,49,'SuperDOM49','localhost'),(250,'48','48',48,48,'SuperDOM48','localhost'),(249,'47','47',47,47,'SuperDOM47','localhost');

livedata_domcabling(約10000エントリ、ゆっくりと成長)

CREATE TABLE `livedata_domcabling` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `dom_id` int(11) NOT NULL,
  `hub_id` int(11) NOT NULL,
  `cwd` varchar(3) NOT NULL,
  `date_change` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `livedata_domcabling_dc592d9` (`dom_id`),
  KEY `livedata_domcabling_4366aa6e` (`hub_id`),
  CONSTRAINT `dom_id_refs_id_73e89ce0c50bf0a6` FOREIGN KEY (`dom_id`) REFERENCES `livedata_dom` (`id`),
  CONSTRAINT `hub_id_refs_id_179c89d8bfd74cdf` FOREIGN KEY (`hub_id`) REFERENCES `livedata_hub` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5397 DEFAULT CHARSET=latin1;

INSERT INTO `livedata_domcabling` VALUES (1,251,1,'22B',NULL),(2,250,1,'33A',NULL),(6,250,1,'32A','2014-04-16 05:23:00'),(5,250,1,'22B','2013-05-22 00:00:00');

livedata_hub(約100エントリ)

CREATE TABLE `livedata_hub` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(14) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=98 DEFAULT CHARSET=latin;

INSERT INTO `livedata_hub` VALUES (1,'megahub01');
4

2 に答える 2

3

この書き直しを試してください(SQL-Fiddleでテスト済み:

SELECT 
    d.*, dc.date_change, dc.cwd, h.name as hub
FROM 
    livedata_dom AS d
  LEFT JOIN 
    livedata_domcabling as dc
        ON dc.id =
           ( SELECT id
             FROM livedata_domcabling AS dcc
             WHERE dcc.dom_id = d.id 
             ORDER BY date_change DESC 
               LIMIT 1
          ) 
  LEFT JOIN 
    livedata_hub AS h 
        ON dc.hub_id = h.id
  WHERE 
     d.cluster = 'localhost' ;

そして、インデックスを(dom_id, date_change)付けると効率が向上します。

d.cluster = 'localhost'(テーブルの行数がlivedata_domこの条件に一致するかどうか)の選択性についてはわかりませんが、インデックスを追加すること(cluster)も役立つ場合があります。

于 2012-10-19T22:01:04.300 に答える
1
set @rn := 0, @dom_id := 0;
select d.*, dc.date_change, dc.cwd, h.name as hub
from
    livedata_dom d
    left join (
        select
            hub_id, date_change, cwd, dom_id,
            if(@dom_id = dom_id, @rn := @rn + 1, @rn := 1) as rn,
            @dom_id := dom_id as dm_id
        from
            livedata_domcabling
        order by dom_id, date_change desc
    ) dc on d.id = dc.dom_id
    left join
    livedata_hub h on h.id = dc.hub_id
where rn = 1 or rn is null
order by dom_id

投稿したデータにはdom_id249がありません。また、#250にはnullの日付が1つあるため、最初に表示されます。したがって、あなたの結果は、あなたの質問から私が理解していることを反映していません。

于 2012-10-19T21:38:48.347 に答える