• 締切済み

集計を行うSQLについて

以下の2テーブルの構成で、from_uid毎のto_uidへの在庫移動個数(val)を集計したいと考えています。 ユーザテーブル(user):uid(int), name(string) 在庫移動管理テーブル(zaiko):id(int), from_uid(int), to_uid(int), val(int) たとえば、それぞれ以下のようなレコードがあった場合、 <user> uid, name 1 aaa 2 bbb 3 ccc <zaiko> id, from_uid, to_uid, val 1 1 2 3 2 1 2 5 3 2 1 1 4 3 1 4 結果として、以下を得たいと考えています。 uid=1で検索した場合 to_uid, val 1 0 2 7 3 -4 uid=2で検索した場合 1 -7 2 0 3 0 uid=3で検索した場合 1 4 2 0 3 0 (イメージとしては、対戦表?の以下のような表を一行ずつ取得する様な感じです。) ___1__2__3 1__0__7_-4 2_-7__0__0 3__4__0__0 上記のような結果を得たい場合、どのようなSQLを書けばよいのでしょうか。 内容について、わかりづらい点等がありましたらご指摘ください。

  • nmtkn
  • お礼率38% (5/13)
  • MySQL
  • 回答数5
  • ありがとう数0

みんなの回答

  • root139
  • ベストアンサー率60% (488/809)
回答No.5

#2の補足です。対戦表風に横に並べたいのなら、下記の様に並べてSELECT句にサブクエリを並べて書けばよいです。 SELECT uid, COALESCE((SELECT SUM(val) FROM zaiko WHERE from_uid = 1 AND to_uid = uid), 0) - COALESCE((SELECT SUM(val) FROM zaiko WHERE to_uid = 1 AND from_uid = uid), 0) AS from_1, COALESCE((SELECT SUM(val) FROM zaiko WHERE from_uid = 2 AND to_uid = uid), 0) - COALESCE((SELECT SUM(val) FROM zaiko WHERE to_uid = 2 AND from_uid = uid), 0) AS from_2, COALESCE((SELECT SUM(val) FROM zaiko WHERE from_uid = 3 AND to_uid = uid), 0) - COALESCE((SELECT SUM(val) FROM zaiko WHERE to_uid = 3 AND from_uid = uid), 0) AS from_3 FROM user ORDER BY uid; (縦横を入替えるには引き算の前と後を入替えます) また、#4さんのSQLは MySQL version 5.5.8 で問題無く動作しました。 下記の様にスカラサブクエリを使っても書けますね。 SELECT u1.uid AS from_uid, u2.uid AS to_uid, COALESCE((SELECT SUM(val) FROM zaiko WHERE from_uid = u1.uid AND to_uid = u2.uid), 0) - COALESCE((SELECT SUM(val) FROM zaiko WHERE to_uid = u1.uid AND from_uid = u2.uid), 0) AS val FROM user u1 INNER JOIN user u2 ORDER BY u1.uid, u2.uid;

  • yamada_g
  • ベストアンサー率68% (258/374)
回答No.4

from,toの全パターンを取得するバージョンです。 件数が多くなるときついと思いますが・・。 SELECT users.from_uid ,users.to_uid ,COALESCE(SUM(CASE WHEN users.from_uid = zaiko.from_uid THEN val ELSE (val * -1) END), 0) AS val FROM ( SELECT u1.uid AS from_uid, u2.uid AS to_uid FROM user u1 CROSS JOIN user u2 ) AS users LEFT JOIN zaiko ON ( (users.from_uid = zaiko.from_uid AND users.to_uid = zaiko.to_uid) OR (users.from_uid = zaiko.to_uid AND users.to_uid = zaiko.from_uid) ) GROUP BY users.from_uid, users.to_uid ORDER BY from_uid, to_uid ; MySQLの環境が無いので、動作確認はできていません。

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

この手の集計が前提であれば、fromとtoをひっくり返したデータを入れ込んでおくと ぐっと集計が楽になります create table user(uid int, name varchar(20)); insert into user values(1,'aaa'),(2,'bbb'),(3,'ccc'); create table zaiko (id int,type int, from_uid int, to_uid int, val int); insert into zaiko values(1,1,1,2,3),(2,1,1,2,5),(3,1,2,1,1),(4,1,3,1,4); insert into zaiko values(1,2,2,1,-3),(2,2,2,1,-5),(3,2,1,2,-1),(4,2,1,3,-4); ※表裏を明確にするためtypeカラムをつけておきましたが実質プラスマイナスだけでも 判断はできます。 これを前提に select from_uid,to_uid,sum(val) as sumvalfrom zaiko group by from_uid,to_uid; とすれば、いかが得られます from_uid to_uid sumval 1 2 7 1 3 -4 2 1 -7 3 1 4 これをuserテーブルとリンクさせると表が簡単につくれます select uid ,sum((to_uid=1)*sub.sumval) as 1 ,sum((to_uid=2)*sub.sumval) as 2 ,sum((to_uid=3)*sub.sumval) as 3 from user left join(select from_uid ,to_uid ,sum(val) as sumval from zaiko group by from_uid,to_uid) sub on user.uid=sub.from_uid group by uid

  • root139
  • ベストアンサー率60% (488/809)
回答No.2

スカラサブクエリを使って、全ユーザに対して在庫の出入りの合計を求めれば良いでしょう。 対象のuidを指定するのが2箇所になってちょっと冗長ですが、下記の様な感じになるかと。 uid=1 の場合 ----------------------------- SELECT uid, COALESCE((SELECT SUM(val) FROM zaiko WHERE from_uid = 1 AND to_uid = uid), 0) - COALESCE((SELECT SUM(val) FROM zaiko WHERE to_uid = 1 AND from_uid = uid), 0) FROM user ORDER BY uid; uid=2 の場合 ----------------------------- SELECT uid, COALESCE((SELECT SUM(val) FROM zaiko WHERE from_uid = 2 AND to_uid = uid), 0) - COALESCE((SELECT SUM(val) FROM zaiko WHERE to_uid = 2 AND from_uid = uid), 0) FROM user ORDER BY uid; ※ MySQL version 5.5.8 で確認

参考URL:
http://gihyo.jp/dev/serial/01/sql_academy2/000402
  • Siegrune
  • ベストアンサー率35% (316/895)
回答No.1

uid=1で検索した場合 to_uid, val 1 0 uid=2で検索した場合 2 0 ・・・ は必ず0のはずですが、それも出したいということですね。 ならば、まず、to_uidを出すためのSQLを考えます。 select distinct to_uid from <zaiko> で求まりますね。 後は単純に、 select to_uid,sum(val from (select to_uid, val from <zaiko> where from_uid = 1 union all select to_uid,0 as val from (select distinct to_uid from <zaiko>) ) group by to_uid order by to_uid で、 uid=1で検索した場合 が求まります。 2・・・も同様。 というのは大嘘で、新たにユーザテーブルに登録されて、from_uidはあれどもto_uidはない場合 漏れます。 select to_uid,sum(val) from (select to_uid, val from <zaiko> where from_uid = 1 union all select uid,0 as val from <user> ) group by to_uid order by to_uid のほうが正しいと思われます。 (ただし、to_uidにないuidはto_uidのほうには出したくないなら最初のほうを使ってください) 次に、uid=1,2,3・・・を横に並べる以下のような結果を求めるのは、難しいです。 ___1__2__3 1__0__7_-4 2_-7__0__0 3__4__0__0 uidに増減がない(または、増減に対してSQLを修正するつもりがあるならば)のならば、 select to_uid,sum(val1),sum(val2),sum(val3),・・・ from (select to_uid, val as val1,0 as val2,0 as val3,・・・ from <zaiko> where from_uid = 1 union all select to_uid, 0 as val1,val as val2,0 as val3,・・・ from <zaiko> where from_uid = 2 union all select to_uid, 0 as val1,0 as val2,val as val3,・・・ from <zaiko> where from_uid = 3 ・・・ union all select uid,0 as val from <user> ) group by to_uid order by to_uid で求めることができますが、通常はプログラム作って対応するほうが簡明かつ柔軟性に富んでいます。 (というよりユーザテーブルの追加・削除でSQLを変えるというはお薦めしません。)

関連するQ&A

  • 二つのテーブルから一文でセレクトするには?

    以下の二つのテーブルがあるとします。 在庫テーブル zaiko_table id | name | zaiko ------------------- 1 |みかん| 1 ------------------- 2 |りんご| 0 ------------------- 3 |とまと| 1 ------------------- 販売テーブル hanbai_table id | day | tenki ------------------- 1 | 3-2 | 晴れ ------------------- 2 | 3-2 | 晴れ ------------------- 1 | 3-1 | 雨 ------------------- 在庫テーブルのIDはその商品のプライマリーキーです zaikoは1なら在庫有り、2なら在庫なしとします。 販売テーブルのIDは在庫テーブルで決めた商品を表しています。 dayは売れた日付、tenkiはその日の天気です。 3月2日に売れた商品でまだ在庫のある物をセレクトしたいと思います。 該当する商品の名前とそれが売れた日の天気が知りたいのです。 これを一回のSQL文の発行で済ませたいときはどの様に書くのでしょうか?

  • 1対多結合で多を絞り込み条件とするSQLについて

    1対多で結合する場合に、多が絞り込み条件となった場合のSQLについての質問です。 たとえばカスタムテーブルを使ったSELECT文などで、このような絞り込み条件が必要になると思います。 まずカスタムテーブルの具体例として、たとえばユーザーテーブルがあったとします。 [user_table] id=INT //オートインクリメント user_id=VARCHAR //adminなどのユーザーID文字列 user_pass=VARCHAR //パスワードを保存※ハッシュ化した値 user_name=VARCHAR //山田太郎などのユーザー名 user_mail=VARCHAR //ユーザーのメールアドレス user_description=TEXT //ユーザーの自己紹介文 user_created=DATETIME //ユーザーの登録日 とりあえず、上記のようなデータをユーザーの基本データだとします。 このユーザーテーブルから、たとえば名前を元に検索するのは単純です。 たとえばこのような感じでしょうか。 SELECT * FROM user_table WHERE user_name = '山田太郎' このテーブル構造を変更することなくカスタムデータを追加したい(しかも柔軟に)という要望を実現するために、ユーザーカスタムテーブルを作ったとします。 [user_custom_table] id=INT //オートインクリメント relational_id=INT //user_table.idへの参照 custom_name=VARCHAR //カスタムフィールド名 custom_value=VARCHAR //カスタムフィールドの値 custom_name、custom_valueには、たとえばそれぞれ以下のような値が入るとします。 custom_name、custom_value Birthday 、1998/1/1 CompanyName 、○○株式会社 CompanyTel、000-0000-0000 CompanyAddress、東京都千代田区○○-○○ user_custom_table.relational_idはuser_table.idにリレーションしているとすると、INNER JOINして値を取得する方法は判ります。 ※カスタムテーブルに値がない場合という状況は無視できる仕様です。 たとえば、会社住所が東京都で始まるユーザのみを抽出すると、以下のような感じでしょうか。 SELECT user_table.* FROM user_table INNER JOIN user_custom_table ON user_table.id = user_custom_table.relational_id WHERE user_custom_table.custom_name = 'CompanyAddress' AND user_custom_table.custom_value LIKE '東京都%' ただ、この方法だと、誕生日が○月○日以前で、会社名に○○を含んで、会社住所が東京都で始まり…と検索条件が増えていった場合にINNER JOINがどんどん増えていって、いかにも効率が悪いと思えてなりません。 とりあえず適当に書いてみるとして、以下のような感じでしょうか。 SELECT user_table.* FROM user_table INNER JOIN user_custom_table AS custom_1 ON user_table.id = custom_1.relational_id INNER JOIN user_custom_table AS custom_2 ON user_table.id = custom_2.relational_id INNER JOIN user_custom_table AS custom_3 ON user_table.id = custom_3.relational_id WHERE custom_1.custom_name = 'CompanyAddress' AND custom_1.custom_value LIKE '東京都%' AND custom_2.custom_name = 'Birthday' AND custom_2.custom_value < '2001/1/1' AND custom_3.custom_name = 'CompanyName' AND custom_3.custom_value LIKE '%○○%' もっと効率の良い書き方、一般的にはこういう場面ではこんな書き方をするなど、識者の方から教えを請いたくて質問しました。 ※ちなみにDBはMySQLですが、とくにMySQLに限らない方法で答えを頂ける方がありがたいです。

    • ベストアンサー
    • MySQL
  • テーブルの結合について

    皆さん教えてください。 zaiko と testBと言うテーブルに商品IDカラムを入れておき この二つのテーブルを結合して商品データはあるけど 在庫がない物だけを抽出したいのですが、その場合どのような SQLになるでしょうか?? 商品データも在庫もある場合下記のSQLで商品情報と在庫データが 出てくるのですが、その逆と言うか、商品情報は登録してあるが、 在庫テーブルに登録されていない商品情報を検索したいと思っております。 select zaiko.*,product_data.* from zaiko inner join product_data on zaiko.nyuko_item_code = product.item_code ではどうかよろしくお願いします。

  • 行と列の入れ替え検索(行は可変で項目を固定にしたい)のSQL文が出来な

    行と列の入れ替え検索(行は可変で項目を固定にしたい)のSQL文が出来ない 以下のテーブルがあります。 <テーブルA> UID  name --------------- 1   伊藤 2   鈴木 3   高橋 4   佐藤 <テーブルB> no UID key   value ------------------- 1  1  telno  001-01-0001 2  1  faxno  001-01-0002 3  1  nickname itochan 4  2  telno  111-11-1111 5  2  faxno  111-11-1112 6  3  nickname hashi この2つのテーブルがあります。 ・テーブルAにあるUIDはテーブルBにない場合もあります。 この場合、以下のように出力して、csvファイルにしたいのですが、可能でしょうか? 申し訳ありませんが、よろしくアドバイスお願いいたします。 UID  name  telno    faxno     nickname ------------------------------------------------- 1   伊藤  001-01-0001 001-01-0002  itochan 2   鈴木  111-11-1111 111-11-1112 3   高橋                  hashi 4   佐藤 (経緯:wordpressのユーザの一覧(users,usetmetaテーブル)をユーザ一覧としてCSV形式にして保管したいのです。抽出&登録できるプラグインがなかったので)

    • ベストアンサー
    • MySQL
  • SQLインジェクションの危険性について[MYSQL]

    下記のコードはPHPプログラマ一年生の自分から見ても、 POST入力値をチェックしていない危険なコード(一部を抜粋)だと 思うのですが先輩は問題ないと言い、修正する気がありません。 そこで、SQLインジェクションのようなSQL文を送信された場合に ユーザー名とパスワードが表示されれば納得すると思い いろいろテストしてみたのですが、うまく表示されません。 実はSQLインジェクションなどの問題ないソースなのでしょうか? ご教授、よろしくお願いいたします。 ■テーブル構成(ユーザ・パスワード管理テーブル) CREATE TABLE `sample`.`test` ( `user_id` INT NOT NULL , `passwd` VARCHAR( 32 ) NOT NULL ) ■問題の処理分 <?php $user_val = "" ; $pass_val = "" ; if (isset($_POST["user_id"]) == TRUE) {   $mysql_c = mysql_pconnect('******' , "******" , "******");   mysql_select_db("******" , $mysql_c);   $sql = 'select * from test where user_id = ' . $_POST["user_id"] ;   $result = mysql_query($sql , $mysql_c);   if ( $result != false)   {     if(($row = mysql_fetch_object($result)) != false )     {       // パスワードチェック       if ( $row->passwd != $_POST["pass"] )       {         echo "check NG" ;       }       else       {         $user_val = $row->user_id ;         $pass_val = $row->passwd ;       }     }   } } ?> <FORM action="index.php" method="post" > <input type="text" name="user_id" value="<?php echo $user_val ?>" > <input type="text" name="pass" value="<?php echo $pass_val ?>" > <input type="submit"> </FORM>

    • ベストアンサー
    • MySQL
  • 以下のSQLについて

    AccessMDBで、以下のテーブルがあったとします。 テーブル1 ID,NAME 1,おなまえ 2,名前 3,なまえ テーブル2 ID,DB1_ID,NAME,開始日,終了日 1,1,4月,2006/4/1,2006/4/30 2,1,6月,2006/6/1,2006/6/30 3,2,2006年度,2006/4/1,2007/3/31 このとき、以下のように出力したいのですが ID(オートナンバーと仮定),NAME,月,開始日,終了日 1,おなまえ,4月,2006/4/1,2006/4/30 2,おなまえ,6月,2006/6/1,2006/6/30 3,おなまえ,(null),(null),(null) 4,名前,2006年度,2006/4/1,2007/3/31 5,名前,(null),(null),(null) 6,なまえ,(null),(null),(null) 試しにクェリーを作ってみた所、(null)の値を手入力しなければいけないのですが nullをいれさせるようにするには、どのようにしないといけないでしょうか? #テーブルをいじることはできず、SQLのみで対処することを考えています。 SELECT テーブル1.NAME, テーブル2.NAME, テーブル2.START, テーブル2.END FROM テーブル1 LEFT JOIN テーブル2 ON テーブル1.ID=テーブル2.DB1_ID UNION SELECT テーブル1.NAME, テーブル2.NAME,テーブル2.START,テーブル2.END FROM テーブル1; よろしくお願いします。

  • SQLで・・

    すみません、教えてください。 次のようなデータがあります。 table_A Name1 Name2 ----------- 1   2 3   4 table_B ID    Name --------------- 1     あ 2     い 3     う 4     え 次のような検索結果が欲しいとします。 Name1 Name2 ------------- あ    い う    え この結果を導くSELECT文はどう書けばいいでしょうか? 今、 select B.Name as Name1, C.Name as Name2 from table_A A, table_B B, table_B C where A.Name1=B.ID AND A.Name2=C.ID とやってみましたが、検索結果は0件になってしまいます。 お知恵をお貸しください!

  • JOINを使ったSQL文を作成しようとしています。

    JOINを使ったSQL文を作成しようとしています。 以下の様な3つのテーブルがある場合のSQL文を教えて下さい ※勉強の為、適当なテーブルを作成しましたが、やりたい事は  JOINの中に更にJOINさせたSQL文を作成したいと思ってます 表A  USER_ID(プライマリキー)  NAME 表B  USER_ID(プライマリキー) ※表A.USER_IDと紐着く  BUMON(プライマリキー)  TERM(プライマリキー) 表C  BUMON(プライマリキー) ※表B.BUMONと紐着く  TERM(プライマリキー) ※表B.TERMと紐着く  MEMBER 【取得したい項目】  表A.USER_ID 【抽出条件】  表CのMEMBERの値が'10','20'以外(である表AのNAMEを取得)

  • 難しいSQL

    別の掲示板に質問したのですが、難易度が高いのかしばらく回答が得られなかったのでこちらに質問してみます。(別の掲示板の方は質問を取り消しました。)ちょっと急いでいるのでどんなものでも構いません。SQLに詳しい方、ご教示お願い致します。環境は、MySQL5.1.33(MacOSX版)を使用しています。 以下のようなデータベースがあります。マスター系となるユーザテーブルと、それを参照するトランザクション系の買い物テーブルです。このデータベースから、「※連続して買い物をしたユーザをSELECTしたい」のですがそのようなことは可能でしょうか?可能であるとすると、どのようなSQLを書けばよろしいでしょうか? ※「連続して」とは、日付をまたがって、という意味です。 例)2009/06/12、2009/06/13 (1)ユーザテーブル mysql> desc user; userid, int(11), primary key username varchar(15) (2)買い物テーブル mysql> desc purchase; purchaseid, int(11), primary key userid int(11) purchaseid date 今、買い物テーブルに以下のようにデータがある場合 mysql> select * from purchase; 1, 3, 2004-01-02 6, 1, 2004-04-03 5, 2, 2008-04-05 4, 2, 2000-01-19 3, 5, 2004-01-01 2, 1, 2001-11-10 9, 5, 2003-12-31 8, 1, 2007-04-05 7, 3, 2004-01-03 mysql> select userid from ... とすると3, 5を抜き出すようにしたいです。(3は2004/01/02, 2004/01/03だから。5は2003/12/31, 2004/01/01だから。) このselect文の書き方を教えて頂きたいです。お手数をおかけしますがよろしくおねがいします。

  • 同じサーバーで、異なるデータベースを同時に開くことはできますか?

    1.タイトルの通り、同時に開くことができますでしょうか? 日本PHPユーザー会ホームページでは以下の方法でできるとの回答があります。 $con1 = mysql_connect(localhost, user_id , pass); $con2 = mysql_connect(localhost, user_id , pass); mysql_select_db("db1",$con1); mysql_select_db("db2",$con2); ======================================== db1 user_table user_id(int),user_name(var) ========================================== db2 main_table main_id(int),user_id(int) ======================================== $sql_user_name = "SELECT user_name FROM user"; $result_user = mysql_query($sql_user_name,$con1); $row_user = mysql_fetch_array($result_user); echo $row_user[user_name]; のようにやっても mysql_fetch_array()がでます。 mysql_queryは最後の接続を活かすとなっているのでこの部分の書き方を教えてください。 2.できたとしてリレーションを行うことができますでしょうか? つまり $sql_user_name = "SELECT user_name FROM db1.user_table,db2.main_table WHERE db1.user_table.user_id = db2.main_table.user_id" というようなことはできますでしょうか? 長文で申し訳ありませんが、どうかよろしくお願いします。

    • ベストアンサー
    • MySQL