使用group by和count in语句实现find contains many的另一种做法

接上一篇文章一个实现类似find contains many(many in many)的sql 语句,原文使用了group by,另外使用了子句的count case when来做条件的过滤。经过笔者的再三实践,发现了另一种做法,即直接在子查询中使用count语句和in的组合来实现这种操作。

原要求:查询即有名字叫张三,也有名字叫李四的班的名字。
翻译为:查询班信息,其中要求学生名字中存在张三,李四的名字的人数至少大于等于2。(不考虑名字重复)

根据这个实现,以下即为完整的sql语句,能够完美地实现这个操作:

select * from class t where (select count(1) from student where classid = t.id and name in (张三,李四)) >= 2

这种做法,即是直接在谓语上使用count语句,并对count子句做过滤操作。
本实现暂未考虑效率问题,不知道和上一篇文章,哪个效率更好一些。

一个实现类似find contains many(many in many)的sql 语句

有如下一种需求(使用班级class和学生student表来描述)

找到学生中即有名字叫张三也有名字叫李四的班级,其中参数<名字>表示任意多个名字,即不限仅有两个参数。

在这种需求中,如果仅只有张三和李四两个条件,则sql可以写成如下:

select a.* from class a where 
exists(select 1 from student where classId=a.id and name=张三) 
and exists(select 1 from student where classid=a.id and name=李四)

在以上的条件中,有两个条件,因此有两个exists子句。而如果有更多呢,比如三个或四个以上,那么 这个exists就会更多。在使用以java实现的sql语句中,就需要使用程序(如for循环)来组装sql了。

有一种更好的解决办法如下,即类似一种 (张三,李四)均在指定班级的学生列表中这种理解方式。使用伪码来描述就是

select a.* rom class a inner join a.studentList where a.studentList.names contains(张三,李思)
或
select a.* rom class a inner join a.studentList where (张三,李思)in a.studentList.names

就是这种集合之间包含的例子,即保证一个集合在另一个集合中。然而现在的sql还没有能够直接表示这种的,更多的使用是使用in来表示一个参数值在一个集合中,而不是一个子集合包含一系列指定的参数。

那么反过来呢,我们利用in来处理这种问题,当学生有一个名字满足参数中值的时候就+1,那么符合条件的班级中的对学生计数的值一定就等于参数列表的长度了(这里必须假设参数值是不相同的)。简单的逻辑如下所示:

对每一个班级进行分组
对每一个班级中的学生进行处理
当学生中的名字满足条件,计数值+1
即最终计数值=条件长度的班级信息,此即我们要查找的班级

使用sql来实现,那么整个实现的sql如下所示:

select * from class where id in (
    select a.id from class a inner join student b on b.classId = a.id
    group by a.id 
    having count(case b.name in (张三,李四) then 1 end)=2
)

以上sql在oracle 10g下测试通过,这里利用了count只对有值的数据计数,而对null不计数的特点。
此文参考了以下文章
http://www.itpub.net/thread-1169213-3-1.html(如何判断多个集合相等,包含)

在centos6.0上安装oracle 11g

oracle11g已经出来很久了,用了oracle 10g一段时间,正好也买了《Oracle Database 9i/10g/11g编程艺术》,所以就想安装oracle 11g,同时也顺便在会了之后,给客户机器升下数据库(原来的oracle 9i不知什么原因很慢,估计是安装时设置不正确)。

参考文章:
http://www.oracle-base.com/articles/11g/OracleDB11gR2InstallationOnEnterpriseLinux5.php 官方文章,在oracle-linux5上安装
http://www.iflym.com/index.php/linux-develop/fedora-15-install-oracle10g-in-gcc-4-6.html 如果在fedora15上安装oracle 10g

其实安装过程与上篇我在fedora 15中安装差不多,惟一的修改即是有些配置信息要变化。其中一个变化即是sysctl.conf文件的变化,原先应用于10g的配置已经不能再应用于11g 了,有些内核要求更大了,详细配置如下所示:

# 以下为安装oracle时修改
#每个信号对象集的最大信号对象数;系统范围内最大信号对象数;每个信号对象支持的最大操作数;系统范围内最大信号对象集数。
kernel.sem  =  250 32000 100 128
#最大共享内存段大小。
kernel.shmmax = 2147483648
#应用程序可使用的IPv4端口范围。
net.ipv4.ip_local_port_range = 9000 65500
#套接字接收缓冲区大小的缺省值
net.core.rmem_default = 1048576
#套接字接收缓冲区大小的最大值
net.core.rmem_max = 8388608
# 套接字发送缓冲区大小的缺省值
net.core.wmem_default = 262144
#套接字发送缓冲区大小的最大值
net.core.wmem_max = 2097152
fs.aio-max-nr = 2097152
fs.file-max = 13631488

主要是socket缓存区大小,oracle 11g要求更大了。其次即是对于文件限制等,oracle要求在/etc/security/limits.conf中增加以下内容:

oracle soft nproc 2047
oracle              hard    nproc   16384
oracle              soft    nofile  4096
oracle              hard    nofile  65536
oracle              soft    stack   10240

其次,在个人配置中,最好配置一些语言设置,让oracle 11g安装时使用英文(中文问题多,还有乱码),如下所示:

#追加语言设置为英文
export LANG=en_US.UTF-8

export PATH
#设置ORACLE_BASE
export ORACLE_BASE=/data/oracle
#设置ORACLE_HOME,一会安装的时候oracle就安装在这个目录下 
export ORACLE_HOME=$ORACLE_BASE/oracle11g
#oracle安装的sid
export ORACLE_SID=orcl
#追加其他path
export PATH=$PATH:$ORACLE_HOME/bin
#追加oracle的加载目录 
export LD_LIBRARY_PATH=$ORACLE_HOME/lib
#追加oracle客户端的语言设置
export NLS_LANG=AMERICAN_AMERICA.AL32UTF8

这样,应该就OK了,在安装时一路next就OK了。安装之后,就可以直接用了,相比oracle 10g好像更简单了。

在OneToMany时一定要使用cascade以级联操作

在使用hibernate时,经常会碰到一级联操作的问题,一般来说,在一对多的情况下,都是应该使用级联操作的,不管是级联删除还是添加都应该支持。不过,主导方都应该是多这边,那在进行配置时,在一这边使用下面代码将主导权交给多这一边:

mappedby="xxxxx"

在这种情况下,我们在添加多这一边的数据,都是经常使用以下的代码来操作的(引用班级与学生的关系,其中班级以持久化):

Clazz c = Clazz.getDomain(班级,1);//班级
Student stu = new Student();//学生
stu.setClazz(c);//设置班级属性
stu.save();//保存操作

这样的操作,可以避免将主导权交由班级时,在进行学生信息修改时出现的过多的sql问题。然而使用以下代码时,会发生什么情况呢:

Student stu = new Student();
Clazz c = Clazz.getDomain(班级,1);
stu.setClazz(c);//设置班级属性
c.getStuList().add(stu);//添加学生
c.update();//更新

简单一看,好像一定会级联保存,因为我们已经对班级强制性的更新了嘛,而且班级的stuList在属性里面也应该是dirty数据,应该被flush,触发级联操作了。然后,事实不是这样的,最终会不会保存学生信息,取决于在班级上对学生的cascade属性。只有当cascade为save_update(包括 配置成all)时,才会触发级联操作,否则会出现意想不到的问题。

具体原因就在于,实际对班级的更新操作,并不会触发对于stuList这个脏数据的判断,真正的更新操作发生在session的flush阶段,并且对于级联操作,也并不是由update进行负责的,而是由hibernate内部在flush阶段根据cascade的配置来决定是否在进行级联的。

继续阅读“在OneToMany时一定要使用cascade以级联操作”

Oracle 字符集的查看和修改(转)

转自:http://blog.csdn.net/tianlesoftware/article/details/4915223 作者:tianlesoftware
什么是Oracle字符集
Oracle字符集是一个字节数据的解释的符号集合,有大小之分,有相互的包容关系。ORACLE 支持国家语言的体系结构允许你使用本地化语言来存储,处理,检索数据。它使数据库工具,错误消息,排序次序,日期,时间,货币,数字,和日历自动适应本地化语言和平台。

影响Oracle数据库字符集最重要的参数是NLS_LANG参数。
它的格式如下: NLS_LANG = language_territory.charset
它有三个组成部分(语言、地域和字符集),每个成分控制了NLS子集的特性。
其中:
Language: 指定服务器消息的语言, 影响提示信息是中文还是英文
Territory: 指定服务器的日期和数字格式,
Charset:  指定字符集。
如:AMERICAN _ AMERICA. ZHS16GBK

从NLS_LANG的组成我们可以看出,真正影响数据库字符集的其实是第三部分。
所以两个数据库之间的字符集只要第三部分一样就可以相互导入导出数据,前面影响的只是提示信息是中文还是英文。

继续阅读“Oracle 字符集的查看和修改(转)”

select any dictionary与select_catalog_role的区别(转)

转自:http://space.itpub.net/23650854/viewspace-688668 作者:zecaro
在日常开发中,连接数据库都使用开发者帐号,仅有connect和resource角色。有时候,我们需要查询v$session时,即发现此帐号没有相应的权限,这时候,就要给相应的帐号赋于select any dictionary的权限或者select_catalog_role的角色了。这两个东西都可以实现让开发者帐号访问v$session的目的,但这两个东西还是有一定区别的。此文章详细地描述了,相应的区别和使用。

相同之处:
有了这两个中的一个,基本就可以查询数据字典,数据字典就是我们经常查询的v$session或v$process这些信息了

不同之处:

  1. select any dictionary是一种系统权限(system privilege),而select_catalog_role 是一种角色(a role)。
  2. 角色的话需要重新登录或者显式的set role 来生效,而赋予系统权限是立即生效的。(P.S. 同样revoke权限也是立即生效)
  3. select_catalog_role可以查看一些数据字典的视图·(可以看role的定义),如dba_之类的,而select any dictionary可以查看sys的表,select_catalog_role看不到。

以下是详细的验证信息:
角色的话需要重新登录或者显式的set role 来生效,而赋予系统权限是立即生效的。(P.S. 同样revoke权限也是立即生效)

继续阅读“select any dictionary与select_catalog_role的区别(转)”