• ベストアンサー

空きのID番号を取得する方法

いつもお世話になっております。 create table AAA( ID int(6) primary key not null auto_increment, DATA varchar(30), )engine=MyISAM; というテーブルにおいて、 insertをすると、IDが1から順に割り振られていきますが、 途中のレコードをdeleteすると、そこが空きのID番号となってしまい、 次にinsertすると、空き番号が残ってしまいます。 やりたいことは、その空き番号を有効活用するために、 auto_incrementを使用せずに、空き番号を適当に取得して、 insertをしたいのです。 何か効率的な良い方法をご存知の方がいらっしゃいましたら、 ご教授いただけると幸いです。 よろしくお願いいたします。

  • MySQL
  • 回答数6
  • ありがとう数5

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

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

#2回答者です。 IDの通番を格納した表を使って、差分検索しない場合、どういうSQLで得られるかというと、以下のようなSQLになります。 <空き番号の内、最小値を求める(その1)> select max(ID)+1 as empty_ID from (select 0 as ID union select ID from AAA) as z where ID<( select min(ID) as ID from( select (select count(*) from AAA where ID<=x.ID) as rownum, ID from AAA as x ) as y where rownum<>ID ); 何をやっているかというと、次のようなことを行っています。 (1)行に通番を付ける (2)通番とIDが不一致になる最小のIDを求める (3)IDの最小値未満のデータをunionでくっつけ、最大のID+1を得る IDの通番を格納した表を使用する方法では、次のようなSQLになります。通番は、ID_tbl表に入っているものとします。 <空き番号の内、最小値を求める(その2)> select min(ID) as empty_ID from ID_tbl as x where not exists(select * from AAA where ID=x.ID) ; 差分検索するための通番管理表を作成するのが、面倒とも思えるでしょうが、空き番号を得るSQLが、かなり簡単になります。 また、MySQL 5.0以降では、ストアドプロシジャを使えますから、通番管理表を簡単に作ることも可能です。 (1)管理表の定義 drop table if exists ID_tbl; create table ID_tbl (ID int primary key); (2)通番を格納するストアドプロシジャの定義 drop procedure if exists ID_gen; -- 存在したら削除 delimiter // -- 終端記号の変更 create procedure ID_gen (in p1stVal int, -- 初期値 in pEndVal int, -- 終了値 in pIncVal int, -- 増分 in pMsgItv int, -- 何行毎に経過メッセージを出力 out pRowNum int ) -- ------------------------------------------- --  多量データ生成 -- ------------------------------------------- begin declare RowNum int; -- 格納行カウンタ declare wVal int; declare fAscDesc smallint; -- 昇順/降順 0:昇順、1:降順 declare cAsc smallint default 0; declare cDesc smallint default 1; -- 昇順か降順か if p1stVal<=pEndVal and pIncVal>=0 then set fAscDesc=cAsc; else set fAscDesc=cDesc; end if; -- set RowNum=0; set wVal=p1stVal; -- while (fAscDesc=cAsc and wVal<=pEndval) or (fAscDesc=cDesc and wVal>=pEndval) do insert into ID_tbl values(wVal); set RowNum=RowNum+1; if pMsgItv>0 then if RowNum%pMsgItv=0 then select concat(cast(RowNum as char(5)), ' row insert,wVal=', cast(wVal as char(5)) ) as msg; end if; end if; set wVal=wVal+pIncVal; end while; set pRowNum=RowNum; end; // delimiter ; -- 終端記号を元に戻す (3)ストアドプロシジャの実行 call ID_gen(1, -- 初期値 5000, -- 終了値 1, -- 増分 100, -- 経過メッセージ間隔(行数) @rownum); select @rownum;

kiku2006
質問者

お礼

chukenkenkou様 複数回にわたり、大変にご親切なご回答をいただきまして、誠にありがとうございました。大変に恐縮しております。 やりたいことも実現できそうです。 ありがとうごうございました。

その他の回答 (5)

回答No.6

#5さんの言う「実際には削除せず、削除フラグを設ける」というのは、 更新の方が削除より、インデクスなどの影響範囲が狭まるためで、高トラフィック環境等でよく行われる方法です。 しかし、削除フラグを立てただけのデータがいつまでも存在するのも問題で、例えば5年後、10年後にはそういったデータが多量件数になっているかも知れません。インデクスの段数も多くなってしまい、通常の検索の性能も劣化してしまうかも知れません。バックアップやリストアの時間も、長くなってしまいます。 これらを許容でき、よほどディスクスペースに余裕があるのでないなら、いずれ夜間バッチやメンテ日で、削除フラグを立てたデータを実際に消すといった作業が必要になります。

kiku2006
質問者

お礼

chukenkenkou様 何度も丁寧なご回答いただきまして、大変にありがとうございました。 大変に勉強になりました。 ありがとうございました。

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

そもそもデータは削除する物ではありません。 削除フラグだけたてて表示しないようにするのが賢明。 つまり「空き番号」は発生しません。

kiku2006
質問者

お礼

yambejp様 ご回答をありがとうございました。 ご指摘、ごもっともですね。 別のテーブルでは、削除フラグを立てる方法も利用しようと思います。 ありがとうございました。

  • Willyt
  • ベストアンサー率25% (2858/11131)
回答No.3

>このようなことをする設定か何かあると良いなと思うのですが、 いかがなものでしょうか ですから、その回答をしたつもりです。シリアルファイルではそれは不可能です。その番号までを別のファイルに読み込み、データを入れてからまたその次から読むという格好にしなければなりません。それをしないためにはファイルをランダムファイルにしなければならないのです。

kiku2006
質問者

お礼

Willyt様 ご回答をありがとうございました。 RDBというのはランダムアクセスファイルになっているのかと思っているのですが、少しハイレベルな感じで今回はよく理解できませんでした。後学のためにも時間を見つけて勉強しておきます。 ありがとうございました。

回答No.2

MySQLのバージョンは何ですか? 空いている番号の内、最小値を使いたいのでしょうか? 一番簡単で安定した性能を出せるのは、1~nの連番を格納した表を作り、その表とAAA表で差分検索する方法です。

kiku2006
質問者

補足

chukenkenkou様 ご回答ありがとうございます。 MySQLのバージョンは5.0です。 >空いている番号の内、最小値を使いたいのでしょうか? その通りです。 >一番簡単で安定した性能を出せるのは、1~nの連番を格納した表を作り、その表とAAA表で差分検索する方法です。 なるほどです。MySQL自体にはそういった空き番号の最小値を取得する機能はなさそうな感じですね。こういったニーズはあまり無いものなのでしょうか? 他に何か方法がありそうでしたら、またご教授いただければ幸いです。

  • Willyt
  • ベストアンサー率25% (2858/11131)
回答No.1

通常のファイルはシリアルアクセスになっていますから、その大きさは入れたデータの量になります。任意の番号に入れたいならランダムアクセスファイル宣言をすればキーを指定して任意の場所に入力できます。しかし、そのためには例えば1000番まで割り振るなら予め1000個分の空き領域を占有することになりますからHDがオーバーフローしないように注意して下さい。

kiku2006
質問者

補足

Willyt様 早速のご回答、ありがとうございました。 少し私の質問が分かりにくかったかもしれません。 補足させてください。 10件のレコードをinsertすると、 IDは1から順に10まで割り振られます。 例えば3番目のレコードを削除した場合、 次にinsertした場合は、auto_incrementのために、 ID=11のレコードとして記録されますが、 3番がすでに空き番号となっているので、 ID=3のレコードとして記録されて欲しいのです。 このようなことをする設定か何かあると良いなと思うのですが、 いかがなものでしょうか? よろしくお願いいたします。

関連するQ&A

  • 存在しないレコードの抽出方法について

    table_aに存在するidのうち、table_bには存在しないidを抽出するSLQ文を作成したいと思ったのですが、抽出方法が分からなかったので、アドバイスいただける方がいらっしゃいましたら、ご教示いただけたらと思います。 下記の例では、table_aには、101~106までのidがあるのに対し、table_bには101,102,104のidがあるため、103,105,106を抽出したいと思います。 CREATE TABLE IF NOT EXISTS `table_a` ( `auto_id` int(11) NOT NULL, `id` varchar(7) NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8; INSERT INTO `table_a` (`auto_id`, `id`) VALUES (1, '101'), (2, '102'), (3, '103'), (4, '104'), (5, '105'), (6, '106'); CREATE TABLE IF NOT EXISTS `table_b` ( `auto_id` int(11) NOT NULL, `id` varchar(7) NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8; INSERT INTO `table_b` (`auto_id`, `id`) VALUES (1, '101'), (2, '102'), (3, '102'), (4, '102'), (5, '104'), (6, '104'); 以上、よろしくお願いします。

    • ベストアンサー
    • MySQL
  • テーブルの自動キー再割り当て

    以下sqlでテーブルhogeを作成し、idを自動キーにしています。 CREATE TABLE `hoge` ( `id` INT NOT NULL AUTO_INCREMENT, ........(省略).......... PRIMARY KEY (id) ) ENGINE=MyISAM DEFAULT CHARSET=sjis; このテーブルのレコード番号4を削除するとテーブルのidは 1,2,3,5(最終)になり, 新しいデータを追加(INSERT)するとidは 1,2,3,5,6(最終)とid番号4が欠番になります。 テーブルのidを1,2,3,4,5と続き番号に再割り当てして、新しいレコードがid番号6の挿入されるようにするにはどのようにすればよいでしょうか? sql文をご教示い頂きたくよろしくお願いします。

  • auto_incrementを任意の数字から開始するには

    次のようなテーブルで入力するとidが1~始まりますが、 これを1000~開始する方法が分かりません、ご教授お願いします。 CREATE TABLE addressbook ( id smallint(4) UNSIGNED NOT NULL auto_increment, name1 varchar(20) default NULL, birthday date default NULL, PRIMARY KEY (id) ) ENGINE=MyISAM DEFAULT CHARSET=sjis;

    • ベストアンサー
    • MySQL
  • MySQLのテーブル設計で迷っています(桁数)

    MySQLのテーブル設計で迷っています。 クリエイト文のカッコの中は桁数を表しているのでしょうか?それともバイト数でしょうか?桁数であれば、それぞれの型で何桁まで設定できますでしょうか? int型のnoを18桁に変更したいのですが、調べているうちに迷ってしまいました。 型 バイト 最小値 最大値 TINYINT 1 -128 127 SMALLINT 2 -32768 32767 MEDIUMINT 3 -8388608 8388607 INT 4 -2147483648 2147483647 BIGINT 8 -9223372036854775808 9223372036854775807 CREATE TABLE `user` ( `no` int(8) unsigned NOT NULL auto_increment, `id` varchar(24) NOT NULL default '', `email` varchar(255) NOT NULL default '', `reg_date` datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`no`) ) TYPE=MyISAM;

    • ベストアンサー
    • MySQL
  • SQL文を入力したらエラー

     phpMyAdminでSQLに  以下の文を入力しました。  CREATE DATABASE xmas; USE xmas; CREATE TABLE xmastran ( book_id int not null auto_increment, name varchar(30) not null default ' ' , org varchar(50) not null default ' ', addr varchar(80) not null default' ', tel varchar(20) default ' ', mail varchar(40) default ' ', course varchar(30) default ' ', nums integer default 0, PRIMARY KEY (book_id) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;  以上SQLに入力した文ですが!  以下のエラーが出て先に進みません。   エラー 実行した SQL: CREATE DATABASE xmas; → (http://dev.mysql.com/doc/refman/5.5/en/create-database.htmlにリンクされている) MySQL のメッセージ: ドキュメント #1007 - Can't create database 'xmas'; database exists サーバ "127.0.0.1" 上でクエリを実行する: CREATE DATABASE xmas; USE xmas; CREATE TABLE xmastran ( book_id int not null auto_increment, name varchar(30) not null default ' ' , org varchar(50) not null default ' ', addr varchar(80) not null default' ', tel varchar(20) default ' ', mail varchar(40) default ' ', course varchar(30) default ' ', nums integer default 0, PRIMARY KEY (book_id) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;   以上ですが、何が原因で進めないか原因を追及中です。  最近SQLを始めたので、全く見当がつきません。 皆様よろしくお願いします。  

    • ベストアンサー
    • MySQL
  • 年月の情報だけを基に階層構造を作る方法について

    下記のテーブルのような年月の情報だけを基に、添付ファイルのような階層構造を作成する事は可能でしょうか? CREATE TABLE IF NOT EXISTS `sample_db` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `year` varchar(255) NOT NULL, `month` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; INSERT INTO `sample_db` (`id`, `year`, `month`) VALUES (1, '10', '3'), (2, '10', '3'), (3, '10', '4'), (4, '11', '2'), (5, '11', '10'), (6, '12', '3'), (7, '12', ''), (8, '12', '5'), (9, '12', '5'); なお、http://www.makizou.com/archives/1662に記載されているpath情報をデータベースに持つ方法であれば、http://okwave.jp/qa/q6405985.htmlでご教示いただいた方法で実現できましたが、既存のデータベースで下記のような情報しか無い場合に実現可能か否か分からなかったので、 アドバイスいただける方がいらっしゃいましたら、ご教示の程よろしくお願いします。 以上、よろしくお願いします。

    • ベストアンサー
    • PHP
  • insert後のプライマリキーの取得

    タイトルの通りなのですが テーブルがこんな感じでtest_stringをインサートしてその後に test_idを受け取りたいのですが何か方法はないでしょうか。 CREATE TABLE test ( test_id int(8) AUTO_INCREMENT , test_string VARCHAR(100), PRIMARY KEY (test_id) ); こんな風にしても1ばかり返ってきます。 String sql = "SELECT * FROM test;" int primarykey = st.executeUpdate(sql,Statement.RETURN_GENERATED_KEYS);

  • SQLにプログラムが反映されないのは何故

    XAMPP Version: 1.8.2 です。   Xamsのデータベースをデータベースから削除した後  SQLから、以下2個プログラムを入力しました。  すると、削除したにも関わらず、エラーが出て プログラムが反映されません。  Xamsを削除後、再起動したらまたXamsのデータベースが 再構築されます。  私は作成した記憶はありません。  すでに存在しているので、作成しする必要は ないのか、よくわかりません!?  皆様どう思われますか!?  以下入力したプログラムです。 ---------------------------------------------------------------- CREATE DATABASE xmas; USE xmas; CREATE TABLE xmastran ( book_id int not null auto_increment, name varchar(30) not null default '' , org varchar(50) not null default '', addr varchar(80) not null default'', tel varchar(20) default '', mail varchar(40) default '', course varchar(30) default '', nums integer default 0, PRIMARY KEY (book_id) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; エラー 実行したSQL: DATABASEクリスマスを作成します。 のMySQLのメッセージ:ドキュメント #1007 - データベース'クリスマス'を作成できません、データベースが存在している -------------------------------------------------- CREATE DATABASE if not exists xmas; USE xmas; CREATE TABLE xmastran ( book_id int not null auto_increment, name varchar(30) not null default '' , org varchar(50) not null default '', addr varchar(80) not null default'', tel varchar(20) default '', mail varchar(40) default '', course varchar(30) default '', nums integer default 0, PRIMARY KEY (book_id) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; エラー 実行したSQL: TABLE xmastranを(CREATE book_idのINT NOT NULL AUTO_INCREMENT、 名VARCHAR(30)NOT NULL DEFAULT''を、 orgのVARCHAR(50)NOT NULL DEFAULT''を、 addrはVARCHAR(80)NOT NULL DEFAULT''を、 電話VARCHAR(20)DEFAULT''を、 メールVARCHAR(40)DEFAULT''を、 もちろんVARCHAR(30)DEFAULT''を、 numsはINTEGERのデフォルトは0、 PRIMARY KEY(book_id) )ENGINE = MYISAM DEFAULT CHARSET= utf8を; のMySQLのメッセージ:ドキュメント #1050 - テーブル 'xmastran'は既に存在します。 ------------------------------------------------------------------- 以上よろしくお願いします。

    • ベストアンサー
    • MySQL
  • Update文の書き方について

    下記のデータベースで『nameフィールドに値がセットされているもののみ』、別の文字に置き換えたいと思いますが、例えば『UPDATE `test` SET `name` = "かきくけ";』のような書き方ではnameフィールドのすべてのレコードを更新してしまいます。 その為、上記SQL文のSET以降で『nameフィールドに値がセットされているもののみ』という条件を付加すれば良いと思ったのですが、どのようなSQL文を書けば良いか分からなかった為、アドバイスいただける方がいらっしゃいましたら、ご教示の程よろしくお願いします。 CREATE TABLE IF NOT EXISTS `test` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(64) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=6 ; INSERT INTO `test` (`id`, `name`) VALUES (1, 'あいうえお'), (2, ''), (3, 'さしす'), (4, ''), (5, 'ab'); 以上、よろしくお願いします。

    • ベストアンサー
    • MySQL
  • 最初に挿入された行だけ取得したい

    このジャンルでお願いします。 次のようなテーブルで CREATE TABLE IF NOT EXISTS user ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(32) NOT NULL, PRIMARY KEY(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO user (name) VALUES ('user1'); INSERT INTO user (name) VALUES ('user2'); INSERT INTO user (name) VALUES ('user3'); CREATE TABLE IF NOT EXISTS item ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(32) NOT NULL, PRIMARY KEY(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO item (name) VALUES ('item1'); INSERT INTO item (name) VALUES ('item2'); INSERT INTO item (name) VALUES ('item3'); CREATE TABLE IF NOT EXISTS user_item ( id int(11) NOT NULL AUTO_INCREMENT, user_id INT, item_id INT, PRIMARY KEY(id), FOREIGN KEY (item_id) REFERENCES user(id) ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (user_id) REFERENCES item(id) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO user_item (user_id, item_id) VALUES (1, 1); INSERT INTO user_item (user_id, item_id) VALUES (2, 1); INSERT INTO user_item (user_id, item_id) VALUES (3, 2); INSERT INTO user_item (user_id, item_id) VALUES (1, 2); INSERT INTO user_item (user_id, item_id) VALUES (1, 3); INSERT INTO user_item (user_id, item_id) VALUES (3, 3); user_itemテーブルでのselectで、 user_idが1で一番最初に挿入された行だけ を取得したいのですが、どのようなSQL文にすれば良いのでしょうか? この例なら INSERT INTO user_item (user_id, item_id) VALUES (1, 1);←取得 INSERT INTO user_item (user_id, item_id) VALUES (2, 1); INSERT INTO user_item (user_id, item_id) VALUES (3, 2); INSERT INTO user_item (user_id, item_id) VALUES (1, 2);←user_id=3が最初に挿入されているので取得しない INSERT INTO user_item (user_id, item_id) VALUES (1, 3);←取得 INSERT INTO user_item (user_id, item_id) VALUES (3, 3);

    • ベストアンサー
    • MySQL