質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Elasticsearch

Elasticsearchは、クラウド向けに構築された、RESTful な API を提供する分散型のサーチエンジンアプリケーションです。

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

Redis

Redisは、オープンソースのkey-valueデータストアで、NoSQLに分類されます。すべてのデータをメモリ上に保存するため、処理が極めて高速です。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

1回答

1606閲覧

一覧検索と分類ごとの件数取得を効率的に行いたい

love_engineerin

総合スコア19

Elasticsearch

Elasticsearchは、クラウド向けに構築された、RESTful な API を提供する分散型のサーチエンジンアプリケーションです。

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

Redis

Redisは、オープンソースのkey-valueデータストアで、NoSQLに分類されます。すべてのデータをメモリ上に保存するため、処理が極めて高速です。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

1クリップ

投稿2018/12/06 02:21

編集2018/12/06 02:36

###実現したいこと
まずは画面UI上で実現したいことを説明します。

一覧画面で一覧検索と合わせて、サイドバーに件数を表示したいです。

例えばサイドバーには国と承認済み・未承認タグ、およびそれぞれの件数が表示されているとします。
(□はチェックボックス)
ーーーーー
□日本 10件
□米国 4件
□中国 4件

□承認済み 14件
□未承認 4件
ーーーーー

日本をチェックすると以下の通り、
承認済み、未承認は日本に絞られた件数で再検索されます。(一覧も再検索されます。)
ーーーーー
■日本 10件 ←ここをチェックすると
□米国 4件
□中国 4件

□承認済み 7件 ←件数が変わる(日本10件のうち承認済みが7件と言う意味)
□未承認 3件  ←件数が変わる(日本10件のうち未承認が3件と言う意味)
ーーーーー

###準備したもの
取得元のテーブルは以下の通りです。
・一覧テーブル

id承認フラグその他一覧情報
日本承認済み
米国未承認

###現状の実現方法
上記を実現するために以下のSQL実行しています。

sql

1-- 一覧を表示するためのSQL 2select 3 その他一覧情報 4from 5 一覧テーブル 6where 7in (チェックされた国) and 8 承認フラグ in (チェックされた承認フラグ) and 9 (その他一覧情報の絞り込み)

sql

1-- 国の件数を表示するためのSQL 2select 3 国,count(*) 4from 5 一覧テーブル 6where 7 承認フラグ in (チェックされた承認フラグ) and 8 (その他一覧情報の絞り込み) 9group by 10

sql

1-- 承認フラグの件数を表示するためのSQL 2select 3 承認フラグ,count(*) 4from 5 一覧テーブル 6where 7in (チェックされた国) and 8 (その他一覧情報の絞り込み) 9group by 10 承認フラグ

###現状の実現方法の課題
実際には上記以上にサイドバーのタグが複雑であること、データ量が多いことが原因で、
性能的にかなり問題が出ています。

###試したこと

  • 1つのSQLにまとめる

→思いつきませんでした。

  • 件数テーブルを別で準備

→その他一覧情報の条件ごとに持つ必要があり、その中には全文検索も含まれるので不可

  • マルチスレッドなど並列処理

→PHPのため、困難と判断

###質問
上記実現したいことを性能問題を回避して実現するためにはどのようなアプローチがありますか。
過去の事例や世の中にあるプロダクトでどのように扱っているかアドバイスいただけないでしょうか。
方針レベルでも思いつく点があれば教えていただけますと幸いです。

最後に自分で思いつくところを列挙します。

  • ElasticsearchやRedisの利用

 →実現できるか不明

  • Ajaxで件数を非同期取得

 →根本解決にはならない気がしている

###環境

  • MySQL5.7
  • PHP7.2

※上記、現行環境ですが、あまりこだわりません。

よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

■日本 10件 ←ここをチェックすると

□米国 5件
□中国 5件

□承認済み 7件 ←件数が変わる

□未承認 3件  ←件数が変わる

上記、もし未チェック分を表示する処理ならチェックしたもの以外を検索するので

SQL

1select カテゴリ,count(*) from tbl where notin('日本')

のような処理になります

sample

  • 送り側

※今回は面倒なのでjQueryで

javascript

1<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script> 2<script> 3$(function(){ 4 $('[name="country[]"]').on('change',function(){ 5 $.ajax({ 6 url:"recv.php", 7 data:$('[name="country[]"]:checked').map(function(){ 8 return "country[]="+encodeURIComponent($(this).val()); 9 }).get().join("&"), 10 }).done(function(data){ 11 console.log(data); 12 /* 実際にはここで承認済み、非承認の件数を書き換える */ 13 }); 14 }); 15}); 16</script> 17 18<div><label><input type="checkbox" name="country[]" value="日本">日本</label>10</div> 19<div><label><input type="checkbox" name="country[]" value="米国">米国</label>4</div> 20<div><label><input type="checkbox" name="country[]" value="中国">中国</label>4</div> 21 22<ul> 23<li>承認済み:<span id="shouninzumi">0</span>24<li>未承認:<span id="mishounin">0</span>25</ul>
  • 受け側(recv.php)

PHP

1<?PHP 2$country=filter_input(INPUT_GET,"country", FILTER_DEFAULT , FILTER_REQUIRE_ARRAY); 3$sql ="select カテゴリ,count(*) as 件数from tbl where 1 "; 4$data=[]; 5if(count((array) $country)>0){ 6 $sql.="and 国 in (".implode(",",array_fill(0,count($country),"?")).")"; 7 $data=$country; 8} 9$sql.=" group by カテゴリ"; 10print $sql.";\n"; 11print_r($data); 12/* 実際には$sqlをprepareで処理してカテゴリごとの件数をjsonで返すのがベター */

DB設計について

集計のロジックを変えます
テーブルはこんな感じになります

SQL

1create table tbl(id int primary key,varchar(10),承認 varchar(10),その他 varchar(10)); 2insert into tbl values 3( 1,'日本','承認済み','その他1'), 4( 2,'日本','承認済み','その他1'), 5( 3,'日本','承認済み','その他2'), 6( 4,'日本','承認済み','その他2'), 7( 5,'日本','承認済み','その他2'), 8( 6,'日本','承認済み','その他2'), 9( 7,'日本','承認済み','その他3'), 10( 8,'日本','未承認','その他1'), 11( 9,'日本','未承認','その他2'), 12(10,'日本','未承認','その他2'), 13(11,'米国','承認済み','その他1'), 14(12,'米国','承認済み','その他1'), 15(13,'米国','承認済み','その他1'), 16(14,'米国','未承認','その他3'), 17(15,'中国','承認済み','その他1'), 18(16,'中国','承認済み','その他2'), 19(17,'中国','承認済み','その他3'), 20(18,'中国','承認済み','その他2');
  • 絞り込みなし

SQL

1select '国' as カテゴリ,as 要素,sum(1) as 件数 from tbl group by2union all select '承認' as カテゴリ,承認 as 要素,sum(1) as 件数 from tbl group by 承認 3union all select 'その他' as カテゴリ,その他 as 要素,sum(1) as 件数 from tbl group by その他
  • 国が'日本'

国の集計以外の件数集計にor条件を追加します

SQL

1select '国' as カテゴリ,as 要素,sum(1) as 件数 from tbl group by2union all select '承認' as カテゴリ,承認 as 要素,sum(1 and (0 orin('日本'))) as 件数 from tbl group by 承認 3union all select 'その他' as カテゴリ,その他 as 要素,sum(1 and (0 orin('日本'))) as 件数 from tbl group by その他 4
  • 国が'日本'または承認が'未承認'

SQL

1select '国' as カテゴリ,as 要素,sum(1 and (0 or 承認 in('未承認'))) as 件数 from tbl group by2union all select '承認' as カテゴリ,承認 as 要素,sum(1 and (0 orin('日本'))) as 件数 from tbl group by 承認 3union all select 'その他' as カテゴリ,その他 as 要素,sum(1 and (0 orin('日本') or 承認 in('未承認'))) as 件数 from tbl group by その他

これを更に集計するとこう

SQL

1select カテゴリ,group_concat(concat(要素,":",件数)) as 要素件数 from( 2select '国' as カテゴリ,as 要素,sum(1 and (0 or 承認 in('未承認'))) as 件数 from tbl group by3union all select '承認' as カテゴリ,承認 as 要素,sum(1 and (0 orin('日本'))) as 件数 from tbl group by 承認 4union all select 'その他' as カテゴリ,その他 as 要素,sum(1 and (0 orin('日本') or 承認 in('未承認'))) as 件数 from tbl group by その他 5) as sub 6group by カテゴリ
  • 結果:
カテゴリ要素件数
その他その他2:6,その他3:2,その他1:3
中国:0,日本:3,米国:1
承認承認済み:7,未承認:3

これを受けてjsonを返してやればよいでしょう

※注意
where句で絞り込みをすると0件のデータが表示されなくなります。
0と表示したいなら今回のようなsumで集計、
表示しなくてもいいなら(つまりjs側で例外処理が必要)where句で絞り込みをしてください

投稿2018/12/06 02:29

編集2018/12/06 06:50
yambejp

総合スコア114843

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

love_engineerin

2018/12/06 02:33

ご回答ありがとうございます。 質問が悪かったので、すぐに直しますが、 欲しいのは、チェックされたものの件数です。 「日本」のうち「承認済み」が7件、「未承認」が3件と言う意味です。 というのと、こういったSQLをカテゴリごとに都度発行するのが性能劣化につながっている認識です。
yambejp

2018/12/06 03:05

それはロジックがおかしい。 チェック済みを表示したいなら最初はすべてにチェックをいれるべき
love_engineerin

2018/12/06 03:15

はい、厳密にはその通りです。 ただUI上,ユーザが絞り込みやすいように、初期表示はチェックを全て外しています。 チェックが全て外れている場合は全チェックと同じ扱いとするようにロジックを組んでいます。 今回の質問の意図と異なるところで混乱させてしまいすみません。
yambejp

2018/12/06 03:41

一応指定された仕様どおりの流れを書いておきました ユーザーが混乱しないように十分配慮してください
love_engineerin

2018/12/06 04:45

詳細な記載ありがとうございます。 今まさに記載いただいた処理の流れで作っております。 (なお承認済み,未承認についてもチェックボックスのイメージです。) 一方でこの方法だと現状で3本のSQLが必要(本文記載の通り)ですし、 実際には「地域」「承認フラグ」以外のカテゴリもあるため、SQLが増えてしまいます。 このロジックではない方法で実現できないか教えていただけると助かります。 それが上記「質問」の意図でした。
yambejp

2018/12/06 04:51

> 現状で3本のSQLが必要 多分考え方がおかしいか、仕様を正しく提示していないかだと思います 集計単位がちがうのですから、集計単位ごとにSQLを発行するしかありません
yambejp

2018/12/06 05:48

DBの設計から見直しました、考え方を精査してみて下さい
love_engineerin

2018/12/06 06:47

詳細にありがとうございます。大変参考になります。 いただいたSQLで実現可能なのですが、unionするのと複数回sqlを発行するのは、あまり性能差がないような気がします。 より具体的な性能の懸念点を説明させて下さい。 上記tblにcontentsというtext型のカラムがあると仮定します。 それをlike検索しようとすると ``` select '国' as カテゴリ,国 as 要素,sum(1) as 件数 from tbl where contents like '%あいうえお%' group by 国 union all select '承認' as カテゴリ,承認 as 要素,sum(1) as 件数 from tbl where contents like '%あいうえお%' group by 承認 union all select 'その他' as カテゴリ,その他 as 要素,sum(1) as 件数 from tbl where contents like '%あいうえお%' group by その他 ``` となると思います。 そうすると一番コストがかかるであろうlike検索を同じ内容で3回走らせないといけません。 ですので、できるだけ1回で発行したいと考えております。
yambejp

2018/12/06 06:52

なんどもいうようで申し訳ないですが、ロジックがおかしいので実現は基本的に不可能ですし そもそもlikeで前方後方一致を書いている時点でスピード云々はあきらめてください unionしているのはスピードのためではなく集計を一括するためですので 当然高速化はできません
love_engineerin

2018/12/06 07:00 編集

ロジックについては、初期表示時に全部チェックされている仕様と考えていただいて差し支えありません。 likeについては、便宜的にそのようにお伝えしましたが実際は性能に配慮してngramによる全文検索です。 (説明が不足しており申し訳ございません。) SQLでの速度改善が難しいとわかったのでその点はよかったです。 ありがとうございます。 別のアプローチがあれば良いのですが、、
yambejp

2018/12/06 07:09

たとえば、国=日本としたときに、米国、中国を0にしていいならやりようはありますよ でもそうじゃないのですよね? そうなると集計単位も検索条件もまとめようがないのできびしいです。
love_engineerin

2018/12/06 08:05

国を日本にしたタイミングでは米国,中国を0とするのは良いです。 その場合は米国,中国の件数を変えないようにjqueryなどで制御すれば良いので。 ただ国に日本が選ばれている状態で、承認のチェックが変更された場合は、 承認の状態に合わせて米国,中国の件数も変更したいです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問