MySQL临时表用法

MySQL目前还不支持动态游标(可能这个名词用在这里不准确),但很多时候我们需要这样的一个功能(http://bugs.mysql.com/bug.php?id=17153)
比如在存储过程中根据传入的参数动态查询出数据,然后遍历数据,这个时候我们当然想动态的构建出这个游标出来

人民大众就开始想尽办法来实现类似的功能,比如下面的创建一个临时表来完成

mysql> CREATE TEMPORARY TABLE tmp_data (uid int(11) NOT NULL) ENGINE=MEMORY DEFAULT CHARSET=utf8;

注意这里为了提高速度用的是MEMORY引擎,当然它有一些限制行的东西,比如表的大小限制,某些类型的字段不能保存
可以根据自己的情况来抉择
更多信息可以参考http://dev.mysql.com/doc/refman/5.1/en/memory-storage-engine.html
还有一些参考意义的测评也可以借鉴,比如http://www.mysqlperformanceblog.com/2007/08/16/how-much-overhead-is-caused-by-on-disk-temporary-tables/

这里还给它增加了个索引
mysql> ALTER TABLE tmp_data ADD INDEX idx_uid (uid);
mysql> SHOW INDEX FROM tmp_data;

当然用完之后记得清理表,简单的方法就是
mysql> DROP TEMPORARY TABLE tmp_data;

临时表在不同连接之间是不可见的,但是在多线程的环境中,这个还是应该值得注意

还有更多的问题值得研究,比如create和drop临时表不会产生事务提交
但直接DROP TABLE tmp_data;也会删除临时表,但是会提交前面打开事务

临时表和正式表重名机制等等

另外,其实在MySQL内部它也大量使用了临时表,当然对程序员来说都是透明的,你不用关心它什么时候用,数据是放在内存还是放在硬盘上的
想看的话可以通过explain查看

有关MySQL奇技淫巧

有关MySQL奇技淫巧(一般都是Linux下试过的,其它平台自行尝试)

0、学会查看MySQL错误日志
日志名一般为HOST_NAME.err,在里面说不定很容易找到你想要的东西

1、开启MySQL General Query Log
想查看MySQL执行了哪些SQL语句?
那么就开启这个日志吧
http://dev.mysql.com/doc/refman/5.1/en/query-log.html
http://dev.mysql.com/doc/refman/5.1/en/log-destinations.html
可以通过启动mysqld加入相关参数或者动态开启

当然最方便的还是动态开启(不用重启数据库,貌似对MySQL版本有要求)
中文参考:http://www.taobaodba.com/html/477_动态开启mysql-general-log.html

mysql> set global log_output = file;
mysql> set global general_log_file=’/home/guohai/dev/x/topics/mysql/sql.log’;
mysql> set global general_log=on;

还有个要求就是MySQL对该日志文件有读写权限,我把它的own和group全改成跟MySQL对应的了,要不然报错

关闭只需要
mysql> set global general_log=off;

2、innotop使用
去http://code.google.com/p/innotop/下载最新版的安装包,这里用的是innotop-1.8.0
按照安装说明安装好,更详细的安装说明可以参见http://crazytoon.com/2008/05/21/mysql-how-do-you-install-innotop-to-monitor-innodb-in-real-time/
可能innotop版本不一样,自己稍微变通下
第一次运行innotop的时候会叫你输入一些配置信息,比如数据库链接信息
这个你可以在~/.innotop/innotop.conf中修改
guohai@KNIGHT:~/.innotop$ tree
.
|– innotop.conf
`– plugins

1 directory, 1 file

man innotop是永远不能少的
另外可以参见http://5iwww.blog.51cto.com/856039/570669
http://blog.csdn.net/qiuyepiaoling/archive/2010/11/29/6042379.aspx

3、mysql命令的一些有用参数选项
生产环境中手工操作数据库是个技术活
有些参数可以方便我们获取需要的数据,或者节省我们的时间
一些明显的信息提示或许可以帮助我们减少出错的概率
比如
mysql>prompt \h:\u@\d>
PROMPT set to ‘\h:\u@\d>’
localhost:root@hpm>
这样你就明显的看到,你是在哪台机器,用户是什么,操作的数据库是什么

MySQL Command Line Pager
mysql>\P more
mysql>\P less
mysql>\P

下面是别人总结出来有用的
http://www.developer.com/db/10-command-line-timesavers-for-mysql-tasks.html
http://www.ningoo.net/html/2008/mysql_cmdline_html_xml_prompt.html
自己也可以查看手册找到一些有用的信息

4、create table log_20110630 like log_template;
like子句会复制表结构,包括索引等,不包括权限

create table log_20110701 as select * from log_20110630;
复制数据,不包括索引

试玩HandlerSocket for MySQL

有这么一个场景,单台MySQL数据库服务器,最大表约500万记录,常用表100万记录,每行记录的数据量不是很大
查询还算频繁,插入修改删除数据不是那么多,多个应用同时访问这个数据库
有个应用就是查询为主,没有修改,少量的删除
查询SQL语句很简单,根据用户ID查询他下面的记录(某些字段),根据记录ID查询整条记录,两表关联查询100万的关联500万
目前应用的实现也很简单,简陋的数据库连接池,客户端发送SQL语句到SQL服务器
现在也没有遇到很大的问题,没有宕机什么的问题,但就是客户端反映慢,但是看服务器的负载都不是很高

所以老大就说太慢了,需要解决

这个慢是比较显然的啊,每次解析执行SQL语句,网络传输,数据库服务器内存较小,缓存不够

所以当然就想到了在应用层加缓存,但是在几个月之前曾经看到过HandlerSocket的简单介绍,只知道它可以加快查询,没有细看

所以突然觉得目前这个也可以改用HandlerSocket,而且更合适,向老大建议,当然建议都是木有用的,因为我做不鸟主,老大有老大的想法

所以只能自己研究看看这个东西适合不了

于是就开始下载编译安装这个东西了,步骤还算简单

具体安装参见installation.en.txt

我开始想用5.1的源码和5.5的二进制版本混合编译(目前我自己用的5.5的MySQL),天知道能不能成功(最后是不能成功的,我又用5.1的源码重新编译了一个MySQL数据库出来,然后编译HandlerSocket并把它安装到5.1中了,后来看到介绍是源码版本一定要和当前安装的数据库的版本一致,之所以需要源码是因为HandlerSocket插件用到了MySQL源码之中的几个头文件之类的吧,总之不看README是很头疼的。比如有的酒店的电梯是要用房卡刷了电梯之后才能按楼层的,README写的清清楚楚,汗)
我的编译步骤如下
$ ./autogen.sh
$ ./configure –with-mysql-source=/home/guohai/dev/src/database/mysql-5.1.57 –with-mysql-bindir=/home/guohai/dev/a/mysql/mysql-5.1.57-linux2.6-i686/bin –with-mysql-plugindir=/home/guohai/dev/a/mysql/mysql-5.1.57-linux2.6-i686/lib/mysql/plugin

$ make
$ sudo make install

mysql> install plugin handlersocket soname ‘handlersocket.so’;
Query OK, 0 rows affected (0.03 sec)

mysql> show plugins;
+—————+——–+—————-+——————+———+
| Name | Status | Type | Library | License |
+—————+——–+—————-+——————+———+
| binlog | ACTIVE | STORAGE ENGINE | NULL | GPL |
| partition | ACTIVE | STORAGE ENGINE | NULL | GPL |
| CSV | ACTIVE | STORAGE ENGINE | NULL | GPL |
| MEMORY | ACTIVE | STORAGE ENGINE | NULL | GPL |
| InnoDB | ACTIVE | STORAGE ENGINE | NULL | GPL |
| MyISAM | ACTIVE | STORAGE ENGINE | NULL | GPL |
| MRG_MYISAM | ACTIVE | STORAGE ENGINE | NULL | GPL |
| handlersocket | ACTIVE | DAEMON | handlersocket.so | BSD |
+—————+——–+—————-+——————+———+
8 rows in set (0.00 sec)

这样插件就安装好了,接下来就是装个客户端试试看了,用的淘宝的那个

当然还要在my.cnf中增加类似参数
loose_handlersocket_port = 9998
# the port number to bind to (for read requests)
loose_handlersocket_port_wr = 9999
# the port number to bind to (for write requests)
loose_handlersocket_threads = 16
# the number of worker threads (for read requests)
loose_handlersocket_threads_wr = 1
# the number of worker threads (for write requests)
open_files_limit = 65535
# to allow handlersocket accept many concurrent
# connections, make open_files_limit as large as
# possible.

弄hs4j还是报了几个错误,什么open_table,原因是我表名没有写对,巨汗,不过这个提示也够瓜的
估计也是我没有理解这个东西

然后我又报了个错误
Exception in thread “main” com.google.code.hs4j.exception.HandlerSocketException: Connection has been closed
at com.google.code.hs4j.impl.HSClientImpl.awaitResponse(HSClientImpl.java:417)
at com.google.code.hs4j.impl.HSClientImpl.insert0(HSClientImpl.java:325)
at com.google.code.hs4j.impl.HSClientImpl.insert(HSClientImpl.java:309)
at com.google.code.hs4j.example.HS4JExample.main(HS4JExample.java:29)
这个我把keys写少了一个,所以报错了
总之都是我的问题

就这样解决几个问题之后客户端就基本可用了

然后我还试了下Python的客户端,装好Python的插件http://packages.python.org/python-handler-socket/
就可以开始玩了,照着例子也很简单

看了还算多的文章介绍以及issues,这个东西在某些特定的情况下还是可以开始用的吧,以前提的比较多的问题,什么auto_increment之类的也都解决了

觉得最关键的是就是参数怎么配置,才能让这个东西在需要的场景下发挥最大的能力

还得多看看,多试试才有更充分的把握

参考
https://github.com/ahiguti/HandlerSocket-Plugin-for-MySQL
http://yoshinorimatsunobu.blogspot.com/2010/10/using-mysql-as-nosql-story-for.html 你懂的
https://github.com/killme2008/hs4j
http://whitesock.iteye.com/blog/811339
http://rdc.taobao.com/team/jm/archives/545
http://www.cnblogs.com/inrie/archive/2011/01/28/1946572.html 系列文章
http://huoding.com/2011/04/10/62

流水帐总是很简单,再把编译MySQL的主要过程也记在这里吧,凑点字数
我用的是Ubuntu 10.10 X86,以前编译过很多软件开发包,所以如果编译的时候遇到缺什么包装上就行了
下载就不说了,下载都不会的就不要从源码编译了,我用的是mysql-5.1.57.tar.gz
编译参见INSTALL-SOURCE
2.11.2. Installing MySQL from a Standard Source Distribution

$ ./configure –prefix=/home/guohai/dev/a/mysql/mysql-5.1.57-linux2.6-i686 –with-unix-socket-path=/tmp/mysql51.sock –with-tcp-port=3307 –with-named-curses-libs=/lib/libncurses.so.5 –enable-assembler –with-plugins=partition,innobase –with-charset=utf8 –with-collation=utf8_general_ci –with-extra-charsets=big5,ascii,gbk,utf8,latin1 –with-big-tables –without-debug

$ make
$ make install

$ cd /home/guohai/dev/a/mysql/mysql-5.1.57-linux2.6-i686
$ chown -R mysql .
$ chgrp -R mysql .

我以前已经装过MySQL5.5所以这些用户以及组都是有的

guohai@KNIGHT:~/dev/a/mysql/mysql-5.1.57-linux2.6-i686$ sudo cp ‘/home/guohai/dev/src/database/mysql-5.1.57/support-files/my-medium.cnf’ ./my.cnf

$ sudo bin/mysql_install_db –user=mysql –basedir=/home/guohai/dev/a/mysql/mysql-5.1.57-linux2.6-i686 –datadir=/home/guohai/dev/a/mysql/mysql-5.1.57-linux2.6-i686/data

启动数据库
$ sudo bin/mysqld_safe –defaults-file=/home/guohai/dev/a/mysql/mysql-5.1.57-linux2.6-i686/my.cnf –user=mysql &
注意这个参数传递的顺序,我开始一直把–user=mysql放前面,启动的时候报错,MySQL日志记载unknown variable ‘defaults-file’
110601 23:23:28 [ERROR] /home/guohai/dev/a/mysql/mysql-5.1.57-linux2.6-i686/libexec/mysqld: unknown variable ‘defaults-file=/home/guohai/dev/a/mysql/mysql-5.1.57-linux2.6-i686/my.cnf’
110601 23:23:28 [ERROR] Aborting
110601 23:23:28 InnoDB: Starting shutdown…
110601 23:23:33 InnoDB: Shutdown completed; log sequence number 0 44233
110601 23:23:33 [Note] /home/guohai/dev/a/mysql/mysql-5.1.57-linux2.6-i686/libexec/mysqld: Shutdown complete

然后看了mysqld_safe下这个脚本的写法,发现它有对传入的第一个参数判断是否为defaults,于是我就把defaults-file这个参数放到第一位,就正常启动了

一切都好了,然后就是设置了
/home/guohai/dev/a/mysql/mysql-5.1.57-linux2.6-i686$ sudo bin/mysql_secure_installation
一步步来就好了,注意默认my.cnf的位置,我的就放在mysql-5.1.57安装目录下

可能还要修改下MySQL数据文件存放的路径,我最开始编译的时候没有指定,然后它帮我生成的是放在mysql-5.1.57安装目录的一个var目录下面的,而我在my.cnf中指定的是data目录
所以我就修改了一个脚本中的,把var改为data了,具体是哪个脚本忘记了

再重启下,就可以用mysql命令连接上去了,基本正常

MySQL编译简单,关键是编译出自己想要的MySQL,编译出符合自己业务高性能的MySQL就是技术活了,参数很关键
只要参数用的好,没有程序写不鸟;只要工具用的好,没有程序写不鸟;夸张了点

还有刚开始编译出来你可能找不到mysqld这个东西,我反正一开始是没有找到,以为自己编译的出错了,后来才发现在libexec下而不是在bin下面,多用用工具搜索搜索就出来了

另外也可以参见Ubuntu二进制安装MySQL 5.5
地址http://lucane.iteye.com/blog/866355

Lyrics: Rox In The Box-The Decemberists

FROM http://www.songmeanings.net/songs/view/3530822107858854930/

Get the rocks in the box
Get the water right down to your socks
This bulkhead’s built of fallen brethren’s bones
We all do what we can
We endure our fellow man
And we sing our songs to the headframe’s creaks and moans

And it’s one, two, three
On the wrong side of the lea
What were you meant for?
What were you meant for?
And it’s seven, eight, nine
You get your shovel back in line
And if you ever make it to ten
You won’t make it again
And if you ever make it to ten
You won’t make it again

And you won’t make a dime
On this gray granite mountain mine
Of dirt you’re made, of dirt you will return
So while we’re living here
Let’s get this little one thing clear
There’s plenty of men to die, don’t jump your turn

And it’s one, two, three
On the wrong side of the lea
What were you meant for?
What were you meant for?
And it’s seven, eight, nine
You get your shovel back in line
And if you ever make it to ten
You won’t make it again
And if you ever make it to ten
You won’t make it again

And it’s one, two, three
On the wrong side of the lea
What were you meant for?
What were you meant for?
And it’s seven, eight, nine
You get your shovel back in line
And if you ever make it to ten
You won’t make it again
And if you ever make it to ten
You won’t make it again
And if you ever make it to ten
You won’t make it again

JVM标记贴

    JVM参数

  • JMX参数
  • -Dcom.sun.management.jmxremote.port=1090
    -Dcom.sun.management.jmxremote.ssl=false
    -Dcom.sun.management.jmxremote.authenticate=false

  • 扩展参数
  • -XX:+PrintGC
    -XX:+PrintGCDetails
    -XX:+PrintGCTimeStamps 和PrintGC以及PrintGCDetails一起使用
    -Xloggc:gc.log

    -XX:+DoEscapeAnalysis
    -XX:+HeapDumpOnOutOfMemoryError

原码,反码,补码备忘

又是一篇炒现饭的帖子,主要是经常忘记,记在这里

首先理解位bit(Binary digit http://en.wikipedia.org/wiki/Bit)

以下都以二进制为例

比如一个8位的存储单元,那么它就可以保存256种不同状态,如果是无符号数的话,那么就是0 ~ 2^8-1,即00000000 ~ 11111111

那么对于有符号的数就是-2^7 ~ 2^7-1,最高位拿来存储符号了,0为正,1为负,即10000001 ~ 01111111

十进制 原码 反码 补码
127 01111111 01111111 01111111
-127 11111111 10000000 10000001
128 010000000 010000000 010000000
-128 10000000
45 00101101 00101101 00101101
-45 10101101 11010010 11010011
1 00000001 00000001 00000001
-1 10000001 11111110 11111111

上表中红色的数是表示溢出的,8位的存储空间无法表示这样的有符号数

计算机中的有符号数都是以补码的形式存放的,可以理解为只有有符号数才有原码,反码,补码
正数的原码,反码,补码表示都一样
负数的原码为其绝对值的原码,但符号位为1;反码就是保持原码的符号位不变,其他位按位取反;补码就是反码加1

引用Wikipedia
“一个数字的二补数就是将该数字作位反相运算(即一补数),再将结果加 1,即为该数字的二补数。”

为什么要引入原码,反码,补码这些概念
参见:http://dev.csdn.net/htmls/17/17680.html

我们最需要关注的是补码,引用作者的原话是

补码的设计目的是:
⑴使符号位能与有效值部分一起参加运算,从而简化运算规则
⑵使减法运算转换为加法运算,进一步简化计算机中运算器的线路设计

引用自http://zh.wikipedia.org/wiki/二補數
在二补数系统中,一个负数就是用其对应正数的二补数来表示。
00000100 4
11111011 按位取反
11111100 二补数(-4)

减法可以用一个数加上另一个数的二补数来表示
5
-4
———-
1

00000101
+11111100
———-
000000001

另外字节(Byte),字(Word),双字,四字,字长这些概念都需要理解

阮一峰有个帖子讲的也很清楚明了

参考

http://zh.wikipedia.org/wiki/原码
http://zh.wikipedia.org/wiki/反码
http://zh.wikipedia.org/wiki/二補數
http://en.wikipedia.org/wiki/One’s_complement
http://en.wikipedia.org/wiki/Two’s_complement

自己动手编译OpenJDK

无聊的时候决定自己动手编译个JDK,曾经想在X86上编译一个Dalvik出来玩玩,但基于那玩意是高手才玩的起的,所以自己那个想法就不了了知了
看了一圈后发现OpenJDK还是比较容易编译的,所以就来炒份现饭
现代JDK这么容易自己编译还是归功于开源和社区

开场白就说这么多,下面是过程记录
下载源码,解压,阅读README和README-builds.html,然后就可以开工了

我是在Linux 2.6.35-28-generic #50-Ubuntu i686 GNU/Linux上编译的,我编译的是openjdk-7-ea-src-b141-05_may_2011这个版本

主要需要以下条件
Basic Linux Check List
1. Install the Bootstrap JDK, set ALT_BOOTDIR.
2. Optional Import JDK, set ALT_JDK_IMPORT_PATH.(可以不要,如果你是完全编译的话)
3. Install or upgrade the FreeType development package.(这个基本有了)
4. Install Ant 1.7.1 or newer, make sure it is in your PATH.

然后在环境变量下做些设置
ALT_BOOTDIR=$SUN_JDK1.6.0_25_HOME(我是用的SUN的JDK6来作为Bootstrap JDK的)
LANG=C(需要设置成这样,否则编译会报错,Linux一般是zh_CN.utf8改下就可以)
ALT_CUPS_HEADERS_PATH=$CUPS_HOME/include(何为CUPS见此)

另外环境变量中不要出现JAVA_HOME,CLASSPATH,有的话就把它们注释掉

记得使修改过的环境变量生效

然后就cd到$openjdk_src下开始执行make sanity检测
成功的话就执行make all ALLOW_DOWNLOADS=true WARNINGS_ARE_ERRORS=开始较为耗时的编译之旅

这两个参数的意思还是比较明白
ALLOW_DOWNLOADS=true就是编译的时候发现缺少某些预知的源码或依赖时,它会自动去下载,前提是必须联网
WARNINGS_ARE_ERRORS=就是禁止编译器把一些警告当错误,如果不加这个就可能会因为类似于这样的cc1plus: warnings being treated as errors提示而终止编译,这不是我们所希望的

但是不要高兴的太早,多多少少可能会遇到点问题,不过找找Google应该都能解决

下面贴出我在编译的时候遇到的问题
1、
/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011/hotspot/src/os/linux/vm/os_linux.cpp
cc1plus: warnings being treated as errors
/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011/hotspot/src/os/linux/vm/os_linux.cpp: In static member function ‘static bool os::Linux::hugetlbfs_sanity_check(bool, size_t)’:
/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011/hotspot/src/os/linux/vm/os_linux.cpp:2853: error: use of assignment suppression and length modifier together in gnu_scanf format
make[6]: *** [os_linux.o] Error 1
make[6]: Leaving directory `/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011/build/linux-i586/hotspot/outputdir/linux_i486_compiler2/product’

WARNINGS_ARE_ERRORS=

2、
../../../src/solaris/native/sun/awt/awt.h:38: fatal error: X11/Intrinsic.h: No such file or directory
compilation terminated.
make[5]: *** [/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011/build/linux-i586/tmp/sun/sun.awt/awt/obj/BufImgSurfaceData.o] Error 1
make[5]: *** Waiting for unfinished jobs….
make[5]: Leaving directory `/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011/jdk/make/sun/awt’
make[4]: *** [library_parallel_compile] Error 2
make[4]: Leaving directory `/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011/jdk/make/sun/awt’
make[3]: *** [all] Error 1
make[3]: Leaving directory `/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011/jdk/make/sun’
make[2]: *** [all] Error 1
make[2]: Leaving directory `/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011/jdk/make’
make[1]: *** [jdk-build] Error 2
make[1]: Leaving directory `/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011′
make: *** [build_product_image] Error 2

sudo apt-get install libxt-dev

3、
../../../src/solaris/native/sun/xawt/XToolkit.c:48: fatal error: X11/extensions/XTest.h: No such file or directory
compilation terminated.
make[5]: *** [/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011/build/linux-i586/tmp/sun/sun.awt.X11/xawt/obj/XToolkit.o] Error 1
make[5]: *** Waiting for unfinished jobs….
make[5]: Leaving directory `/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011/jdk/make/sun/xawt’
make[4]: *** [library_parallel_compile] Error 2

sudo apt-get install libxtst-dev

从2011-05-12 20:39开始编译,遇到问题,前前后后经历了近一个小时
另外如果编译出错了,并且你解决了错误,貌似你只需要继续执行make all ALLOW_DOWNLOADS=true WARNINGS_ARE_ERRORS=
就可以了,它会接着编译,而不需要从头开始编译
当你看到下面这种提示时
— Build times ———-
Target all_product_build
Start 2011-05-12 21:28:48
End 2011-05-12 21:37:48
00:00:04 corba
00:00:12 hotspot
00:00:03 jaxp
00:00:05 jaxws
00:08:32 jdk
00:00:04 langtools
00:09:00 TOTAL
————————-
应该就可以完工了,cd到$openjdk_src/build/platform/下,看看j2sdk-image和j2re-image中的内容是不是很熟悉?
cd$openjdk_src/build/platform/bin/下执行如下命令就应该可以看到结果,当然版本,硬件架构可能不一样
总之你就自己编译出了一个带自己名字的JDK了

$openjdk_src/build/linux-i586/bin$ ./java -version
openjdk version “1.7.0-internal”
OpenJDK Runtime Environment (build 1.7.0-internal-guohai_2011_05_12_20_39-b00)
OpenJDK Server VM (build 21.0-b11, mixed mode)

$openjdk_src/build/linux-i586/bin$ ./javac -version
javac 1.7.0-internal

有图
compile-my-openjdk

编译成功之后再把那些需要改回来的东西改回来


sudo apt-get install libfreetype6-dev
sudo apt-get install libasound2-dev
sudo apt-get install libcups2-dev

MySQL存储过程备忘

突然要弄MySQL存储过程
好久没有怎么接触过数据库存储过程了,都忘了,记在这里

create table email (id int auto_increment primary key, name text, content text, int size);
create table email_trash (id int auto_increment primary key, name text, content text, int size);

create table attachment (id int auto_increment primary key, email_id int, url text);
create table attachment_trash (id int auto_increment primary key, email_id int, url text);
delimiter //
drop procedure if exists fix_data_trash_magic //
CREATE PROCEDURE `fix_data_trash_magic`()
BEGIN

    DECLARE done INT DEFAULT 0;

    DECLARE _amount INT DEFAULT 0;
    DECLARE _size INT;
    DECLARE _id INT;
    DECLARE _newid LONG;

    DECLARE email_loop CURSOR FOR select id, size from email_trash where name = 'jemmy';

    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

    OPEN email_loop;
        REPEAT
        FETCH email_loop INTO _id, _size;
            IF NOT done THEN
                insert into email(name, content, size) select name,content,size from email_trash where id = _id;

                select last_insert_id() into _newid;

                insert into attachment(email_id, url) select _newid,url from attachment_trash where email_id = _id;
                
                SET _amount = _size + _amount;
            END IF;
        UNTIL done END REPEAT;
    CLOSE email_loop;
END
call fix_data_trash_magic()//

另外还有常用的几个命令

mysql> show procedure status;
mysql> show create procedure procedure_name; // SHOW THE CREATING SQL OF THE SP

包含异常处理,保存点的存储过程

DELIMITER ;;
CREATE PROCEDURE ps_with_exception_etc(in_domain varchar(50), in_account varchar(32), in_passwd varchar(32), OUT out_no int, OUT out_msg varchar(50))
BEGIN
    DECLARE s_fullid varchar(100);

    DECLARE EXIT HANDLER FOR SQLSTATE '23000'
    BEGIN
        SET out_no = -1;
        SET @prc_work_error = 'Repeated key';
        ROLLBACK TO sp_01;
    END;

    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        SET out_no = -1;
        SET @prc_work_error = 'Unknown error';
        ROLLBACK TO sp_01;
    END;

    -- 声明之类的都应该放在前面

    SET AUTOCOMMIT = 0;
        SELECT concat(in_account, '@', in_domain) INTO s_fullid;
        START TRANSACTION;
        SAVEPOINT sp_01;
        -- DO OO HERE
        -- DO XX HERE
        COMMIT;
    SET out_no = 1;
END
;;
DELIMITER ;

存储过程中的动态SQL
这里只是演示动态SQL的写法,要是真这么写太浪费了,直接可以写出来的SQL就不需要动态SQL

DELIMITER ;;
CREATE PROCEDURE ps_with_dynamic_sql(in_domain varchar(50), in_account varchar(32), OUT out_no int, OUT out_msg varchar(50))
BEGIN
    DECLARE fullid varchar(100);

    DECLARE DROP_USER_TABLEK varchar(500);
    DECLARE DROP_USER_MAILS varchar(500);

    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        SET out_no = -1;
        SET @prc_work_error = 'Unknown error';
        ROLLBACK TO sp_01;
    END;

    SET AUTOCOMMIT = 0;
    START TRANSACTION;
    SAVEPOINT sp_01;

        SET @MyQuery = concat('DELETE FROM tablek WHERE uuid = ', in_account);
        PREPARE DROP_USER_TABLEK from @MyQuery;

        SET @MyQuery = concat('DELETE FROM mails WHERE uuid = ', in_account);
        PREPARE DROP_USER_MAILS from @MyQuery;

        EXECUTE DROP_USER_TABLEK;
        EXECUTE DROP_USER_MAILS;
    COMMIT;
    SET out_no = 1;
END
;;
DELIMITER ;

还有个简单的

CREATE PROCEDURE `insert_data`(in item integer)
BEGIN
    DECLARE counter INT;
    SET counter = item;
    WHILE counter >= 1 DO
        // DO YOUR BIZ HERE
        SET counter = counter - 1;
    END WHILE;
END

另外在存储过程中声明变量,游标,处理器是有一定的顺序的,否则会报语法错误

触发器

create trigger insert_after_addr
after insert on addr
for each row
begin
set @aid=new.id;
insert into data(addr_id, type, value) values (@aid, 5, 'hello trigger');
end
show triggers//
drop trigger insert_after_addr//
alter table data modify id int(22) not null auto_increment
CREATE trigger insert_before_tablek
    before
        insert on tablek
    for each row
        begin
            set new.status = 25;
        end

Post Office Protocol – Version 3 阅读杂记

Post Office Protocol – Version 3 http://www.rfc-editor.org/rfc/rfc1939.txt

A POP3 session progresses through a number of states during its
lifetime. Once the TCP connection has been opened and the POP3
server has sent the greeting, the session enters the AUTHORIZATION
state. In this state, the client must identify itself to the POP3
server. Once the client has successfully done this, the server
acquires resources associated with the client’s maildrop, and the
session enters the TRANSACTION state. In this state, the client
requests actions on the part of the POP3 server. When the client has
issued the QUIT command, the session enters the UPDATE state. In
this state, the POP3 server releases any resources acquired during
the TRANSACTION state and says goodbye. The TCP connection is then
closed.

Minimal POP3 Commands:

USER name valid in the AUTHORIZATION state
PASS string
QUIT

STAT valid in the TRANSACTION state
LIST [msg]
RETR msg
DELE msg
NOOP
RSET
QUIT

Optional POP3 Commands:

APOP name digest valid in the AUTHORIZATION state

TOP msg n valid in the TRANSACTION state
UIDL [msg]

POP3 Replies:

+OK
-ERR

Example POP3 Session

S:
C:
S: +OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>
C: APOP mrose c4c9334bac560ecc979e58001b3e22fb
S: +OK mrose’s maildrop has 2 messages (320 octets)
C: STAT
S: +OK 2 320
C: LIST
S: +OK 2 messages (320 octets)
S: 1 120
S: 2 200
S: .
C: RETR 1
S: +OK 120 octets
S: S: .
C: DELE 1
S: +OK message 1 deleted
C: RETR 2
S: +OK 200 octets
S: S: .
C: DELE 2
S: +OK message 2 deleted
C: QUIT
S: +OK dewey POP3 server signing off (maildrop empty)
C:
S:

Responses to certain commands are multi-line. In these cases, which
are clearly indicated below, after sending the first line of the
response and a CRLF, any additional lines are sent, each terminated
by a CRLF pair. When all lines of the response have been sent, a
final line is sent, consisting of a termination octet (decimal code
046, “.”) and a CRLF pair. If any line of the multi-line response
begins with the termination octet, the line is “byte-stuffed” by
pre-pending the termination octet to that line of the response.
Hence a multi-line response is terminated with the five octets
“CRLF.CRLF”. When examining a multi-line response, the client checks
to see if the line begins with the termination octet. If so and if
octets other than CRLF follow, the first octet of the line (the
termination octet) is stripped away. If so and if CRLF immediately
follows the termination character, then the response from the POP
server is ended and the line containing “.CRLF” is not considered
part of the multi-line response.

POP3命令实例
telnet mail-host 110

USER hello@example.com

PASS 111111

STAT
+OK 13 332023

LIST
+OK POP3 clients that break here, they violate STD53.
1 202
2 73113
3 402
4 402
5 80267
6 13380
7 407
8 755
9 115473
10 16932
11 30343
12 169
13 178
.

LIST 2
+OK 2 73113

UIDL 3
+OK 3 uuxvo-13134583174281799

RETR 1 #应该返回第1封邮件的所有内容
+OK
Date: Mon, 15 Aug 2011 14:37:42 +0800 (CST)
From:
To: hello@example.com
Message-ID: <6879346.11.1313450262952.JavaMail.root@knight>
Subject: =?UTF-8?Q?=E6=B5=8B=E8=AF=952011=E5=B9=B48=E6=9C=881?=
=?UTF-8?Q?5=E6=97=A5=E6=98=9F=E6=9C=9F=E4=B8=8014:37?=
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=”—-=_Part_10_343799.1313390262951″

——=_Part_10_343799.1313390262951
Content-Type: text/html;charset=gbk
Content-Transfer-Encoding: base64

PGh0bWw+PGhlYWQ+PG1ldGEgaHR0cC1lcXVpdj0iQ29udGVudC1UeXBlIiBjb250ZW50PSJ0ZXh0
L2h0bWw7IGNoYXJzZXQ9Z2JrIj48dGl0bGU+PC90aXRsZT48L2hlYWQ+PGJvZHk+suLK1DIwMTHE
6jjUwjE1yNXQx8ba0rsxNDozNzxkaXY+suLK1DIwMTHE6jjUwjE1yNXQx8ba0rsxNDozNzwvZGl2
PjwvYm9keT48L2h0bWw+
——=_Part_10_343799.1313390262951–
.

DELE 1
+OK

RSET
+OK

TOP 2 3 #应该返回第2封邮件的邮件头+前3行
+OK
Date: Tue, 16 Aug 2011 09:31:27 +0800 (CST)
From: noreply@example.com
To: hello@example.com
Message-ID: <27682895.1.1313458287431.JavaMail.root@SERVER2>
Subject: =?gbk?B?PLLiytTNy9DFMjAxMcTqONTCMTbI1dDHxtq2/jA5OjI3Ps22td3Kp7Dc?=
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary=”—-=_Part_0_31571602.1313458287431″

——=_Part_0_31571602.1313458287431
Content-Type: text/html;charset=gbk
Content-Transfer-Encoding: base64
.

TOP 1 #应该返回第1封邮件的邮件头

QUIT #断开连接