データベースに親子関係が書かれていて、id番号を与えるとその祖先からのリストを表示するプログラムを考えています。
https://oshiete.goo.ne.jp/qa/11554544.html
例えばチンパンジーに該当するid番号を与えると
哺乳類- サル目(霊長類)- ヒト科 - チンパンジー属
と表示するプログラムです
C言語ならば、
データベースは id番号に該当する名前char* name[id]、親 int mather[id]があるとして
char *str;
int i;
i= id;
str = name[i];
while(mather[i] != 0) {
i = mother[i];
str = name[i] + str; // 前に親の名前を追加する。実際は関数で実現
}
感じで書けますが、これをPHPから呼び出せるMySQLのストアドプロシージャをどう書けばよいのか教えてほしいです。
きっと初歩的な間違いをしているのだと思いますが、はじめの一歩でつまずいています。
<試した事>
コマンドラインで
mysql>
create function chainlist(id INT) returns
begin
set @i = id;
set @str = "";
while(@i != 0)
select name,mother form DB where id=@i;
set $str = CONCAT( name, @str);
@i = mother;
end while;
return @str;
mysql> chainlist(10);
で動作確認しようと思ったのですが
#1064 - 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 'begin
set @i = id' at line 2
で動作確認も出来ません。
No.3ベストアンサー
- 回答日時:
DROP PROCEDURE IF EXISTS SET_LR;
DELIMITER //
CREATE PROCEDURE SET_LR()
BEGIN
DECLARE a INT DEFAULT 0;
DECLARE done INT DEFAULT 0;
DECLARE CUR CURSOR FOR
SELECT id FROM animal WHERE level=0 ORDER BY parent_id ASC,id DESC;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
UPDATE animal SET level=0,l=0,r=0;
UPDATE animal SET level=1,l=(SELECT @a:=@a+1 FROM (SELECT @a:=0) AS sub),r=@a:=@a+1
WHERE parent_id IS NULL
ORDER BY id;
OPEN CUR;
REPEAT
FETCH CUR INTO a;
IF NOT done THEN
SET @id=a;
SET @sql='UPDATE animal as a1,animal as a2,animal as a3 SET a1.l=a2.l+1,a1.r=a2.l+2,a1.level=a2.level+1,a2.r=a2.r+2,a3.r=a3.r+2 WHERE a1.parent_id=a2.id AND a2.l<a3.r AND a1.id=?';
PREPARE stmt from @sql;
EXECUTE stmt USING @id;
SET @sql='UPDATE animal as a1,animal as a2,animal as a3 SET a3.l=a3.l+2 WHERE a1.parent_id=a2.id AND a2.l<a3.l and a3.id!=a1.id AND a1.id=?';
PREPARE stmt from @sql;
EXECUTE stmt USING @id;
END IF;
UNTIL done END REPEAT;
CLOSE CUR;
END
//
DELIMITER ;
回答ありがとうございます。
2つ質問
>UPDATE animal SET level=1,l=(SELECT @a:=@a+1 FROM (SELECT @a:=0) AS sub),r=@a:=@a+1
ここの '=' と ':=' の使い分けが理解に苦しみます。項目への代入は=、変数への代入は:=ということでしょうか?
>a1.parent_id=a2.id AND a2.l<a3.l and a3.id!=a1.id AND a1.id=?';
はこのままでも良いですが、「キーワード大文字にする」ルールに従えば
a1.parent_id=a2.id AND a2.l<a3.l AND a3.id!=a1.id AND a1.id=?';
ですよね。
No.9
- 回答日時:
> 「比較の=」でなく「代入の=」
UPDATE構文での=は代入の=になります。
ちょっと頭を切り替えて考えてください
私の例はあくまでも「変数をインクリメントしながら別の式に利用する」という
説明です。
> IF SQLSTATE != '02000' THEN
> と書かない理由
例外処理と処理順番のカラミだったような記憶がありますが
実際に別の書き方で試してみてなぜ一度doneで受けているか検証してみれば
いいでしょう。
ほんと、何度もありがとうございます。
欲張って質問してはいけませんね(反省)。
改めて、質問します。
r=@a:=@a+1
はなぜ
r=@a=@a+1
とは書かないのですか?
しつこくてすいません・・・
No.8
- 回答日時:
> r=@a:=@a+1
これはupdate文でrに「@a+1」を設定しているのと同時に
@aに対して@a+1を投入(インクリメント)しています
代入式は代入返すとということです
こんな感じでわかりますか?
set @a=0;
select 2=(@a:=@a+1); /* @a+1は1なので2!=1でfalse=0を返す */
select @a; /* @aは1 */
select 2=(@a:=@a+1); /* @a+1は2なので2==2でtrue=1を返す */
select @a; /* @aは2 */
select 2=(@a:=@a+1); /* @a+1は3なので2!=3でfalse=0を返す */
select @a; /* @aは3 */
何度もご回答、ありがとうございます
>こんな感じでわかりますか?
C言語は知っていますので、お示しくださった例はわかります。が、
select 2=(@a:=@a+1);
は、Cで書くと
if( 2 == (@a=@a+1) )
ということですね。
この例では=は代入ではなく、比較演算子==の意味ですが、私が疑問に思っている
SET
level=1,
l=(SELECT @a:=@a+1 FROM (SELECT @a:=0) AS sub),
r=@a:=@a+1 ← ここ
は、set構文なので、「比較の=」でなく「代入の=」、Cで書くと
r=@a=@a+1;
ということではないのでしょうかという疑問です?
「@a:=@a+1」という平文の式の値をrに代入という事でしょうか?
質問のついでにもう一つ、プログラムの別の部分ですが、
REPEAT
FETCH CUR INTO a;
IF NOT done THEN
は、C的に書くと
while( 「FETCH CUR INTO a;」 が成功する間)
ということですよね。
ならば、もっと素直に
REPEAT
FETCH CUR INTO a;
IF SQLSTATE != '02000' THEN
と書かない理由などありましたら、教えてください。
No.7
- 回答日時:
> ':=' の使い分け
変数はset構文では=ですが平文では:=で代入します
> 「キーワード大文字にする」ルール
そうですね、ストアドプロシージャの場合なるべく大文字で処理してください
ANDがぶれてしまったのは諸々拡張してつかっている備忘録を
コピペしたからです、混乱させてすみません
この木構造を使えば、ul-liをつかった階層表示や
各属性ごとの要素の集計、など諸々直感的で簡単に処理できます
デメリットは、データ編集ごとに更新が必要でデータ量にともない重くなること
また同じカテゴリ内の表示順位はidに依存しているので
id以外で表示順を変えたいならソート用のカテゴリをつくって
別途管理が必要です
何度も回答ありがとうございます。
>変数はset構文では=ですが平文では:=で代入します
r=@a:=@a+1
はどっちなのでしょう?
No.5
- 回答日時:
検索:チンパンジーのid=50001から、系列を列挙
select group_concat(name order by level desc separator '-') as names from animal as t1
where exists(select 1 from animal where id=50001 and l between t1.l and t1.r)
No.2
- 回答日時:
ちょっと長くなるので注意
create table animal(id int primary key,parent_id int null,name varchar(50),level int NOT NULL default 0,l int not null default 0,r int not null default 0);
insert into animal(id,parent_id,name) values
(10001,null,'哺乳類'),
(10002,null,'爬虫類'),
(20001,10001,'サル目(霊長類)'),
(20002,10001,'ネコ目'),
(30001,20001,'ヒト科'),
(30002,20002,'イヌ科'),
(40001,30001,'チンパンジー属'),
(40002,30002,'イヌ属'),
(50001,40001,'チンパンジー'),
(50002,40001,'さる'),
(50003,40001,'ゴリラ'),
(50004,40002,'イヌ'),
(50005,40002,'オオカミ');
No.1
- 回答日時:
MySQLはあまり使ったことがなく、現在テスト環境もないので、
より詳細には、マニュアル、参考書等を使ってください。
https://dev.mysql.com/doc/refman/5.6/ja/stored-p …
・エラーメッセージからして、set @i = id; の ; で切られているように思います。
> mysql クライアントプログラムを使用してセミコロン文字を含むストアドプログラムを定義する(以下略)
にあるようにdelimiter が必要かと
https://dev.mysql.com/doc/refman/5.6/ja/create-p …
・returns の直後には、戻り値の型が来るはずですが、その入力にはありません。
> set $str = CONCAT( name, @str);
> @i = mother;
select した列は into 変数 を使って一度変数に入れないと後から利用できないと思います
回答ありがとうございます
>にあるようにdelimiter が必要かと
なるほど、delimiter // の使い方がやっとわかりました。
>select した列は into 変数 を使って一度変数に入れないと後から利用できないと思います
C言語の代入のようにはMySQLでは行かないんですね。ありがとうございます。
ご指摘、ありがとうございます
前途多難ですが、頑張ってみます
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- MySQL テーブル作成です。どこかのスペルが間違っているか記号など スペースかな? 1 2022/10/01 05:08
- MySQL 参考書に従って入力したつもりでしたが、最後はエラーがでました。 1 2022/09/28 03:45
- MySQL 何にかが違うから エラーなんでしょうね! 2 2022/09/18 05:28
- MySQL MySQLのテーブル作成でハイフン - は使用できないのでしょうか? 2 2022/10/21 16:50
- JavaScript 以前の質問だと、どの条件でも配列が表示されてしまいます。 1 2022/07/09 11:40
- MySQL php テーブルを作れない 2 2022/11/17 18:22
- PHP php テーブルが作成できない 1 2022/11/17 23:41
- C言語・C++・C# 宣言する関数の形が決まっている状態で、 str1とstr2の文字列をこの順に引っ付けてstrに保存し 2 2022/05/30 18:21
- MySQL PHPとMySQLを使った掲示板の作り方 1 2022/06/02 13:00
- C言語・C++・C# プログラミングの授業の課題です 1 2023/01/17 22:15
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
エクセルで最後の文字だけ置き...
-
[MySQL] 3つのテーブルの結合で...
-
エクセルの関数について教えて...
-
sqlで、600行あるテーブルを100...
-
SQLサーバから、項目の属性(型...
-
上位3位を求めるSQL文は?
-
副問合せの書き方について
-
SQL Left Join で重複を排除す...
-
SQLにて特定の文字を除いた検索...
-
[MySQL] UNIQUE制約の値を更新...
-
Access パラメータクエリをcsv...
-
selectした大量データをinsert...
-
親と子供が複数のSQL取得方法
-
テーブルが5つの時の結合の仕...
-
書籍の内容はまともでしょうか?
-
クエリ表示と、ADOで抽出したレ...
-
【SQL文】Insert into文で文法...
-
VIEWの元のテーブルのindexって...
-
inner joinをすると数がおかし...
-
Mysqlでunionを使った検索速度...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
エクセルで最後の文字だけ置き...
-
SQL Left Join で重複を排除す...
-
SQLサーバから、項目の属性(型...
-
副問合せの書き方について
-
VIEWの元のテーブルのindexって...
-
エクセルの関数について教えて...
-
SQLにて特定の文字を除いた検索...
-
マイクラPC版のコマンドで効率...
-
select文のwhere句に配列を入れ...
-
sqlで、600行あるテーブルを100...
-
ある条件の最大値+1を初番する...
-
inner joinをすると数がおかし...
-
Unionした最後にGROUP BYを追加...
-
期間の重複を調べるSQL文につい...
-
クエリ表示と、ADOで抽出したレ...
-
Access パラメータクエリをcsv...
-
PL/SQLの変数について
-
MySQLのDATE型カラム値がNULLの...
-
php+mysqlで複数選択削除について
-
上位3位を求めるSQL文は?
おすすめ情報