• ベストアンサー
  • 困ってます

クエリの遅さの原因

下記のクエリーをそれぞれ試してみたところ、圧倒的に下の方が遅くなってしまいました。 $rs = mysql_query("select * from A INNER JOIN B ON B.cat = A.id order by B.id desc LIMIT 1, 10 ;",$con); $rs = mysql_query("select * from A INNER JOIN B ON B.cat = A.id where B.name is not null group by B.area order by B.id desc LIMIT 1, 10 ;",$con); where B.name is not null group by B.area この処理はそれほど負荷が掛かってしまうのでしょうか。 他に良い書き方(方法)がありましたら教えてください。

共感・応援の気持ちを伝えよう!

  • MySQL
  • 回答数3
  • 閲覧数159
  • ありがとう数9

質問者が選んだベストアンサー

  • ベストアンサー
  • 回答No.1

データが何件あるのか分かりませんが、「B.name is not null」という検索条件があり、さらに「group by B.area」でソートが行われる訳ですから、普通に考えて後者の方が「重い」でしょう。 それ以前の問題として、後者のSQLは「間違った使い方」をしていると思います。 標準SQLや主要なRDBMSでは、group by指定時、selectの選択リストで指定できるのは、 (1)group byで指定した列 (2)sumやcount、maxなどの集合(集計)関数 (3)定数 だけです。 MySQL以外のRDBMSでは、文法エラーになります。 しかし、MySQLはこの制限に拡張仕様を持っていて、「group byにより結果が一意になるなら、その列を指定してもよい。しかし、一意にならないなら結果は保証しない」としています。 <例> select c1,max(c2),c3 from t1 group by c1 c3列を指定することは、標準SQLに反する。また、主要なRDBMSではgroup byとselectの選択リストの指定の矛盾でエラーになる。 MySQLでは、c1でグループ化することで、c3も一意になるなら指定してよい。そうでないなら、結果は保証されない。 (エラーにならないので注意が必要) >他に良い書き方(方法)がありましたら教えてください。 何をやりたいのか具体的な説明なしに、アドバイスのしようがありません。また、MySQLのバージョンも書きましょう。

共感・感謝の気持ちを伝えよう!

質問者からの補足

SQLの間違った使い方をご指摘して頂いてありがとうございました。 まったく知りませんでした。 バージョンはMySQL3.23です。 やりたいことは、A・Bテーブルを結合して、Bのareaカラムの重複データを除外した上で、A・Bテーブルから全てのデータを取得したいと思っています。 こういうことは可能でしょうか?

その他の回答 (2)

  • 回答No.3
  • yambejp
  • ベストアンサー率51% (3827/7415)

>A・Bテーブルを結合して、Bのareaカラムの重複データを除外した上で、 >A・Bテーブルから全てのデータを取得した もしそうであればSQLの書き方が違いますね Bのareaに重複データは最初からあるのですよね? areaが重複する場合、その他のフィールド値は同じなのでしょうか? 違う場合はどのデータが抽出されることを期待しているのでしょうか? やり方としてはまずBのareaを一元化してからそのデータをもとに AにJOINするということになります また、正規化されているかどうかもわかりづらいので、 A.idとB.catは1対1なのかn対nのなのかそのあたりも踏まえた質問が 望ましいです

共感・感謝の気持ちを伝えよう!

質問者からのお礼

はい、Bのareaに重複データがありまして、その他のフィールド値は異なります。 また抽出したいデータはAとBの全データになります。 >Bのareaを一元化してからそのデータをもとにAにJOINするということになります これを実際にSQLで記述(?)するとどのようになるのでしょうか? A.idとB.catは1対1になります。 正規化という意味も分からない知識レベルなので質問に必要な情報が欠けていて申し訳ありません。

  • 回答No.2
  • yambejp
  • ベストアンサー率51% (3827/7415)

ちょっとめんどうなので実証していませんが AにBをINNER JOIN しているのにB.nameのnullをwhere句で検証しているのは なぜ? 通常AにBをLEFT JOINするとBにnullがのこる可能背はあるとおもうけど・・・ もしB.nameに最初からNULLがあるならWhereするまえにINNER JOIN ON の条件にANDでつないでみてはどうでしょうか? それと外部結合したテーブルの項目でgroup by するのは非効率のような 気が・・・設計段階の問題があるかも

共感・感謝の気持ちを伝えよう!

質問者からのお礼

私の知識のなさに加えて、おっしゃるとおり、設計段階の問題が大きいみたいです。どうもありがとうございました。

関連するQ&A

  • phpとmysqlでデータの一覧表示をしたいと思っています。

    phpとmysqlでデータの一覧表示をしたいと思っています。 ずらーっと並べるだけでなく、1ページに100件した場合には 10行毎に<hr>や<br>などのタグを挿入して間隔を空けたいのですが どのようにしたらいいのか悩んでいます。 現在は下記のようにLIMITで何度もqueryを発行しています。 できれば1回のqueryで処理したいのですが 他にどのような方法があるでしょうか。 $rs = mysql_query("select * from data order by id desc LIMIT 0,10 ;",$conn); while($rec = mysql_fetch_array($rs, MYSQL_ASSOC)){ $site = $rec['site']; $url = $rec['url']; echo '<a href="'.$url.'">'.$site.'</a>'; } $rs = mysql_query("select * from data order by id desc LIMIT 10,10 ;",$conn); while($rec = mysql_fetch_array($rs, MYSQL_ASSOC)){ $site = $rec['site']; $url = $rec['url']; echo '<a href="'.$url.'">'.$site.'</a>'; } $rs = mysql_query("select * from data order by id desc LIMIT 20,10 ;",$conn); while($rec = mysql_fetch_array($rs, MYSQL_ASSOC)){ $site = $rec['site']; $url = $rec['url']; echo '<a href="'.$url.'">'.$site.'</a>'; } よろしくお願い致します。

    • ベストアンサー
    • MySQL
  • クエリが数秒かかる

    こんにちは、データベース初心者です。 ―――― id | tag ―――― 1 | 1002 1 | 1004 1 | 4005 2 | 3001 というような、IDに複数の検索用タグが付いているテーブルに対して、 select TB2.id from tb as TB1 inner join tb as TB2 on TB2.id=TB1.id where TB1.tag=1002 and TB2.tag=1004 limit X,Y; というような、任意の数(例は2個)の指定したtagを全て含むIDを検索するクエリがあります。 (指定が3個の場合は単純にinner join を増やしています) で、指定のtagが1つの場合は一瞬で返されるのですが、 複数指定の場合に何秒もかかるときがありまして、 一瞬で返させるには一体どこを直せばよいのかがわからないのです。 クエリを変えるのか、テーブル構成を変えるのか、マシンを変えなきゃ無理なのか、 こういった場合のセオリーなど、ご指導願いたいのです。 2つ目の質問として、 order by が無い場合というのは、データに大きな変更が無い期間中は、 同じクエリであれば同じ順序で返ってくると考えて良いのでしょうか? というのも、上記クエリにorder byを付けたかったのですが、 ソート項目が多数あり断念しまして、ただページング機能は付けたく思いまして。 環境は、ローカルで、winXP、MySQL 5.1、PHP経由で、ブラウザから操作・表示しています。 id数が約1000万、tagは500種ほどあり、1idに対して1~30個ほどで平均8個くらい付いており、 テーブルのレコード数は約8000万となっています。 素人質問で恐縮ですがよろしくお願いします。

  • mysqlのスマートな書き方

    MySQL5.1とPHP5を使っています。 phpからmysqlのtestテーブルにアクセスして点数順に生徒名一覧を作成したいと思っています。 イメージとしてはこのような感じです。○○○は名前になります。 90点以上 ○○○ ○○○ 80点以上 ○○○ ○○○ ○○○ 70点以上 ○○○ ○○○ ○○○ ○○○ このような一覧を表示するため、下記のように作成したところ上記のような表示をしてくれました。 しかしながら、同じmysql_queryを何度も使っていますし、DBに負担が掛かりそうな気がします(本当に負荷があるのかは分かりませんが・・・) もっとスマートな書き方はないものでしょうか。 よろしくお願い致します。 echo "90点以上"; $rs = mysql_query("select * from test where kamoku = '1' order by tensu desc LIMIT 0, 100;",$conn); while($res = mysql_fetch_array($rs, MYSQL_ASSOC)){ if ($res['tensu'] >= 90){ echo $res['name']; echo "<br>" ; } } echo "80点以上~90点未満"; $rs = mysql_query("select * from test where kamoku = '1' order by tensu desc LIMIT 0, 100;",$conn); while($res = mysql_fetch_array($rs, MYSQL_ASSOC)){ if($res['tensu'] >= 80 and $res['tensu'] < 90 ){ echo $res['name']; echo "<br>" ; } } echo "70点以上~80点未満"; $rs = mysql_query("select * from test where kamoku = '1' order by tensu desc LIMIT 0, 100;",$conn); while($res = mysql_fetch_array($rs, MYSQL_ASSOC)){ if($res['tensu'] >= 70 and $res['tensu'] < 80 ){ echo $res['name']; echo "<br>" ; } }

    • ベストアンサー
    • MySQL
  • 複数JOINしているとCOUNTが正しく取得できな

    LAMP環境で開発をしています。 SQL文でCOUNTを求める際に、まとめて結果を求めようとして上手く行きません。 状況としては以下です。 テーブルdはidをkeyにa,b,c3つのテーブルとjoinしています。 id = 1の場合、テーブルa,b,cにマッチするレコードがそれぞれに4個、1個、0個あります。 ひとつひとつを SELECT COUNT(CASE WHEN a.name IS null THEN 1 ELSE null END) as a_count FROM d INNER JOIN a ON a.id = d.id WHERE d.id = 1 として結果を求めると4,1,0と出るのですが、まとめて SELECT COUNT(CASE WHEN a.name IS null THEN 1 ELSE null END) as a_count, COUNT(CASE WHEN b.name IS null THEN 1 ELSE null END) as b_count, COUNT(CASE WHEN c.name IS null THEN 1 ELSE null END) as c_count FROM d INNER JOIN a ON a.id = d.id INNER JOIN b ON b.id = d.id INNER JOIN c ON c.id = d.id WHERE d.id = 1 とすると28,5,0という値が返されます。 どのように書けば正しい4,1,0を得られるのでしょうか? よろしくお願いします。

    • ベストアンサー
    • MySQL
  • viewでfrom句にサブクエリが書けない

    こんにちは。MYSQLのVIEWの代替についてお教え下さい。 下記でinner joinについて教えて頂きました。 http://okwave.jp/qa/q7296711.html 環境はWin 7 MYQL5です。 t1テーブルのデータ ID,在庫 001,22 t2テーブルのデータ ID,出庫 001,17 001,3 SELECT A.z AS `在庫`,B.* FROM (SELECT SUM(stock) AS z FROM t1 WHERE id='001' GROUP BY id) A INNER JOIN t2 B ON A.id=B.id というコードで、それぞれのテーブルをグループ化してグループ化したもの同士をJOINしました。 このコードを元にVIEWを作り、そのVIEWを対象にクエリを作ろうと考えていました。 しかしながら、MYSQLのVIEWではFROM句でサブクエリは使えませんでした。 http://www.klab.jp/media/mysql/index4.html 上記のコードを書きなおして、なんとしてもVIEWで利用したいのですが、FROM句でサブクエリを使わずにVIEWを作成する方法がわかりません。 どのようにリライトすればよいか、ご助言お願い致します。

    • ベストアンサー
    • MySQL
  • 削除クエリについて

    ACCESS2000で開発しています。 現在LOOPで A_TBL と B_TBL を日付とコードでリンクさせ同じデータがあれば削除するという処理をしています。 件数が多いためこれでは時間がかかるため、一括で削除できる削除クエリができないかと考えています。 どなたかご教授お願いします。 <現在のプログラム> SQL="SELECT 年月, コード, 日付 FROM A_TBL AS A INNER JOIN B_TBL AS B ON (A.日付 = B.日付) AND (A.コード = B.コード) WHERE (A.年月='200707')" Set RS = CurrentDb.OpenRecordset(SQL, dbOpenSnapshot) Do While Not RS.EOF SQL= "DELETE * FROM A_TBL WHERE (年月='200707') AND (コード='" & RS!コード & "') AND (日付='" & RS!日付 & "')" CurrentDb.Execute SQL RS.MoveNext Loop RS.Close Set RS = Nothing

  • SQLエラー

    検索システムの検索結果の分割をしようとしています。 しかしどうしてもSQL文にエラーがでてしまいます。 SELECT * FROM shops where (area='◯◯◯') ORDER BY id DESC limit0, 10 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'limit0, 10' at line 1 該当するSQL文は以下になります。 $query = "SELECT * FROM shops"; $where = array(); if (isset($_GET['area'])and($_GET['area'] !== '')) { $where[] = sprintf("(area='%s')", mysql_real_escape_string($_GET['area'])); } if (count($where) <> 0) { $query .= ' where ' . implode(' and ', $where) . ' ORDER BY id DESC '; }   $query .= "limit" . $page_num*10 . ", 10" ; limit句の使い方等間違いはないのかなと思うのですが、いかがでしょうか? アドバイスご教示いただけないでしょうか。 よろしくお願いします。

    • ベストアンサー
    • PHP
  • EXPLAINのUsing filesortについて

    explain select * from address where area =1 order by id desc limit 10,1\G 上記クエリーをexplainで確認すると Extra: Using where; Using filesort が表示されてしまいます。 1. Using filesortを消したいのですが、idを降順で表示するのに order by id desc 以外の方法はありますか?このような場合、Using filesortは仕方ないのでしょうか? 2. Using whereは効率の悪いクエリーの要素になるのでしょうか?

    • ベストアンサー
    • MySQL
  • プリペアードステートメントへの変更

    PHP、MySQL初級者です。 ある商品を検索するサイトを運営していますが、SQLインジェクション対策がなされておらず、mysql_queryで書いていたものを、プリペアドステートメントに初挑戦しています。 元々作ったプログラムを、ネットの情報や本を参考に修正していますが、検索をすると画面が真っ白になってしまう現象から2日間抜け出せずにいます。 どなたかご教授いただけますでしょうか。PHPのバージョンは5.2、Windows環境です。 ちなみに、$display_fromはページネーションに関係するものです。 また、//でコメントアウトしている箇所は一旦PEAR::DBをつかうやりかたに修正したなごりです。 また、rs2のところは検索結果のヒット数を表示させるためにわざわざ全件を取得しています。(このやり方自体スマートではない感じがしていますが。。) /*POSTでkeywordを取得*/ if($_POST['keyword']!=""){ $keyword=$_POST['keyword']; } elseif($_GET['pageID']!=""){ $keyword=$_SESSION['keyword']; } else{ } //ログイン情報をインクルードする include('db_login.php'); //PEAR DBの機能が含まれるDB.phpもインクルードする require_once("DB.php"); //接続する $connection = DB::connect("mysql://$db_username:$db_password@$db_host/$db_database"); if(DB::isError($connection)){ die("Could not connect to the database: <br />".DB::errorMessage($connection)); } //$db = mysql_connect("localhost", "root", "root")or die ('DB Connection Error= '. mysql_error()); //mysql_select_db("book_db", $db)or die ('DB Connection Error= '.mysql_error()); //$rs = mysql_query("SET character set UTF8", $db); //$rs2 = mysql_query("SET character set UTF8", $db); /*keywordが空白だった場合*/ if($keyword==""){ $query="SET character set UTF8"; $rs=$connection->query($query); $query="select * from book_card order by point desc limit $display_from, 50"; $rs=$connection->query($query); if(DB::isError($rs)){ die("Could not query the database:<br />$query ".DB::errorMessage($rs)); } $query2="SET character set UTF8"; $rs2=$connection->query($query2); $query2="select * from book_card"; $rs2=$connection->query($query2); if(DB::isError($rs2)){ die("Could not query the database:<br />$query2 ".DB::errorMessage($rs2)); } /*もともとのコード $rs=mysql_query("select * from book_card order by point desc limit $display_from, 50", $db); $rs2=mysql_query("select * from book_card", $db); */ } /*keywordに何か入っていた場合*/ else{ $query="SET character set UTF8"; $rs=$connection->query($query); //$query="select * from book_card where Name like '%$keyword%' order by point desc limit $display_from, 50"; //$rs=$connection->query($query); $query = "select * from book_card where Name like ? order by point desc limit ?, 50"; $rs = $connection->prepare($query); $data = array("%$keyword%", $display_from); $connection->execute($rs, $data); $query2="SET character set UTF8"; $rs2=$connection->query($query2); //$query2="select * from book_card where Name like '%$keyword%'"; //$rs2=$connection->query($query2); $query2 = "select * from book_card where Name like ?"; $rs2 = $connection->prepare($query2); $data2 = "%$keyword%"; $connection->execute($rs2, $data2); /*もともとのコード $rs=mysql_query("select * from book_card where Name like '%$keyword%' order by point desc limit $display_from, 50", $db); rs2=mysql_query("select * from book_card where Name like '%$keyword%'", $db); */ } 分かりにくい質問ですみませんが、どなたかお分かりの方ご教授いただけますでしょうか。 何卒宜しくお願い致します。

    • ベストアンサー
    • PHP
  • MySQLで、NULLか空の判定について

    下記のSQL文は、間違っていると思われます。 MySQLをJAVAでコーディングしています。 やりたいことは、 1)food_textが、NULLか、空っぽの時に、 2)limit_dateが最大の 3)レコード1件を抽出したいのです。 SELECT Id, food_id FROM foods   WHERE    (kind_id = 1)  AND (food_text IS NULL) AND (DATALENGTH(food_text) <= 0)  AND (delete_flag = false)  ORDER BY limit_date DESC  LIMIT 1;

    • ベストアンサー
    • MySQL