sql >> データベース >  >> RDS >> Mysql

結合されたテーブルのフィールドで結果をグループ化するクエリを最適化します

    問題は、 nameによるグループ化です。 sales_idを失うことになります したがって、MySQLは一時テーブルを使用するように強制されます。

    これは最もクリーンなソリューションではなく、私のあまり好きではないアプローチの1つですが、両方に新しいインデックスを追加できます。 名前 およびsales_id 次のような列:

    ALTER TABLE `yourdb`.`ycs_products` 
    ADD INDEX `name_sales_id_idx` (`name` ASC, `sales_id` ASC);
    

    および force indexのいずれかでこのインデックスを使用するためのクエリ またはuseindex

    SELECT SQL_NO_CACHE p.name, COUNT(1) FROM ycs_sales s
    INNER JOIN ycs_products p use index(name_sales_id_idx) ON s.id = p.sales_id 
    WHERE s.dtm BETWEEN '2018-02-16 00:00:00' AND  '2018-02-22 23:59:59'
    GROUP BY p.name;
    

    私の実行では、テーブルpでは「usingwhere; using index」、テーブルsでは「usingwhere」のみが報告されました。

    とにかく、この2つのテーブルのより良い設計が見つかる可能性があるため、スキーマについて再考することを強くお勧めします。一方、これがアプリケーションの重要な部分でない場合は、「強制」インデックスを処理できます。

    編集

    問題が設計にあることは明らかなので、多対多の関係を描くことをお勧めします。テスト環境で検証する機会があれば、次のようにします。

    1)製品の名前とIDを保存するためだけに一時テーブルを作成します:

    create temporary table tmp_prods
    select min(id) id, name
    from ycs_products
    group by name;
    

    2)一時テーブルから始めて、salesテーブルを結合し、 ycs_productの代わりを作成します :

    create table ycs_products_new
    select * from tmp_prods;
    
    ALTER TABLE `poc`.`ycs_products_new` 
    CHANGE COLUMN `id` `id` INT(11) NOT NULL ,
    ADD PRIMARY KEY (`id`);
    

    3)結合テーブルを作成します:

    CREATE TABLE `prod_sale` (
    `prod_id` INT(11) NOT NULL,
    `sale_id` INT(11) NOT NULL,
    PRIMARY KEY (`prod_id`, `sale_id`),
    INDEX `sale_fk_idx` (`sale_id` ASC),
    CONSTRAINT `prod_fk`
      FOREIGN KEY (`prod_id`)
      REFERENCES ycs_products_new (`id`)
      ON DELETE NO ACTION
      ON UPDATE NO ACTION,
    CONSTRAINT `sale_fk`
      FOREIGN KEY (`sale_id`)
      REFERENCES ycs_sales (`id`)
      ON DELETE NO ACTION
      ON UPDATE NO ACTION);
    

    既存の値を入力します:

    insert into prod_sale (prod_id, sale_id)
    select tmp_prods.id, sales_id from ycs_sales s
    inner join ycs_products p
    on p.sales_id=s.id
    inner join tmp_prods on tmp_prods.name=p.name;
    

    最後に、結合クエリ:

    select name, count(name) from ycs_products_new p
    inner join prod_sale ps on ps.prod_id=p.id
    inner join ycs_sales s on s.id=ps.sale_id 
    WHERE s.dtm BETWEEN '2018-02-16 00:00:00' AND  '2018-02-22 23:59:59'
    group by p.id;
    

    group byは、名前ではなく主キーにあることに注意してください。

    出力の説明:

    explain select name, count(name) from ycs_products_new p inner join prod_sale ps on ps.prod_id=p.id inner join ycs_sales s on s.id=ps.sale_id  WHERE s.dtm BETWEEN '2018-02-16 00:00:00' AND  '2018-02-22 23:59:59' group by p.id;
    +------+-------------+-------+--------+---------------------+---------+---------+-----------------+------+-------------+
    | id   | select_type | table | type   | possible_keys       | key     | key_len | ref             | rows | Extra       |
    +------+-------------+-------+--------+---------------------+---------+---------+-----------------+------+-------------+
    |    1 | SIMPLE      | p     | index  | PRIMARY             | PRIMARY | 4       | NULL            |    3 |             |
    |    1 | SIMPLE      | ps    | ref    | PRIMARY,sale_fk_idx | PRIMARY | 4       | test.p.id       |    1 | Using index |
    |    1 | SIMPLE      | s     | eq_ref | PRIMARY,dtm         | PRIMARY | 4       | test.ps.sale_id |    1 | Using where |
    +------+-------------+-------+--------+---------------------+---------+---------+-----------------+------+-------------+
    


    1. Symfony2:国ごとに都市を一覧表示

    2. Java + Tomcat、データベース接続が停止していますか?

    3. プリペアドステートメントの構文エラーが発生するのはなぜですか?

    4. MySQL、空の値に相当するものを合体させますか?