ラベル MySQL の投稿を表示しています。 すべての投稿を表示
ラベル MySQL の投稿を表示しています。 すべての投稿を表示

2020年6月8日月曜日

mysql(5.1) + カーソルloop使ったプログラムのテンプレ

たまに、ツールとしてデータベースプログラムを書くのですが、1から書くのではなく、いつもどこかの作成済みプログラムを流用しているので、流用も面倒くさくなり、テンプレ化することにしました。
他の人にも使ってもらえるよう、コメント多めです。

【概略】
データを取得して、繰り返しする処理のテンプレです。
データベースへの書き込み処理も考慮してトランザクションも記載してあります。

【コード】


-- プロシージャ入れ替え用のDROP
DROP PROCEDURE IF EXISTS PROCEDURENAME;
delimiter ///
CREATE DEFINER = `root`@`localhost` PROCEDURE `PROCEDURENAME`
 (IN param1 VARCHAR(10)
 ,IN param2 VARCHAR(10))
BEGIN
    -- Loop検知・保持する変数
    DECLARE done INT DEFAULT 0;
    -- 通常変数を記載する際にはカーソルの前に書く
    DECLARE local_aa VARCHAR(10);
    DECLARE local_bb VARCHAR(10);
    -- カーソル定義
    DECLARE l_cur CURSOR FOR select a ,b from xx; -- カーソル定義

-- データなしを検知するハンドラ
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
-- 例外を検知するハンドラ
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
    ROLLBACK;
    SET autocommit=1;
    SELECT 0 AS RESULT;
END;

-- メイン処理
-- オートコミットを切って、自立トランザクションにしています
    SET autocommit=0;
    START TRANSACTION;

-- カーソルを開く
    open l_cur;
-- 繰り返し処理
    REPEAT
-- 1件データ取得
        FETCH l_cur into local_aa , local_bb;
-- EOF判定
        IF NOT done THEN
            -- データ処理
            -- これを書いておくとコンソールにデバッグのように出力される
            SELECT now() AS CURRENT_DATETIME local_aa AS aa , local_bb AS bb;

            -- この例では別ストアドを呼ぶ
            call sub_procedure(param1,param2,local_aa,local_bb);
        END IF;

    UNTIL done END REPEAT;

-- 後片づけ
    close l_cur;

    COMMIT;
    SET autocommit=1;

-- 呼び出し元用に正常終了を返す    
    SELECT 1 AS RESULT;

end;
///
delimiter ;

【Tips】mysqlのfunctionでテキストの行数を数える

いつ何のために作ったか忘れたのですが、ごみ箱行きはもったいないのでここに残しておきます。

キーワード:
・テキストの行数を数える
・functionの作り方
・Loopの使い方



drop function if exists util_getRowCount;
DELIMITER ///
CREATE DEFINER=`root`@`localhost` function util_getRowCount(target longtext)
returns varchar(1000)
-- returns decimal(11,0)
BEGIN
    declare lineMaxCount    int;
    declare sLine           VARCHAR(100);
    declare retValue        decimal(11,0);
    declare lineCount       int;
    declare i               int;
    declare isComment       int;
    declare isBlank         int;
    declare lCodeCharCount  int;
    declare currentTarget   longtext;
    declare vDebug          VARCHAR(10000);
    -- 引数チェック
    if CHAR_LENGTH(target) = 0 THEN
        return 0;
    END IF;

    -- 変数初期化
    set i = 0;
    set lineCount = 0;
    set vDebug    = '';
    set lCodeCharCount = 0;

    -- 利用変数準備
    select CHAR_LENGTH(target) - CHAR_LENGTH(REPLACE(target,'\n','')) +1 into lineMaxCount;
    set currentTarget = REPLACE(target,'\r\n','\n');
    set currentTarget = REPLACE(target,'\r','\n');
    charLoopLabel: LOOP
        -- increment 
        SET i = i + 1;

        -- 抜ける条件
        IF i > lineMaxCount THEN
            LEAVE charLoopLabel;
        END IF;

        -- 行として判定する
        set sLine = replace(substring_index(currentTarget,'\n',1),'\t',' ');
        set isComment = 0;
        set isBlank   = 0;
        
        -- コメント判定
        if substr(trim(sLine),1,2)= '--' THEN
            set isComment = 1;
        end if;

        -- 空行判定
        if length(trim(REPLACE(sLine,'\t',''))) = 0 THEN
            set isBlank = 1;
        end if;

        if (isComment = 0 and isBlank = 0) THEN
            SET lineCount = lineCount + 1;
            set lCodeCharCount = lCodeCharCount + CHAR_LENGTH(sLine);
        end if;
        -- 判定した行を取り除く
        set currentTarget = substr(currentTarget,CHAR_LENGTH(sLine) + 2);
        
        -- set vDebug = CONCAT(vDebug,isComment,isBlank,'xx',sLine,'_');
    END LOOP charLoopLabel;
    return CONCAT(lineCount , ',' , lCodeCharCount);
    -- return lineCount;
    -- set vDebug = CONCAT(vDebug,'__result__',lineCount);
    -- return vDebug;

-- return CHAR_LENGTH(target);

end;
///
delimiter ;
-- select util_getRowCount('12345\n22345\n33345') AS '3';
-- select util_getRowCount('12345\n22345\n33345\n\n\n\n\n') AS '3';
-- select util_getRowCount('12345\n22345\n33345\n\n\n\n\n    -- comment \n -- comment2\n44444') AS '4';
-- select util_getRowCount('日本語\n日本語2\n日本語3\n\n\n\n\n    -- コメント1 \n -- comment2\n44444\n') AS '4';

2017年11月13日月曜日

MySQLのチューニング(JOIN編)

おそらくオプティマイザの挙動が安定しないことが起因で、今まで問題なく動作していたクエリがものすごく遅くなるという事象に遭遇しました。
結果、「ばらし」て組み直すことで解消したのですがその一部始終で技術要素を色々調べられたのでメモしておきます。

やったことの結果要約

・5つあるJOIN句のなかにサブクエリが2つあったので、先行してTemporaryTableへ吐き出すようにした
 →合わせてその時にIndexも貼る
・ベースとして結合したい表をFROMの後へ置き、そのあとは結合しやすい順で並べた。
 →特に意味はないという記事もあったが念のため。元は「ほしい情報」をベースで置いていた。
・結合の条件のなかで、左に今まで出てきている項目、右が対象項目という順とした。
 →オプティマイザに効果があるとの情報
・結果、2分かかっても終わらなかったものが、6秒。これでOKとしました。

実施1)Temporary Tableを使い処理を分割

元々1クエリだったものを、分割して、事前にTemporaryを作成、そのあとにクエリでTemporaryから読むという分割をしました。

【Temporary Table作成の構文】
 CREATE TEMPORARY TABLE temptablename
(INDEX idx_temporary (a,b)) ENGINE=MEMORY
SELECT a,b,c,d,e from table1
今回はSSD環境のDBだったのでMyISAMでも良いのですが、メモリへの展開と指定しました。tmp_table_sizeの設定に依存しますが、今回は許容範囲内なので問題なし。
構文にはフィールド名、型名を指定する方法もありますが、今回はベース表のフィールド名、型をそのまま引き継ぐ形で作ればよく、省略して記載しています。

実施2)ベースの表を変える

対象レコードが少ない表をベース表とし、INNER JOINで結合していきました。
これは今まであまり気にしていませんでしたが、オプティマイザの仕組みを考えると理にかなっている方法だと思います。

実施3)結合の構文の左右を意識する

例文)
select tbl1.a , tbl2.cc from tbl1 inner join tbl2 on tbl1.a = tbl2.a
通常無意識的にメイン表を左、結合対象を右にしていましたが、今回は一部の結合条件で左右が逆でした。

ダメな例文)
select tbl1.a , tbl2.cc from tbl1 inner join tbl2 on tbl2.a = tbl1.a
これもオプティマイザの解釈を考慮すれば当然なのでしょうが、結合したい基本を左に置くことを意識し、全体を調整しました。
→結合表が多くなってきた時に無意識に間違いそうな気がします。

実施4)Temporary Tableにインデックス

これは「ひょっとしたら」で追加したのですが、効果が劇的でした。
Temporary(今回は2つ)はそれぞれUNIQUEなので結合項目としてはすべて使ってます。※先の例で言うとa,b,c,d,eのすべてが結合条件。
それにもかかわらず、aとb(抽出元表:table1のPrimary)にインデックスを貼ったことで
使ってくれて、速度も向上したのです。
知識ある人からしたら「当然だよ」かもしれませんが結果には驚きました。

その他)STRAIGHT_JOIN

結局試していないのですが、[STRAIGHT_JOIN]キーワードを使うと、JOINの順番を強制することができるようです。

2016年7月6日水曜日

MySQLでコメントの文字コードを間違ってしまった場合の探し方

日々データベースを操作していると、ついうっかり、テーブル定義のDDLをShift-JISで作ってしまう場合があります。

そんなとき、すぐわかれば作り直しでよいのですが、気づかず運用してしまった場合に
MySQLのWarningsを確認することで対処する方法をメモします。

【要約】

・文字コード違いのデータが格納されている場合、MySQLのCUIで教えてくれる。

【カラムコメントの確認と直しかた】

 $] mysql -uroot ~ ログイン
> select column_comment from information_schema.columns where table_schema = database();
> show warnings;

 警告があると、教えてくれます。

変更方法(通常のALTER TABLE MODIDY です)
> ALTER TABLE TABLENAME MODIFY COLUMNNAME char(2) DEFAULT NULL COMMENT 'コメントを記載';

【テーブルコメントの確認と直しかた】


> select * from information_schema.tables where table_schema = database();
> show warnings;
> alter table TABLENAME COMMENT 'テーブル名';

2016年6月1日水曜日

MySQLにてtoo many connections のエラーが出たときの対処

稼働中アプリを動かしていたところ、システムエラーが発生しました。
エラーログを見てみると [ too many connections ] と。
MySQLのエラーということはわかったので、mysqlサーバーへコンソールログインして確認、処置を行います。

現在の接続状況を確認

> show processlist;
+------+-------+-----------------+-----------+---------+------+------+------+
| Id   | User  | Host            | db        | Command | Time | State| Info |
+------+-------+-----------------+-----------+---------+------+------+------+
| 1944 | user  | hostname : port | dbName    | Sleep   |    6 |      | NULL |
+------+-------+-----------------+-----------+---------+------+------+------|
140 rows in set (0.00 sec)

ここでいうところのrows が接続数

設定の確認

> show variables like "%max_connections%";
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 151   |
+-----------------+-------+

現在は151

> show variables like "%wait_timeout%";
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 50    |
| table_lock_wait_timeout  | 50    |
| wait_timeout             | 28800 |
+--------------------------+-------+

8時間って・・・長いし。(デフォルトでした)

一時しのぎとしてオンラインで設定変更

> set global max_connections = 300;
からの確認

> show variables like "%max_connections%";
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 300   |
+-----------------+-------+

変わりました。

永続設定

/etc/my.cnfへ記載します。
[mysqld]
wait_timeout=3600
max_connections=200

2016年4月6日水曜日

MySQLの接続状況の確認

運用中のMySQLに過去データを大量投入する必要があったため、接続状況を確認する方法を調べたのでメモ。

概略


1) \s にて、MySQLの稼働状況をチェックできる。 (今回の件とは直接関係なし)
2) show processlistにて、接続プロセス数、接続時間などを確認できる。
   接続時間(下記でいうところのTime)は、最終応答からの経過時間。

サンプル


mysql> \s
--------------
mysql  Ver 14.14 Distrib 5.1.73, for redhat-linux-gnu (x86_64) using readline 5.1

Connection id:          132877
Current database:       sampledb
Current user:           root@localhost
SSL:                    Not in use
Current pager:          stdout
Using outfile:          ''
Using delimiter:        ;
Server version:         5.1.73 Source distribution
Protocol version:       10
Connection:             Localhost via UNIX socket
Server characterset:    utf8
Db     characterset:    utf8
Client characterset:    utf8
Conn.  characterset:    utf8
UNIX socket:            /var/lib/mysql/mysql.sock
Uptime:                 61 days 17 hours 51 min 29 sec

Threads: 19  Questions: 99114337  Slow queries: 531  Opens: 2600916  Flush tables: 1  Open tables: 64  Queries per second avg: 18.579
--------------

mysql> show processlist;
+--------+-------+----------------------------+-----------+---------+------+-------+------------------+
| Id     | User | Host                        | db        | Command | Time | State | Info             |
+--------+------+-----------------------------+-----------+---------+------+-------+------------------+
| 132709 | app  | location.domain.ne.jp:10486 | sampledb  | Sleep   | 7045 |       | NULL             |
| 132710 | app  | location.domain.ne.jp:10588 | sampledb  | Sleep   | 6952 |       | NULL             |
| 132711 | app  | location.domain.ne.jp:10625 | sampledb  | Sleep   | 6717 |       | NULL             |
| 132712 | app  | location.domain.ne.jp:10840 | sampledb  | Sleep   | 5968 |       | NULL             |
| 132713 | app  | location.domain.ne.jp:10841 | sampledb  | Sleep   | 6169 |       | NULL             |
| 132714 | app  | location.domain.ne.jp:10915 | sampledb  | Sleep   | 6395 |       | NULL             |
| 132715 | app  | location.domain.ne.jp:10006 | sampledb  | Sleep   | 5986 |       | NULL             |
| 132716 | app  | location.domain.ne.jp:10062 | sampledb  | Sleep   | 5666 |       | NULL             |
| 132717 | app  | location.domain.ne.jp:10168 | sampledb  | Sleep   | 5666 |       | NULL             |
| 132718 | app  | location.domain.ne.jp:10429 | sampledb  | Sleep   | 5583 |       | NULL             |
| 132719 | app  | location.domain.ne.jp:10464 | sampledb  | Sleep   | 5577 |       | NULL             |
| 132870 | app  | remote.domain.ne.jp:33063   | NULL      | Sleep   |  475 |       | NULL             |
| 132871 | app  | remote.domain.ne.jp:41295   | sampledb  | Sleep   |  396 |       | NULL             |
| 132872 | app  | remote.domain.ne.jp:38509   | sampledb  | Sleep   |  470 |       | NULL             |
| 132873 | app  | remote.domain.ne.jp:37658   | sampledb  | Sleep   |  420 |       | NULL             |
| 132874 | app  | remote.domain.ne.jp:39027   | sampledb  | Sleep   |  370 |       | NULL             |
| 132875 | app  | location.domain.ne.jp:10031 | sampledb  | Sleep   |  165 |       | NULL             |
| 132876 | app  | location.domain.ne.jp:10032 | sampledb  | Sleep   |  203 |       | NULL             |
| 132877 | root | localhost                   | sampledb  | Query   |    0 | NULL  | show processlist |
+--------+-------+----------------------------+-----------+---------+------+-------+------------------+
19 rows in set (0.00 sec)

mysql>
 

2016年4月4日月曜日

Win+MySQL5.1環境にMySQL5.7を共存させる方法

MySQL5.1で運用しているシステムがあり、検証評価のために5.7系を利用したいと思い
とりあえずとして自分で使えるWin+MySQL5.1の環境に対してMySQL5.7を導入した時のメモです。

【環境】

・Windows8 Pro(x64) + MySQL5.1(インストーラにてインストール)の環境
・MySQL5.7は公式サイトよりZipで取得


【手順】

・ダウンロードしてきたzipファイルを展開。パスは任意でよいと思いますが、私の場合ProgramFilesの中に直接ディレクトリを作って展開しました。
C:\Program Files\MySQL57

・my.iniを作成

・初期化を実施
mysqld --initialize

・コンソールにて起動

・別コンソールにてmysqlクライアントを起動

・パスワードを変更

ALTER USER 'root'@'localhost' IDENTIFIED BY 'aaaaaaaa';

・mysqld.logに記載

・コンソールの終了

・サービスとしてインストール
mysqld --install MySQL57
※サービスの削除は、mysqld --remove MySQL57

・サービスの起動
net start mysql

ひとまず起動したので、別途、マイグレーションの手順をメモしたいと思います。


2016年3月23日水曜日

MySQL 4.x系ではCREATE USERの構文が違う

ついつい忘れてしまうMySQLのCREATE USERですが、今回改めて実施したところ
はまってしまったので備忘録です。

結果としては、CREATE USER は、MySQL 5.x系からであり、MySQL4.xで実施する際には
構文が違うということでした。

NGケース


mysql> CREATE USER 'username'@'%' IDENTIFIED BY 'password';
ERROR 1064 (42000): 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
'USER'test1'@'%' IDENTIFIED BY 'test12345'' at line 1

というわけで

OKケース


GRANT ALL PRIVILEGES ON *.* TO 'username'@'%' IDENTIFIED BY 'password';

2014年12月15日月曜日

MySQLで、データ保存時の暗号化と復号化


百聞は一見に如かず。MySQLの標準関数で実装できるなんて便利です。

注:本記事は、データベースの暗号化ではなく、カラム(項目)単位です。

サンプル用のテーブル作成


DROP TABLE IF EXISTS `encrytest`;
CREATE TABLE `encrytest` (
 `ID` int(11) NOT NULL AUTO_INCREMENT,
 `Value1` varchar(200) DEFAULT NULL,
 `Value2` varchar(200) DEFAULT NULL,
 PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=UTF8;

サンプルデータの登録


INSERT INTO ENCRYTEST (VALUE1,VALUE2) VALUES (HEX(AES_ENCRYPT('AIUEO','CRYPTKEY')),'KAKIKUKEKO');

データ読み出し (そのまま読みだしてみる。


SELECT ID , VALUE1,VALUE2 FROM ENCRYTEST;
+----+----------------------------------+------------+
| ID | VALUE1              | VALUE2   |
+----+----------------------------------+------------+
|  1 | E50F8C75A2C25FC6C8CDF8AA10C78B41 | KAKIKUKEKO |
+----+----------------------------------+------------+
1 row in set

データ読み出し(復号化して読みだす


SELECT ID , AES_DECRYPT(UNHEX(VALUE1),'CRYPTKEY') AS VALUE1,VALUE2 FROM ENCRYTEST;

+----+--------+------------+
| ID | VALUE1 | VALUE2   |
+----+--------+------------+
|  1 | AIUEO  | KAKIKUKEKO |
+----+--------+------------+
1 row in set

その他メモ


 HEXで保存するので、保存に必要なバイト数は、増えます。
 ※計算してませんが、4倍程度?
 他のBlogなどを参照すると、blogを使うとか、bigbinaryを使うとか、
 色々とアドバイスが出ています。
 今回は、HEX,UNHEXを使っていますが、blobとかだとそれは必要なさそう。






2014年9月16日火曜日

MySQLでサブクエリでUPDATE

以外と便利です。ただ、方言なので、ANSI的なSQLとは違います。


UPDATE `tableA` r,
    (SELECT id,c.hoge
    FROM `tableA` r
       JOIN tableB e ON r.aa = e.aa
       JOIN tableC c ON c.bb = e.bb) x
SET
r.hoge = x.hoge
WHERE
r.id = x.id;

2014年7月7日月曜日

MySQLのGROUP_CONCAT という関数がものすごく便利な件

簡単に言ってしまうと、複数行のデータを、ぐちゃっと1行に纏めてくれる関数です。

SQL


select group_concat(column_name) from information_schema.columns
where table_schema = 'information_schema' and table_name = 'COLLATIONS'

結果


COLLATION_NAME,CHARACTER_SET_NAME,ID,IS_DEFAULT,IS_COMPILED,SORTLEN


ちなみに、group_concatを使わずに取れるデータがこれ。

-------------------------------
COLLATION_NAME
CHARACTER_SET_NAME
ID
IS_DEFAULT
IS_COMPILED
SORTLEN
-------------------------------
複数行のデータが、1行にまとまっていることがわかります。


親子の関連を持つデータで、このデータを横並び表示したい時とかにも活用できそうです。

注意点

●戻せるデータのサイズが決まってます。(設定で変更可能)
[my.ini]の[mysqld]セクションに以下を記載すると反映されます。(要再起動
set group_concat_max_len = 2048000;
※2MBにしています

●NULLを含むデータの場合、NULLが除外されます。

応用

・区切り文字をタブへ変えて、並び順を指定しています
・NULLは、ブランクへ置き換える事により、除外されることを防止しています



select group_concat(CASE WHEN column_name IS NULL THEN '' ELSE column_name END order by ordinal_position separator '\t') from information_schema.columns
where table_schema = 'information_schema' and table_name = 'COLLATIONS'

補足

Oracleでも、11gより同等の関数があるようです。

2014年7月4日金曜日

MySQLでスキーマ情報を取得する

汎用な処理が必要になったので、あるだろうと思い調べてみたらありました。
MySQL 5.5で確認しています。

テーブル名を取得する

SELECT * FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'データベーススキーマ名' ORDER BY TABLE_NAME;

Viewも取れてしまうので、あとは工夫で絞ると良いと思います。

カラム情報を取得する

SELECT * FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'データベーススキーマ名' AND TABLE_NAME = 'テーブル名'
ORDER BY ORDINAL_POSITION;

汎用的にする

Function化して返すようにしています。
この処理は、CSVのヘッダ情報をカンマ区切り、ダブルクォーテーション付きで返すものです

---------------------------------------------------

CREATE FUNCTION GetColumnCommentsOfCSV(schemaName VARCHAR(255) , tableName VARCHAR(255)) RETURNS VARCHAR(2000)
BEGIN
  DECLARE done INT DEFAULT 0;
  DECLARE vColName VARCHAR(255);
  DECLARE vColComment VARCHAR(255);
  DECLARE vOutChar VARCHAR(2000);
  DECLARE curRec CURSOR FOR
    SELECT
        COLUMN_NAME
        ,COLUMN_COMMENT
    FROM
        INFORMATION_SCHEMA.COLUMNS
    WHERE 
        UPPER(TABLE_SCHEMA)   = UPPER(schemaName)   collate utf8_general_ci
      AND UPPER(TABLE_NAME)    = UPPER(tableName)    collate utf8_general_ci
    ORDER BY
        TABLE_NAME,ORDINAL_POSITION
    ;
  DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
  SET vOutChar = '';
  OPEN curRec;
  REPEAT
    FETCH curRec INTO vColName , vColComment;
    IF NOT done THEN
      IF LENGTH(vOutchar) > 0 THEN
        SET vOutchar = CONCAT(vOutChar,',');
      END IF;
      SET vOutchar = CONCAT(vOutChar,'"');
      SET vOutChar = CONCAT(vOutChar,vColComment);
      SET vOutchar = CONCAT(vOutChar,'"');
    END IF;
  UNTIL done END REPEAT;
  CLOSE curRec;
  RETURN vOutChar;
END;
//
DELIMITER ;

Oracleとか、SQLserverでもやってた記憶があるので、今度載せます。

2014年7月3日木曜日

MySQLで、カーソルを使って変数バインドでデータを返す

mysqlでカーソルを使ってあれこれやろうとした時に作ったサンプルを置いておきます。

mysqlの公式ドキュメントに載っているやり方のようです。

カーソルで、値をバインドしたい時には、ユーザー変数を利用するとできます。

プロシージャサンプル


----------------------------------------------------------------
drop procedure if exists sample_cursor;
DELIMITER ;;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `sample_cursor` (IN schemaNm VARCHAR(100) , IN tableNm VARCHAR(100) , IN pos VARCHAR(10))
  READS SQL DATA
BEGIN
  DECLARE done INT DEFAULT 0;
  DECLARE val VARCHAR(100);
  DECLARE cur CURSOR FOR
  SELECT
      column_name
  FROM
      information_schema.columns
  where
      TABLE_SCHEMA   = @table_schema collate utf8_unicode_ci
    and TABLE_NAME    = @table_name collate utf8_unicode_ci
    and ORDINAL_POSITION = @ordinal_position collate utf8_unicode_ci
  ;
  DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
  select schemaNm , tableNm , pos into @table_schema ,@table_name , @ordinal_position;
  open cur;
  REPEAT
    FETCH cur into val;
    -- データが存在する場合
    IF NOT done THEN
      select val;
    END IF;

  UNTIL done END REPEAT;
  close cur;

end
;
;;;
DELIMITER ;

----------------------------------------------------------------

以下実行結果



mysql> call sample_cursor('information_schema','COLUMNS',1);

+---------------+
| val      |
+---------------+
| TABLE_CATALOG |
+---------------+
1 row in set

Query OK, 0 rows affected

mysql> call sample_cursor('information_schema','COLUMNS',2);

+--------------+
| val          |
+--------------+
| TABLE_SCHEMA |
+--------------+
1 row in set

Query OK, 0 rows affected

mysql> call sample_cursor('information_schema','COLUMNS',3);

+------------+
| val        |
+------------+
| TABLE_NAME |
+------------+
1 row in set

Query OK, 0 rows affected




まだまだいろいろとできそうです。

2014年7月2日水曜日

MySQLでMerge文

MERGE , UPSERTといわれる、INSERTとUPDATEを組み合わせた処理。

MySQLでは、[REPLACE]または[INSERT ON DUPLICATE KEY UPDATE]で
実装する事が出来ます。

[REPLACE]
http://dev.mysql.com/doc/refman/4.1/ja/replace.html

[記述例]

REPLACE INTO TARGET_TABLE (COL1,COL2,COL3) VALUES ('1','2','3');

内部的にはDELETE INSERTということなので、タイムスタンプ等の保持で工夫は必要。

ただ、圧倒的に楽にはなりそうです。
(実際、DELETEINSERTを書くところだったので

MySQLで動的DDL

見るよりやってみる。が分かりやすいかもしれません。

キーワード
PREPAREステートメント , executeステートメント

Procedureの実装

drop procedure if exists cust_createtable;
DELIMITER ;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `cust_createtable` (IN table_nane varchar(100))
  READS SQL DATA
BEGIN
  declare col varchar(4000);
  set col = 'col1 varchar(100)';
  select CONCAT('create table ',table_nane,' (' , col , ')') into @qry;
  PREPARE ddl_stmt from @qry;
  execute ddl_stmt;
end
;
;;
DELIMITER ;

呼び出し

call cust_createtable('test3')

確認


show tables like 'test%'

2014年4月14日月曜日

メジャーどころのRDBで利用する主要ツールまとめ。

メジャーどころのRDBで利用する主要ツール覚え書き


  CUI Import Export データ操作GUI Tools
(無償・公式)
Oracle SQLPlus imp exp SQL Developer
SQLServer OSQL Bcp Bcp SQLServer Management Studio
MySQL mysql mysqlimport mysqldump MySQL GUI Tools


MYSQLでフルダンプ

mysqldump -R -uusername -ppassword -hhostname databasename > exportfilename.sql

Oracleでフルダンプ

exp user/pass@connectionname file=fullpath

SQLServerで該当テーブルのデータをCSV(カンマ)で出力

bcp databasename.owner.tablename out dat.dat -U sa -P password -S .\SQLEXPRESS -r \n -c -t, -T


その他1行Tips

○Oracleでデータを一括登録
 SQLLoaderか、SQLPlusで作り込み

○OracleでCSV出力
 SQLPLUSで、パラメータを色々いじくるとできる

○MySQLでCSV出力
 SQLの構文に、LOAD FILE ~とやると取込、OUTFILE~とやると出力

2014年3月18日火曜日

mysqlでリモート接続を可能とする

このメモは、リモート接続でデータベースアクセスをするためのDMLです。


create user remoteuser@192.168.100.100 IDENTIFIED BY 'password'
GRANT ALL PRIVILEGES ON databasename.* TO remoteuser@192.168.100.100

権限を適当にしてしまっていますが、運用に合わせて適切に。。。


別手法として、 remoteuser@% ってのもありますのでご参考まで(自分は使ってません

2013年11月5日火曜日

MySQLでProcedureを使って動的表を返す

開始年月と終了年月を指定すると、表にして返してくれるサンプルです

コード


DROP PROCEDURE IF EXISTS procedureName;
DELIMITER ;;
CREATE DEFINER=root@localhost PROCEDURE `procedureName`(in `arg_ym_start` varchar(10),in `arg_ym_end` varchar(10))
  READS SQL DATA
BEGIN
  -- 年月の開始と終了を指定することにより、年月の表を返す
  declare current_ym varchar(10);
  DROP TABLE IF EXISTS tmp_ym;
  CREATE TEMPORARY TABLE tmp_ym
  (
    title      varchar(100)
    ,yearmonth    varchar(10)
    ,ym_start     varchar(10)
    ,ym_end      varchar(10)
  );
  set current_ym = arg_ym_start;
  CREYMD : WHILE current_ym <= arg_ym_end DO
    INSERT INTO tmp_ym values (current_ym,current_ym,CONCAT(current_ym,'/01'), DATE_FORMAT(ADDDATE(ADDDATE(CONCAT(current_ym,'/01'),INTERVAL 1 MONTH),INTERVAL -1 DAY),'%Y/%m/%d'));
    set current_ym = SUBSTR(DATE_FORMAT(ADDDATE(CONCAT(current_ym,'/01'),INTERVAL 1 MONTH),'%Y/%m/%d'),1,7);
  END WHILE CREYMD;

  SELECT
  title  
  ,yearmonth
  ,ym_start
  ,ym_end 
  from
  tmp_ym;
  DROP TABLE IF EXISTS tmp_ym;

END;;
DELIMITER ;

実行結果例


mysql> call procedureName('2012/01','2012/12');
+---------------+-----------+------------+------------+
| title     | yearmonth | ym_start  | ym_end   |
+---------------+-----------+------------+------------+
| 2012/01    | 2012/01  | 2012/01/01 | 2012/01/31 |
| 2012/02    | 2012/02  | 2012/02/01 | 2012/02/29 |
| 2012/03    | 2012/03  | 2012/03/01 | 2012/03/31 |
| 2012/04    | 2012/04  | 2012/04/01 | 2012/04/30 |
| 2012/05    | 2012/05  | 2012/05/01 | 2012/05/31 |
| 2012/06    | 2012/06  | 2012/06/01 | 2012/06/30 |
| 2012/07    | 2012/07  | 2012/07/01 | 2012/07/31 |
| 2012/08    | 2012/08  | 2012/08/01 | 2012/08/31 |
| 2012/09    | 2012/09  | 2012/09/01 | 2012/09/30 |
| 2012/10    | 2012/10  | 2012/10/01 | 2012/10/31 |
| 2012/11    | 2012/11  | 2012/11/01 | 2012/11/30 |
| 2012/12    | 2012/12  | 2012/12/01 | 2012/12/31 |
+---------------+-----------+------------+------------+