呓语的博客

不管前面的路有多艰辛多长,只要怀着一颗执着的心。成功就离你不远了!
posts - 43, comments - 2, trackbacks - 0, articles - 1

2006年1月10日

Java实用工具类库中的类java.util.Random提供了产生各种类型随机数的方法。它可以产生int、long、float、double以及Goussian等类型的随机数。这也是它与java.lang.Math中的方法Random()最大的不同之处,后者只产生double型的随机数。
  类Random中的方法十分简单,它只有两个构造方法和六个普通方法。
  构造方法:
  (1)public Random()
  (2)public Random(long seed)
  Java产生随机数需要有一个基值seed,在第一种方法中基值缺省,则将系统时间作为seed。
  普通方法:
  (1)public synonronized void setSeed(long seed)
  该方法是设定基值seed。
  (2)public int nextInt()
  该方法是产生一个整型随机数。
  (3)public long nextLong()
  该方法是产生一个long型随机数。
  (4)public float nextFloat()
  该方法是产生一个Float型随机数。
  (5)public double nextDouble()
  该方法是产生一个Double型随机数。
  (6)public synchronized double nextGoussian()
  该方法是产生一个double型的Goussian随机数。
  例2 RandomApp.java。
  //import java.lang.*;
  import java.util.Random;

  public class RandomApp{
   public static void main(String args[]){
    Random ran1=new Random();
    Random ran2=new Random(12345);
    //创建了两个类Random的对象。
    System.out.println("The 1st set of random numbers:");
    System.out.println("\t Integer:"+ran1.nextInt());
    System.out.println("\t Long:"+ran1.nextLong());
    System.out.println("\t Float:"+ran1.nextFloat());
    System.out.println("\t Double:"+ran1.nextDouble());
    System.out.println("\t Gaussian:"+ran1.nextGaussian());
    //产生各种类型的随机数
    System.out.print("The 2nd set of random numbers:");
    for(int i=0;i<5;i++){
     System.out.println(ran2.nextInt()+" ");
     if(i==2) System.out.println();
     //产生同种类型的不同的随机数。
     System.out.println();
    }
   }
}

posted @ 2006-02-13 23:24 呓语的博客 阅读(237) | 评论 (0)编辑 收藏

本章介绍Java的实用工具类库java.util包。在这个包中,Java提供了一些实用的方法和数据结构。例如,Java提供日期(Data)类、日历(Calendar)类来产生和获取日期及时间,提供随机数(Random)类产生各种类型的随机数,还提供了堆栈(Stack)、向量(Vector) 、位集合(Bitset)以及哈希表(Hashtable)等类来表示相应的数据结构。
  图8.1给出了java.util包的基本层次结构图。下面我们将具体介绍其中几个重要的类。
           ┌java.util.BitSet
           │java.util.Calendar
           │      └java.util.GregorianCalendar
           │java.util.Date
           │java.util.Dictionary
           │      └java.util.Hashtable
           │             └java.util.Properties
           │java.util.EventObject
           │java.util.ResourceBundle
       ┌普通类┤      ├java.util.ListResourceBundle
       │   │      └java.util.PropertyResourceBundle
       │   │java.util.Local
       │   │java.util.Observable
       │   │java.util.Random
       │   │java.util.StringTokenizer
       │   │java.util.Vector
       │   │      └java.util.Stack
  Java.util┤   └java.util.TimeZone
       │          └java.util.SimpleTimeZone
       │   ┌java.util.Enumeration
       ├接 口┤java.util.EventListener
       │   └java.util.Observer
       │   ┌java.util.EmptyStackException
       └异常类┤java.util.MissingResourceException
           │java.util.NoSuchElementException
           └java.util.TooManyListenersException
       图8.1 java.util包的基本层次结构


8.2 日期类Date

  Java在日期类中封装了有关日期和时间的信息,用户可以通过调用相应的方法来获取系统时间或设置日期和时间。Date类中有很多方法在JDK1.0公布后已经过时了,在8.3中我们将介绍JDK1.0中新加的用于替代Date的功能的其它类。
  在日期类中共定义了六种构造函数。
  (1)public Date()
  创建的日期类对象的日期时间被设置成创建时刻相对应的日期时间。
  例 Date today=new Date();//today被设置成创建时刻相对应的日期时间。
  (2)public Date (long date)
  long 型的参数date可以通过调用Date类中的static方法parse(String s)来获得。
  例 long l=Date.parse("Mon 6 Jan 1997 13:3:00");
    Date day=new Date(l);
  //day中时间为1997年 1月6号星期一,13:3:00。
  (3)public Date(String s)
  按字符串s产生一日期对象。s的格式与方法parse中字符串参数的模式相同。
  例 Date day=new Date("Mon 6 Jan 1997 13:3:00");
  //day 中时间为1997年1月6号星期一,13:3:00.
  (4)public Date(int year,int month,int date)
  (5)public Date(int year,int month,int date,int hrs,int min)
  (6)public Date(int year,int month,int date,int hrs,int min,int sec)
  按给定的参数创建一日期对象。
  参数说明:
  year的值为:需设定的年份-1900。例如需设定的年份是1997则year的值应为97,即1997-1900的结果。所以Date中可设定的年份最小为1900;
  month的值域为0~11,0代表1月,11表代表12月;
  date的值域在1~31之间;
  hrs的值域在0~23之间。从午夜到次日凌晨1点间hrs=0,从中午到下午1点间hrs=12;
  min和sec的值域在0~59之间。
  例 Date day=new Date(11,3,4);
  //day中的时间为:04-Apr-11 12:00:00 AM
另外,还可以给出不正确的参数。
  例 设定时间为1910年2月30日,它将被解释成3月2日。
  Date day=new Date(10,1,30,10,12,34);
  System.out.println("Day's date is:"+day);
  //打印结果为:Day's date is:Web Mar 02 10:13:34 GMT+08:00 1910
  下面我们给出一些Date类中常用方法。
  (1)public static long UTC(int year,int month,int date,int hrs. int min,int sec)
  该方法将利用给定参数计算UTC值。UTC是一种计时体制,与GMT(格林威治时间)的计时体系略有差别。UTC计时体系是基于原子时钟的,而GTMT计时体系是基于天文学观测的。计算中使用的一般为GMT计时体系。
  (2)public static long parse(String s)
  该方法将字符串s转换成一个long型的日期。在介绍构造方法Date(long date)时曾使用过这个方法。
  字符串s有一定的格式,一般为:
  (星期 日 年 时间GMT+时区)
  若不注明时区,则为本地时区。
  (3)public void setMonth(int month)
  (4)public int getMonth()
  这两个方法分别为设定和获取月份值。
  获取的月份的值域为0~11,0代表1月,11代表12月。
  (5)public String toString()
  (6)public String toLocalString()
  (7)public String toGMTString()
  将给定日期对象转换成不同格式的字符串。它们对应的具体的格式可参看例子8.1。
  (8)public int getTimezoneOffset()
  该方法用于获取日期对象的时区偏移量。
  例8.1中对上面介绍的Date类中的基本方法进行了具体的应用,并打印了相应的结果。由于使用了一些过时的方法,所以编译时会有警告信息。另外,由于本例中的时间表示与平台有关,不同的JDK版本对此处理不完全相同,因此不同版本的JDK执行本例的结果可能有细微差异。
  例8.1 DateApp.java
  import java.lang.System;
  import java.util.Date;
  public class DateApp{
   public static void main(String args[]){
    Date today=new Date();
    //today中的日期被设成创建时刻的日期和时间,假设创建时刻为1997年3月
    //23日17时51分54秒。
    System.out.println("Today's date is "+today);
    //返回一般的时间表示法,本例中结果为
    //Today's date is Fri May 23 17:51:54 1997
    System.out.println("Today's date(Internet GMT)is:"
     +today.toGMTString());
    //返回结果为GMT时间表示法,本例中结果为
    //Today's date(Internet GMT)is: 23 May 1997 09:51:54:GMT
    System.out.println("Today's date(Locale) is:"
     +today.toLocaleString());
    //返回结果为本地习惯的时间表示法,结果为
    //Today's date(Locale)is:05/23/97 17:51:54
    System.out.println("Today's year is: "+today.getYear());
    System.out.println("Today's month is: "+(today.getMonth()+1));
    System.out.println("Today's date is: "+today.getDate());
    //调用Date类中方法,获取年月日的值。
    //下面调用了不同的构造方法来创建Date类的对象。
    Date day1=new Date(100,1,23,10,12,34);
    System.out.println("Day1's date is: "+day1);
    Date day2=new Date("Sat 12 Aug 1996 13:3:00");
    System.out.println("Day2's date is: "+day2);
    long l= Date.parse("Sat 5 Aug 1996 13:3:00 GMT+0800");
    Date day3= new Date(l);
    System.out.println("Day3's date(GMT)is: "+day3.toGMTString());
    System.out.println("Day3's date(Locale)is: "
     +day3.toLocaleString());
    System.out.println("Day3's time zone offset is:"
     +day3.getTimezoneOffset());
   }
  }

  运行结果(JDK1.3版,与原文不同,原文是JDK1.0版):
  E:\java\tutorial\java01>java DateApp
  Today's date is Thu Dec 27 17:58:16 CST 2001
  Today's date(Internet GMT)is:27 Dec 2001 09:58:16 GMT
  Today's date(Locale) is:2001-12-27 17:58:16
  Today's year is: 101
  Today's month is: 12
  Today's date is: 27
  Day1's date is: Wed Feb 23 10:12:34 CST 2000
  Day2's date is: Fri Aug 12 13:03:00 CST 1996
  Day3's date(GMT)is: 5 Aug 1996 05:03:00 GMT
  Day3's date(Locale)is: 1996-8-5 13:03:00
  Day3's time zone offset is:-480

  E:\java\tutorial\java01>

8.3 日历类Calendar

  在早期的JDK版本中,日期(Date)类附有两大功能:(1)允许用年、月、日、时、分、秒来解释日期:(2)允许对表示日期的字符串进行格式化和句法分析。在JDK1.1中提供了类Calendar来完成第一种功能,类DateFormat来完成第二项功能。dateFormat是java.text包中的一个类。与Date类有所不同的是,DateFormat类接受用各种语言和不同习惯表示的日期字符串。本节将介绍java.util包中的类Calendar及其它新增加的相关的类。
  类Calendar是一个抽象类,它完成日期(Date)类和普通日期表示法(即用一组整型域如YEAR,MONTH,DAY,HOUR表示日期)之间的转换。
  由于所使用的规则不同,不同的日历系统对同一个日期的解释有所不同。在JDK1.1中提供了Calendar类一个子类GregorianCalendar??它实现了世界上普遍使用的公历系统。当然用户也可以通过继承Calendar类,并增加所需规则,以实现不同的日历系统。
  第GregorianCalendar继承了Calendar类。本节将在介绍类GregorianCalendar的同时顺带介绍Calendar类中的相关方法。
  类GregorianCalendar提供了七种构造函数:
  (1)public GregorianCalendar()
  创建的对象中的相关值被设置成指定时区,缺省地点的当前时间,即程序运行时所处的时区、地点的当前时间。
  (2)public GregorianCalendar(TimeZone zone)
  创建的对象中的相关值被设置成指定时区zone,缺省地点的当前时间。
  (3)public GregorianCalendar(Locale aLocale)
  创建的对象中的相关值被设置成缺省时区,指定地点aLocale的当前时间。
  (4)public GregorianCalendar(TimeZone zone,Local aLocale)
  创建的对象中的相关值被设置成指定时区,指定地点的当前时间。
  上面使用到的类TimeZone的性质如下:
  TimeZone是java.util包中的一个类,其中封装了有关时区的信息。每一个时区对应一组ID。类TimeZone提供了一些方法完成时区与对应ID两者之间的转换。
  (Ⅰ)已知某个特定的ID,可以调用方法
  public static synchronized TimeZone getTimeZone(String ID)
来获取对应的时区对象。
  例 太平洋时区的ID为PST,用下面的方法可获取对应于太平洋时区的时区对象:
  TimeZone tz=TimeZone.getTimeZone("PST");
  调用方法getDefault()可以获取主机所处时区的对象。
  TimeZone tz=TimeZone.getDefault();
  (Ⅱ)调用以下方法可以获取时区的ID
  ■public static synchronized String[] getavailableIDs(int rawOffset)
  根据给定时区偏移值获取ID数组。同一时区的不同地区的ID可能不同,这是由于不同地区对是否实施夏时制意见不统一而造成的。
  例String s[]=TimeZone.getAvailableIDs(-7*60*60*1000);
  打印s,结果为s[0]=PNT,s[1]=MST
  ■public static synchronized String[] getAvailableIDs()
  获取提供的所有支持的ID。
  ■public String getID()
  获取特定时区对象的ID。
  例 TimeZone tz=TimeZone.getDefault();
  String s=tz.getID();
  打印s,结果为s=CTT。
  上面使用类的对象代表了一个特定的地理、政治或文化区域。Locale只是一种机制,它用来标识一类对象,Local本身并不包含此类对象。
  要获取一个Locale的对象有两种方法:
  (Ⅰ)调用Locale类的构造方法
  Locale(String language,String country)
  Locale(String language,String country,String variant)
  参数说明:language??在ISO-639中定义的代码,由两个小写字母组成。
       country??在ISO-3166中定义的代码,由两个大写字母组成。
       variant??售货商以及特定浏览器的代码,例如使用WIN代表Windows。
  (Ⅱ)调用Locale类中定义的常量
  Local类提供了大量的常量供用户创建Locale对象。
  例 Locale.CHINA
    为中国创建一个Locale的对象。
  类TimeZone和类Locale中的其它方法,读者可查阅API。
  (5)public GregorianCalendar(int year,int month,int date)
  (6)public GregorianCalendar(int year,int month,int date,int hour,int minute)
  (7)public GregorianCalendar(int year,int month,int date,int hour,int minute,int second)
  用给定的日期和时间创建一个GregorianCalendar的对象。
  参数说明:
  year-设定日历对象的变量YEAR;month-设定日历对象的变量MONTH;
  date-设定日历对象的变量DATE;hour-设定日历对象的变量HOUR_OF_DAY;
  minute-设定日历对象的变量MINUTE;second-设定日历对象的变量SECOND。
  与Date类中不同的是year的值没有1900这个下限,而且year的值代表实际的年份。month的含义与Date类相同,0代表1月,11代表12月。
  例 GregorianCalendar cal=new GregorianCalendar(1991,2,4)
  cal的日期为1991年3月4号。
  除了与Date中类似的方法外,Calendar类还提供了有关方法对日历进行滚动计算和数学计算。计算规则由给定的日历系统决定。进行日期计算时,有时会遇到信息不足或信息不实等特殊情况。Calendar采取了相应的方法解决这些问题。当信息不足时将采用缺省设置,在GregorianCalendar类中缺省设置一般为YEAR=1970,MONTH=JANUARY,DATE=1。
  当信息不实时,Calendar将按下面的次序优先选择相应的Calendar的变量组合,并将其它有冲突的信息丢弃。
  MONTH+DAY_OF_MONTH
  MONTH+WEEK_OF_MONTH+DAY_OF_WEEK
  MONTH+DAY_OF_WEEK_OF_MONTH+DAY_OF_WEEK
  DAY_OF+YEAR
  DAY_OF_WEEK_WEEK_OF_YEAR
  HOUR_OF_DAY

8.4 随机数类Random

  Java实用工具类库中的类java.util.Random提供了产生各种类型随机数的方法。它可以产生int、long、float、double以及Goussian等类型的随机数。这也是它与java.lang.Math中的方法Random()最大的不同之处,后者只产生double型的随机数。
  类Random中的方法十分简单,它只有两个构造方法和六个普通方法。
  构造方法:
  (1)public Random()
  (2)public Random(long seed)
  Java产生随机数需要有一个基值seed,在第一种方法中基值缺省,则将系统时间作为seed。
  普通方法:
  (1)public synonronized void setSeed(long seed)
  该方法是设定基值seed。
  (2)public int nextInt()
  该方法是产生一个整型随机数。
  (3)public long nextLong()
  该方法是产生一个long型随机数。
  (4)public float nextFloat()
  该方法是产生一个Float型随机数。
  (5)public double nextDouble()
  该方法是产生一个Double型随机数。
  (6)public synchronized double nextGoussian()
  该方法是产生一个double型的Goussian随机数。
  例8.2 RandomApp.java。
  //import java.lang.*;
  import java.util.Random;

  public class RandomApp{
   public static void main(String args[]){
    Random ran1=new Random();
    Random ran2=new Random(12345);
    //创建了两个类Random的对象。
    System.out.println("The 1st set of random numbers:");
    System.out.println("\t Integer:"+ran1.nextInt());
    System.out.println("\t Long:"+ran1.nextLong());
    System.out.println("\t Float:"+ran1.nextFloat());
    System.out.println("\t Double:"+ran1.nextDouble());
    System.out.println("\t Gaussian:"+ran1.nextGaussian());
    //产生各种类型的随机数
    System.out.print("The 2nd set of random numbers:");
    for(int i=0;i<5;i++){
     System.out.println(ran2.nextInt()+" ");
     if(i==2) System.out.println();
     //产生同种类型的不同的随机数。
     System.out.println();//原文如此
    }
   }
  }

  运行结果:
  E:\java01>java RandomApp
  The 1st set of random numbers:
    Integer:-173899656
    Long:8056223819738127077
    Float:0.6293638
    Double:0.7888394520265607
    Gaussian:0.5015701094568733
  The 2nd set of random numbers:1553932502
  -2090749135
  -287790814
  -355989640
  -716867186
  E:\java01>

8.5 向量类Vector

  Java.util.Vector提供了向量(Vector)类以实现类似动态数组的功能。在Java语言中。正如在一开始就提到过,是没有指针概念的,但如果能正确灵活地使用指针又确实可以大大提高程序的质量,比如在C、C++中所谓“动态数组”一般都由指针来实现。为了弥补这点缺陷,Java提供了丰富的类库来方便编程者使用,Vector类便是其中之一。事实上,灵活使用数组也可完成向量类的功能,但向量类中提供的大量方法大大方便了用户的使用。
  创建了一个向量类的对象后,可以往其中随意地插入不同的类的对象,既不需顾及类型也不需预先选定向量的容量,并可方便地进行查找。对于预先不知或不愿预先定义数组大小,并需频繁进行查找、插入和删除工作的情况,可以考虑使用向量类。
  向量类提供了三种构造方法:
  public vector()
  public vector(int initialcapacity,int capacityIncrement)
  public vector(int initialcapacity)
  使用第一种方法,系统会自动对向量对象进行管理。若使用后两种方法,则系统将根据参数initialcapacity设定向量对象的容量(即向量对象可存储数据的大小),当真正存放的数据个数超过容量时,系统会扩充向量对象的存储容量。参数capacityIncrement给定了每次扩充的扩充值。当capacityIncrement为0时,则每次扩充一倍。利用这个功能可以优化存储。
  在Vector类中提供了各种方法方便用户使用:
  ■插入功能
  (1)public final synchronized void addElement(Object obj)
  将obj插入向量的尾部。obj可以是任何类的对象。对同一个向量对象,可在其中插入不同类的对象。但插入的应是对象而不是数值,所以插入数值时要注意将数值转换成相应的对象。
  例 要插入一个整数1时,不要直接调用v1.addElement(1),正确的方法为:
  Vector v1=new Vector();
  Integer integer1=new Integer(1);
  v1.addElement(integer1);
  (2)public final synchronized void setElementAt(object obj,int index)
  将index处的对象设成obj,原来的对象将被覆盖。
  (3)public final synchronized void insertElementAt(Object obj,int index)
  在index指定的位置插入obj,原来对象以及此后的对象依次往后顺延。
  ■删除功能
  (1)public final synchronized void removeElement(Object obj)
  从向量中删除obj。若有多个存在,则从向量头开始试,删除找到的第一个与obj相同的向量成员。
  (2)public final synchronized void removeAllElement()
  删除向量中所有的对象。
  (3)public final synchronized void removeElementlAt(int index)
  删除index所指的地方的对象。
  ■查询搜索功能
  (1)public final int indexOf(Object obj)
  从向量头开始搜索obj ,返回所遇到的第一个obj对应的下标,若不存在此obj,返回-1。
  (2)public final synchronized int indexOf(Object obj,int index)
  从index所表示的下标处开始搜索obj。
  (3)public final int lastIndexOf(Object obj)
  从向量尾部开始逆向搜索obj。
  (4)public final synchronized int lastIndexOf(Object obj,int index)
  从index所表示的下标处由尾至头逆向搜索obj。
  (5)public final synchronized Object firstElement()
  获取向量对象中的首个obj。
  (6)public final synchronized Object lastelement()
  获取向量对象中的最后一个obj。
  了解了向量的最基本的方法后,我们来看一下例8.3VectorApp.java。
  例8.3 VectorApp.java。
  import java.util.Vector;
  import java.lang.*;//这一句不应该要,但原文如此
  import java.util.Enumeration;
  public class VectorApp{
   public static void main(String[] args){
    Vector v1=new Vector();
    Integer integer1=new Integer(1);
    v1.addElement("one");
    //加入的为字符串对象
    v1.addElement(integer1);
    v1.addElement(integer1);
    //加入的为Integer的对象
    v1.addElement("two");
    v1.addElement(new Integer(2));
    v1.addElement(integer1);
    v1.addElement(integer1);
    System.out.println("The vector v1 is:\n\t"+v1);
    //将v1转换成字符串并打印
    v1.insertElementAt("three",2);
    v1.insertElementAt(new Float(3.9),3);
    System.out.println("The vector v1(used method insertElementAt()) is:\n\t "+v1);
    //往指定位置插入新的对象,指定位置后的对象依次往后顺延
    v1.setElementAt("four",2);
    System.out.println("The vector v1(used method setElementAt()) is:\n\t "+v1);
    //将指定位置的对象设置为新的对象
    v1.removeElement(integer1);
    //从向量对象v1中删除对象integer1由于存在多个integer1所以从头开始
    //找,删除找到的第一个integer1
    Enumeration enum=v1.elements();
    System.out.print("The vector v1(used method removeElement())is:");
    while(enum.hasMoreElements())
    System.out.print(enum.nextElement()+" ");
    System.out.println();
    //使用枚举类(Enumeration)的方法来获取向量对象的每个元素
    System.out.println("The position of object 1(top-to-bottom):"
     + v1.indexOf(integer1));
    System.out.println("The position of object 1(tottom-to-top):"
     +v1.lastIndexOf(integer1));
    //按不同的方向查找对象integer1所处的位置
    v1.setSize(4);
    System.out.println("The new vector(resized the vector)is:"+v1);
    //重新设置v1的大小,多余的元素被行弃
   }
  }
  运行结果:
  E:\java01>java VectorApp
  The vector v1 is:
     [one, 1, 1, two, 2, 1, 1]
  The vector v1(used method insertElementAt()) is:
     [one, 1, three, 3.9, 1, two, 2, 1, 1]
  The vector v1(used method setElementAt()) is:
     [one, 1, four, 3.9, 1, two, 2, 1, 1]
  The vector v1(used method removeElement())is:one four 3.9 1 two 2 1 1
  The position of object 1(top-to-bottom):3
  The position of object 1(tottom-to-top):7
  The new vector(resized the vector)is:[one, four, 3.9, 1]
  E:\java01>
  从例8.3运行的结果中可以清楚地了解上面各种方法的作用,另外还有几点需解释。
  (1)类Vector定义了方法
  public final int size()
  此方法用于获取向量元素的个数。它的返回值是向是中实际存在的元素个数,而非向量容量。可以调用方法capactly()来获取容量值。
  方法:
  public final synchronized void setsize(int newsize)
  此方法用来定义向量大小。若向量对象现有成员个数已超过了newsize的值,则超过部分的多余元素会丢失。
  (2)程序中定义了Enumeration类的一个对象
  Enumeration是java.util中的一个接口类,在Enumeration中封装了有关枚举数据集合的方法。
  在Enumeration中提供了方法hawMoreElement()来判断集合中是束还有其它元素和方法nextElement()来获取下一个元素。利用这两个方法可以依次获得集合中元素。
  Vector中提供方法:
  public final synchronized Enumeration elements()
  此方法将向量对象对应到一个枚举类型。java.util包中的其它类中也大都有这类方法,以便于用户获取对应的枚举类型。

8.6 栈类Stack

  Stack类是Vector类的子类。它向用户提供了堆栈这种高级的数据结构。栈的基本特性就是先进后出。即先放入栈中的元素将后被推出。Stack类中提供了相应方法完成栈的有关操作。
  基本方法:
  public Object push(Object Hem)
  将Hem压入栈中,Hem可以是任何类的对象。
  public Object pop()
  弹出一个对象。
  public Object peek()
  返回栈顶元素,但不弹出此元素。
  public int search(Object obj)
  搜索对象obj,返回它所处的位置。
  public boolean empty()
  判别栈是否为空。
  例8.4 StackApp.java使用了上面的各种方法。
  例8.4 StackApp.java。
  import java.lang.*;
  import java.util.*;
  public class StackApp{
   public static void main(String args[]){
    Stack sta=new Stack();
    sta.push("Apple");
    sta.push("banana");
    sta.push("Cherry");
    //压入的为字符串对象
    sta.push(new Integer(2));
    //压入的为Integer的对象,值为2
    sta.push(new Float(3.5));
    //压入的为Float的对象,值为3.5
    System.out.println("The stack is,"+sta);
    //对应栈sta
    System.out.println("The top of stack is:"+sta.peek());
    //对应栈顶元素,但不将此元素弹出
    System.out.println("The position of object Cherry is:"
    +sta.search("cherry"));
    //打印对象Cherry所处的位置
    System.out.print("Pop the element of the stack:");
    while(!sta.empty())
    System.out.print(sta.pop()+" ");
    System.out.println();
    //将栈中的元素依次弹出并打印。与第一次打印的sta的结果比较,可看出栈
    //先进后出的特点
   }
  }
  运行结果(略)


8.7 哈希表类Hashtable

  哈希表是一种重要的存储方式,也是一种常见的检索方法。其基本思想是将关系码的值作为自变量,通过一定的函数关系计算出对应的函数值,把这个数值解释为结点的存储地址,将结点存入计算得到存储地址所对应的存储单元。检索时采用检索关键码的方法。现在哈希表有一套完整的算法来进行插入、删除和解决冲突。在Java中哈希表用于存储对象,实现快速检索。
  Java.util.Hashtable提供了种方法让用户使用哈希表,而不需要考虑其哈希表真正如何工作。
  哈希表类中提供了三种构造方法,分别是:
  public Hashtable()
  public Hashtable(int initialcapacity)
  public Hashtable(int initialCapacity,float loadFactor)
  参数initialCapacity是Hashtable的初始容量,它的值应大于0。loadFactor又称装载因子,是一个0.0到0.1之间的float型的浮点数。它是一个百分比,表明了哈希表何时需要扩充,例如,有一哈希表,容量为100,而装载因子为0.9,那么当哈希表90%的容量已被使用时,此哈希表会自动扩充成一个更大的哈希表。如果用户不赋这些参数,系统会自动进行处理,而不需要用户操心。
  Hashtable提供了基本的插入、检索等方法。
  ■插入
  public synchronized void put(Object key,Object value)
给对象value设定一关键字key,并将其加到Hashtable中。若此关键字已经存在,则将此关键字对应的旧对象更新为新的对象Value。这表明在哈希表中相同的关键字不可能对应不同的对象(从哈希表的基本思想来看,这也是显而易见的)。
  ■检索
  public synchronized Object get(Object key)
  根据给定关键字key获取相对应的对象。
  public synchronized boolean containsKey(Object key)
  判断哈希表中是否包含关键字key。
  public synchronized boolean contains(Object value)
  判断value是否是哈希表中的一个元素。
  ■删除
  public synchronized object remove(object key)
  从哈希表中删除关键字key所对应的对象。
  public synchronized void clear()
  清除哈希表
  另外,Hashtalbe还提供方法获取相对应的枚举集合:
  public synchronized Enumeration keys()
  返回关键字对应的枚举对象。
  public synchronized Enumeration elements()
  返回元素对应的枚举对象。
  例8.5 Hashtable.java给出了使用Hashtable的例子。
  例8.5 Hashtalbe.java。
  //import java.lang.*;
  import java.util.Hashtable;
  import java.util.Enumeration;
  public class HashApp{
   public static void main(String args[]){
    Hashtable hash=new Hashtable(2,(float)0.8);
    //创建了一个哈希表的对象hash,初始容量为2,装载因子为0.8

    hash.put("Jiangsu","Nanjing");
    //将字符串对象“Jiangsu”给定一关键字“Nanjing”,并将它加入hash
    hash.put("Beijing","Beijing");
    hash.put("Zhejiang","Hangzhou");

    System.out.println("The hashtable hash1 is: "+hash);
    System.out.println("The size of this hash table is "+hash.size());
    //打印hash的内容和大小

    Enumeration enum1=hash.elements();
    System.out.print("The element of hash is: ");
    while(enum1.hasMoreElements())
     System.out.print(enum1.nextElement()+" ");
    System.out.println();
    //依次打印hash中的内容
    if(hash.containsKey("Jiangsu"))
     System.out.println("The capatial of Jiangsu is "+hash.get("Jiangsu"));
    hash.remove("Beijing");
    //删除关键字Beijing对应对象
    System.out.println("The hashtable hash2 is: "+hash);
    System.out.println("The size of this hash table is "+hash.size());
   }
  }

  运行结果:
  The hashtable hash1 is: {Beijing=Beijing, Zhejiang=Hangzhou, Jiangsu=Nanjing}
  The size of this hash table is 3
  The element of hash is: Beijing Hangzhou Nanjing
  The capatial of Jiangsu is Nanjing
  The hashtable hash2 is: {Zhejiang=Hangzhou, Jiangsu=Nanjing}
  The size of this hash table is 2

  Hashtable是Dictionary(字典)类的子类。在字典类中就把关键字对应到数据值。字典类是一个抽象类。在java.util中还有一个类Properties,它是Hashtable的子类。用它可以进行与对象属性相关的操作。

8.8 位集合类BitSet

  位集合类中封装了有关一组二进制数据的操作。
  我们先来看一下例8.6 BitSetApp.java。
  例8.6 BitSetApp.java
  //import java.lang.*;
  import java.util.BitSet;
  public class BitSetApp{
   private static int n=5;
   public static void main(String[] args){
    BitSet set1=new BitSet(n);
    for(int i=0;i<n;i++) set1.set(i);
    //将set1的各位赋1,即各位均为true
    BitSet set2= new BitSet();
    set2=(BitSet)set1.clone();
    //set2为set1的拷贝
    set1.clear(0);
    set2.clear(2);
    //将set1的第0位set2的第2位清零
    System.out.println("The set1 is: "+set1);
    //直接将set1转换成字符串输出,输出的内容是set1中值true所处的位置
    //打印结果为The set1 is:{1,2,3,4}
    System.out.println("The hash code of set2 is: "+set2.hashCode());
    //打印set2的hashCode
    printbit("set1",set1);
    printbit("set2",set2);
    //调用打印程序printbit(),打印对象中的每一个元素
    //打印set1的结果为The bit set1 is: false true true true true
    set1.and(set2);
    printbit("set1 and set2",set1);
    //完成set1 and set2,并打印结果
    set1.or(set2);
    printbit("set1 or set2",set1);
    //完成set1 or set2,并打印结果
    set1.xor(set2);
    printbit("set1 xor set2",set1);
    //完成set1 xor set2,并打印结果
   }
   //打印BitSet对象中的内容
   public static void printbit(String name,BitSet set){
    System.out.print("The bit "+name+" is: ");
    for(int i=0;i<n;i++)
     System.out.print(set.get(i)+" ");
    System.out.println();
   }
  }

  运行结果:
  The set1 is: {1, 2, 3, 4}
  The hash code of set2 is: 1225
  The bit set1 is: false true true true true
  The bit set2 is: true true false true true
  The bit set1 and set2 is: false true false true true
  The bit set1 or set2 is: true true false true true
  The bit set1 xor set2 is: false false false false false

  程序中使用了BitSet类提供的两种构造方法:
    public BitSet();
    public BitSet(int n);
  参数n代表所创建的BitSet类的对象的大小。BitSet类的对象的大小在必要时会由系统自动扩充。
  其它方法:
  public void set(int n)
  将BitSet对象的第n位设置成1。
  public void clear(int n)
  将BitSet对象的第n位清零。
  public boolean get(int n)
  读取位集合对象的第n位的值,它获取的是一个布尔值。当第n位为1时,返回true;第n位为0时,返回false。
  另外,如在程序中所示,当把一BitSet类的对象转换成字符串输出时,输出的内容是此对象中true所处的位置。
  在BitSet中提供了一组位操作,分别是:
  public void and(BitSet set)
  public void or(BitSet set)
  public void xor(BitSet set)
利用它们可以完成两个位集合之间的与、或、异或操作。
  BitSet类中有一方法public int size()来取得位集合的大小,它的返回值与初始化时设定的位集合大小n不一样,一般为64。

小结

  本章我们介绍了Java的实用工具类库java.util中一些常用的类。java.util包中还有其它一些类。它们的具体用法用户可以自行查阅API。在下一章,我们将学习利用java.awt包进行窗口程序设计。在下章的例子中我们将使用到本章介绍的各种类。

posted @ 2006-02-13 09:00 呓语的博客 阅读(162) | 评论 (0)编辑 收藏

(前言)
根据上一讲,你或许大概已经了解了Eclipse的组成,以及大致的运行机理. 这篇文章, 将
开始带您使用eclipse.

(正文)

[Eclipse的工作台使用指南]

这部分要写的话,其实要写很多,而且最好方式则是图文并茂,最好再有演示. 我这里只是
给一些总体的介绍,并给予一些使用上的指导. 个人感觉,如果你从来

没碰过eclipse, 启动之后,最好先看一下help, 这样会比较好.

具体的操作步骤是这样的, 启动eclipse后, 选 菜单里的help-> help contents, 此时
会弹出一个新的窗口,就是eclipse的帮助窗口, 这个窗口的左边是一个导航

条,选择Workbench User Guide, 里面分为Getting Started, Concepts, Tasks 和Refer
ence. 可以先看一下 Getting Started里面的Basic Tutorial. 这份

tutorial可以在最短的时间内,让你熟悉eclipse的工作台.

其实,eclipse平台ui方面有这几个组件: 菜单, 工具栏, 视图, 透视图, 编辑器

菜单和工具栏不用说了, 地球人都知道的, 视图就是view, 比如 Navigator, Outline,
Tasks 等等都是视图, 每一个视图都有自己相应的功能,你可以参看

workbench user guide来了解这些视图. 编辑器,就是editor, 比如有开发java的编辑器
, 编写文本的编辑器,等等, 最后还有一个叫透视图, 英文是perspective,

这个东东其实是不同的view,menu,toolbar,editor的排列组合. 比如你开发java, 你经
常会用到package explorer, tasks, outline等view和编写java的editor,

以及适合开发java的菜单(Source和Refactor), 那它就会布局一个适合开发java的透视
图, 以此类推.

下面我介绍一下常用菜单项:
+ File
- New: 新建文件,项目,或者其他都是从这里进入

- Import: 这个也是很有用的,比如别人开发的eclipse项目,你copy到
你机器上,可以通过import把这个项目导入工作区

- Export: 这个是导出功能, 比如你开发了一个项目,最后想导出一个
运行库,jar包之类的都可以用这个,这样你就不用自己手动的去把那些class

文件打包了

+ Window
- New Window: 你如果觉得在一个window下开发东西太挤的话,可以再开一
个,等于冒出两个workbench,其实操作的resource都是一样的.

- Open Perspective: eclipse有很多透视图如resource, java, java
browsing, cvs, debug等等,你可以根据当前开发的需要,选择你要的透视图进行开发

- Open View: 透视图毕竟有限,不可能把所有的view都帮你开好, 所以你
如果发觉你要用某一个view,但是它没有开,就用这个选项

- Customize Perspective: 毕竟每个人都有自己的习惯, 你觉得这个透视图用
的不爽, 可以自己定制的

- Reset Perspective: 给你定制的一塌糊涂, 唉, 没办法, 还原成老样子把,
就用这个

- Preference: 这个是非常重要的选项, 偶是没事就进去改的, 它保存了
很多配置方面的东西, 比如字体, 快捷键设置, 很多很多方面都要用到这个

的, 这个东西的详细介绍, 会稍后介绍.

+ Help
- Welcome: eclipse很多插件都做了welcome page,这个page对很多初学
者来说,很有用的,否则很多情况下,新的东西是无从下手的.

- Help Content: 前面讲过了,用来启动帮助系统

- Update: 这个以后会具体介绍

- About Platform: 你可以从这里了解你装了哪些features和plugins


[利用 eclipse 开发简单的 java 程序]

好了,我们一起step by step来学一下把, 很easy的

1. 菜单 new -> project, 然后在new project 对话框里选 java ->java project, 按n
ext 按钮
2. 输入 project 的名字, 按 next 按钮, 当然如果你不想把项目的根目录建在默认的
地方,也可以取消掉use default ,然后自己设定目录
3. 之后就 finish 把, 都用默认配置
4. 如果你没有在java透视图下面,它会提示你是否跳到java透视图,选择是
5. ok 一个项目就建好了
6. 之后,你就可以 new class 开始写java程序了
7. 比如你的new一个class,如下
public class A{
    public static void main(String args[]){
        System.out.println("Hello World");
    }
}

8. 编辑好保存, 然后跳到菜单 -> run -> run as -> java application
9. 你可以看到console view中就冒出Hello World了

很easy把, 当然, 你会在开发中会遇到很多问题, 这是必然的, 那这些问题只能在实践
中积累才能得到解决, 所以不用急,多用用,多玩玩,遇到问题经常到版上来问

问.

还有如果你发现问题的话, 找问题解决方案的第一个地方,应该是eclipse的帮助系统里
的Java Development User Guide, 几乎绝大多数问题,上面都有答案. 所以

有空的话还是要多读一读.

不过,很多人都说用了eclipse之后,就抛弃了其他的java ide, 说明它必有爽的地方, 那
我来介绍一下用eclipse 开发java,有哪些爽的地方


[Java Development Tool (JDT) 特色]

其实特色有很多,我也只是凭我的开发经验, 介绍一下jdt的突出功能.

1. 自动修饰代码功能
这个功能很大程度上, 把平时一些开发代码中的琐碎的工作给自动化了

打开菜单
+ Source
- Comment: 这个比较有用,比如你写java代码,发觉你有一段代码要注
释掉,那就选中那一块代码,然后选这项,他就会自动把这段代码注释掉. 快捷

健是 "Ctrl+/"

- Uncomment: 反注释, 操作方法和Comment差不多, 快捷键是 "Ctrl+\"

- Format: 这个操作项是我一直推荐的, 非常方便, 比如你写了段格
式很烂的代码, 乱七八糟的, 那你就选择这项,你会发觉, 哇~~, 我的代码怎

么一下子变漂亮了, 不信你可以试试, 快捷键是"Ctrl+Shift+F", 所以我现在都养成习
惯了,写一会儿代码,就c+s+f一下, 呵呵,很方便的.

- Sort Member: 这个不是很常用,但是如果你觉得代码太长,老是找不到函
数,,也可以试试. 他会帮你把你写的函数,变量重新排序

- Organize Import: 这个功能也是一个不用不爽的功能, 比如你编一个项目,
发现有很多import都没有用到,或者说你引用了一个类,但是你没有import,

结果编译不通过, 那都没关系, 一用这个,所有的问题迎刃而解. 至少我用这个之后, 就
从来没有写过import这类语句了. 快捷键是"Ctrl+Shift+O(是字母O,不是数

字0)", 我经常把这个和c+s+f一起用, 呵呵,人也变懒了不少

- Override/Implement Method: 这个比如你写一个类, 实现了某一个接口,但是
你还没有实现那个接口的函数, 那就用这个,它会自动搜索父类和接口的方

法,你可以选择要覆盖还是实现哪些函数

- Generate Getter and Setter: 如果你加了一个类变量, 要为它写getter和se
tter,不用那么麻烦, 用这个把,都是自动的

- Generate Delegating Method: 如果某一个field要生成代理函数,用这个把,
选一下就ok

- Add Javadoc Comment: 点中某一个你想要加javadoc的函数或类或变量,然后
选这项,它会自动帮你加好javadoc的头,包括你用的那些param或return

- surround with try/catch block: 比如你有一段代码要处理某些exception,
可你又忘了用try/catch来写,别急,选中那段代码,然后选这项,你会发觉它会

自动针测你这段代码里要抛出哪些exception,并且自动生成好所有代码

- externalize string: 这个是在做国际化的时候用的,简单的说,就是把string
包在resource bundle里, 这也是i18n的一个解决方案,我想我以后会详细介

绍这方面的东西.

2. 重构功能:
重构这两年很热, 那让我们看看jdt里面的重构到底有多强. 呵呵, 这也是eclipse最吸
引我的一个地方.

打开菜单
+ Refactor
- Rename: 如果你写了一个类,你发觉这个类某个类变量的名字起的不太好听
, 你觉得不爽, 于是你就想改名字, 那怎么改呢, 就把那个变量名改了

? 呵呵,没那么简单,因为你这个变量如果已经在某些函数里引用到,那编译要出错了, 而
且你根本就不知道你哪里引用了, 写了那么多代码,脑子都晕了, 那不是死

菜了吗? 别紧张, 用这个rename可以帮你解决一切问题, 它不仅可以帮你把变量的名字
改了,而且它还会自动搜寻所有这个变量被引用到的地方,然后把那些地方也

一起改了, 爽不爽啊, 给你省了很大的劳动力不是.

- Move: 同样, 要移动一个实现了的静态函数或变量到别的类的话,
用这个移动, 保证不出错

- Modify Method Signature: 你设计函数不可能一下子就定型的,比如你一开始这
个函数有一个参数,后来发觉不对,要用两个, 那你就要用这个来改, 这

样它还会搜寻所有已经引用这个函数的地方, 并且把这些应用的地方也改掉,否则编译也
要出错的

- Extract Interface: 它可以帮你把一个类抽象成一个接口, 规范你的代码

- Extract Method: 如果你写了一段很长的函数, 但是这个函数有些代码有
重复利用性, 你就可以把给分割出来, 选中那段代码,然后选这项,系统会问

你抽出来的函数的定义, 然后它就会生成这段函数,把实现从原来函数那里抽出来,并在
原来函数那里写一个对这个新函数的引用,以保证程序不变性.

- Extract Local Variable: 如果你发觉你函数里有些值都是通过一样的表达
式得到的,你就可以通过这项把这段表达式变成一个变量,并且把这个变量替

换到引用到表达式的地方

- Extract Constant: 抽取常量, 比如一个string= "eclipse", 你觉得很多
地方要用到, 那就抽出来变成一个常量 ECLIPSE, 就这么简单

- convert local variable to field : 这个看名字就知道, 不多说了

- encapsulate field: 这个和生成getter,setter有点类似, 但是不同的在于,
如果你有一个public的常量 var,并且已经在别处引用到了, 那你用生成

getter,setter肯定有问题, 因为引用的地方没改过来, 用这个的话,不仅会生成getter,
setter,而且还会改掉所有引用的地方, 比如把var = ...;的地方改成

setvar(...), 把 ... = var地方改成 ... = getvar(); , 呵呵,够强把...


3. 敏感帮助:

这个jb之类的ide也有, 启动方式为"alt+/" , 你如果觉得这个不爽,可以选preference-
>workbench->keys->edit->content assist 修改键值
而且,这个敏感帮助还有一个强的地方在于:
你如果想写一个for语句, 呵呵, 就打 for, 然后alt+/, 选一个for的生成方式, 一个完
成的for语句就出来了, 呵呵,eclipse多用用, 人都会变懒的
这个功能其实是jdt的模板功能,你也可以加自己用的模板, 具体在 preference->
java -> editor ->templates 加

4. Quick Fix功能:

比如我们来编一个类A, 如下:
public class A {
    
    public static void main(String args[]){
        System.out.prin("Hello World");
                   
    }
}

编译是不通过的,print方法打错了, 打成prin了

所有你会发觉那一行的左边有个红差差,说明这行有错误,你把鼠标移到那个红差差上,它
会有一个提示出来,告诉你出了什么错了

这还不止,你还发觉在红差差左边有个电灯泡, 你点那个电灯泡,它会弹出框问你是chang
e to print 还是change to println, 选择change to print, 它就自动帮

你改好了,呵呵,连改错都这么方便,太爽了. 当然,如果没有电灯泡的话,你就只能手动改
了,毕竟这东西没这么智能,可以帮你自动解决所有的问题

posted @ 2006-01-18 11:31 呓语的博客 阅读(171) | 评论 (0)编辑 收藏

(前言)
今天这篇侧重于eclipse的内部结构剖析,对于想开发插件的同志们,这些都是基础知识,
可以好好看看. 由于我写这个东西,也是随性发挥,想到什么就写什么. 而关于eclipse的
如何使用,如何用eclipse来开发一个java项目或其他项目之类的文章, 我想我会在以后
的文章中写到. 但我想对于要在开发eclipse上开发web项目,c项目或其他的话,你也要会
自己能安装相应的插件,才能开发,所以,这些基础知识的对于这些人来说还是很有必要的
.

(正文)

上一回我们且说到eclipse的下载,安装,启动. 其中讲到eclipse目录结构时,你会发现有
两个目录,一个叫plugins,一个叫features,而且你会发觉就这两个目录就占了整个eclip
se项目的9x%的空间,如果少了这两个目录,呵呵,eclipse根本就是空架子. 那这里面到底
存放了些什么东西呢, 让我们来研究一下.

[什么是 plug-in]

我们来做一个比喻, 你买了一套新房子,买过来是毛坯房,然后你稍微装修一下,铺了地板
,上了墙纸, 当然现在大家都要用家用电器, 没电咋行, 所以我们就要布好电线,装好电
源插座. 这个时候,你可以把这个房子想像成eclipse这个平台. 之后, 我们或许就要添
置家用电器了,比如电视, 音响等等, 等我们买好回家, 然后把电源往插座上一插, 那我
们就抱着孩子, 搂着老婆, 看电视, 听音乐, 舒舒服服的过上幸福美满的小生活了~~~

同样的, eclipse的plug-in 也是同样的工作原理, plug-in 只要放到/p
lugins目录下, eclipse启动后就会自动给所有在这个目录下的plug-ins, 通上电, 那这
些plug-ins就会自动的运行起来, 美妙的eclipse界面也随之呈现在你眼前.

那接下来,让我们看看eclipse这个由插件组成的平台,到底是个什么样的架构

[Eclipse 平台架构]


---------------------------------------------
| Eclipse Platform | --------
| | ---\ | |
| ----------------------- ------------ |==| _ |______| JDT |
| |Workbench | | | |==| | | |
| | | | | | ---/ --------
| | | | | |
| | ---------------| | Help | |
| | | jface | | | |
| |----------- | | | |
| | SWT | | | | |
| | | | ------------ |
| ----------------------- |
| ------------ | --------
| ------------------ | | | ---\ | |
| |Workspace | | | |==| _ |______| PDE |
| | | | Team | |==| | | |
| ------------------ | | | ---/ --------
| | | |
| ------------ |
---------------------------------------------


Eclipse Platform 就是一个房子, workbench,jface, swt, workspace, help, team,
jdt, pde都是基于这个平台的插件.

下面我介绍一下这些基础插件的基本功能:

*: workbench用来控制工作台, 负责控制工作台上包括菜单,视图,透视图等等的控制和
操作
*: SWT是一个类似AWT,SWING的java组件,是一个轻量级的组件,而且和awt,swing不同的
是,它底层实现不是基于jre,而是根据不同操作系统,有相应的动态链接库实现,所以作出
来的效果很专业, SWT主要用于workbench的ui绘制
*: jface是基于SWT的一个插件, 对SWT进行了封装, 封装实现了对话框, 视图等东东
*: workspace是用来控制工作区的,(有别于工作台), 包括对工作区内的项目的控制,删
除,添加,编译项目资源等等都由它来控制
*: help是一个eclipse帮助系统, eclipse的菜单->Help-> Help Content,就可以打开这
个帮助系统, 这个系统不是封闭的, 可以进行扩展(以后会介绍做eclipse帮助的插件)
*: team是一个cvs系统,可以和CVS server协调使用,进行版本控制
*: jdt 是 Java Development Tools, 开发java的插件
*: pde 是 plug-in development environment, 开发插件的平台

[plug-in 的基本结构]

每一个plug-in都用一个目录包起来, 而且起目录名也是有讲究的,比如plug-in的名字叫
edu.sjtu.bbs.eclipse,版本是1.0.0, 那这个目录名就是edu.sjtu.bbs.eclipse_1.0.0.

而且随便打开一个plugin目录,可以发现总有一个文件叫一个叫plugin.xml,这个文件对
于plugin来说十分重要, 它相当于定义了plugin的运行参数,没有这个,plugin无法启动,
就像你家的电冰箱如果不知道是用110V还是220V的,你也不敢乱往插座上插,所以总要有
个说明,这个说明就是plugin.xml. 至于这个文件有些什么具体结构,我想在以后介绍编
写插件的时候,我会详细介绍.

[什么是 feature]

feature是功能部件,它里面没有实际的运行的库,它只是eclipse用来管理plugins的一种
途径. 比如你家装了电灯,总要有开关控制把,比如大堂的灯有一个开关控制, 卧室的灯
也有一个开关控制, 它们分别用来控制灯的亮与灭.
同样,功能部件就是用来控制插件的启动与否. eclipse的update透视图可以设定各个功
能部件的启用或禁用状态, 所以你可以通过禁用功能部件,来禁止插件的启动. 这样有一
个好处,比如你装了很多插件在eclipse上,但是装的越多,加载就越多,启动也会变慢, 你
不信的话,可以玩玩wsad, 就知道我说的话不是假的了.
所以,我们可以把功能部件看作是插件或插件集合的开关, 用来控制插件的状态. 如果pl
ugins目录有插件没有被任何一个功能部件包络的话, 我称之为"野插件", 就是eclipse
启动,它也一定会启动, 就相当于没有开关, 电源一直连通一样.

当然,功能部件还有很多其他方面的用处,以后会有详细介绍.

posted @ 2006-01-18 11:30 呓语的博客 阅读(207) | 评论 (0)编辑 收藏

(前言)

Eclipse这个新的东东,大家都很感兴趣,为了帮助初学者揭开Eclipse的神秘面纱,也为了
总结一下自己的使用开发经验,所以打算写下来共享给大家,当然我也是在不断的摸索中,
文中如有不对之处,还望大家指正.

(正文)

Eclipse项目是IBM在2001年捐献的一个开发平台,当时此项目评估价值为40million
USD. 此东东如此值钱,可见自是有过人之处. 接下来直接转入正题, 哪里可以下到Eclip
se呢.

[Eclipse 下载]

下载Eclipse的官方网站:

http://www.eclipse.org/downloads/

这个网站上有很多mirror,你可以根据网路,选择自己最方便的下载mirror
如果你在交大的校园的话,呵呵,那就去这里

ftp://ftp.sjtu.edu.cn/mirror/sites/download.eclipse.org/

这个目录是也是一个Eclipse网站的镜像,但是为什么没有公布在Eclipse.org网站上,这
我就不清楚了
你可以从上述网站上找到Eclipse的下载包
上了网站,你会发现有许多Eclipse版本,有1.x, 2.x, 3.x,那到底该用什么版本呢?

[Eclipse 版本 (以及WSAD版本)]

IBM有个深受大家欢迎的产品,叫做Webshpere Studio Application Developer (WSAD),
当时WSAD4.0发布时用的Eclipse是1.0版本的,后来IBM把Eclipse捐献出来后,就陆续开发
了2.x版本,而且也应用到了后来的WSAD产品,现在WSAD5.0用的是Eclipse 2.0, WSAD
5.1 用的是Eclipse 2.1.1.
现在Eclipse.org Release的最近Release的版本是Eclipse 2.1.3, 相信大家平时要用Ec
lipse做开发的话,用2.1.3比较合适.
同时,如果你如果想尝尝鲜的话,就试试Eclipse 3.0版本,现在3.0还没有Release,
Eclipse 3.0的开发过程是分成10个Milestone来分的,现在已经开发到第8个Milestone,
就是Eclipse 3.0M8. 大家可以试试. 预计3.0的正式版本要到今年6月发布, 相信到时候
发布不久后, WSAD6.0就会发布了. 呵呵,期待啊 @_@

另外对于Eclipse的开发,还有一种版本标记方式,是分别已 N, I, R 打头的, 比如
N20040101之类的, 大家会觉得纳闷,这到底代表什么意思呢. 其实, N 是 Nightly
Build , I 是 Integration Build, R 是 Release Build, 特别是对于大型软件, 有时
编译一个项目要花很多时间, 所以很多时候编译的工作就放在了夜里, 所以就有了
Nightly Build 这一说, 而Nightly版本在经过集成测试,就生成了 Integration 版本.
Integration版本再经过严格的测试,最后就发布Release版本. 所以 稳定性程度: R >
I > N.

相信大家了解了这个版本后,就可以当自己想要的版本了.
等当好了之后,我们就可以安装了

[Eclipse 安装] (以 R2.1.3 版本为例)

当下来的东东是一个压缩包, 然后你要做的就是把这个压缩包解压都某一个目录,为了方
便介绍,我们叫这个目录为.

让我们展开一下这个目录看看:

|
--- /plugins 存放插件的目录 (稍后介绍)
--- /features 存放功能部件的目录 (稍后介绍)
--- /links 其他plugins和features的连接地址的存放目录 (稍后介绍)
--- /readme
--- eclipse.exe 启动Eclipse程序
--- ...


[启动 Eclipse]

但是如果你机器还没有装jre的话,那eclipse还是不能启动的
如果没有的话,去java.sun.com当一个jre,安装好jre之后才能启动
如果你同时有几个jre, 那eclipse会自动搜索注册表,并找到版本高的jre使用.

提示: 如果想指定eclipse使用你想它使用的jre的话,可以设置eclipse.exe的启动参数:

eclipse.exe -vm

然后你就可以启动eclipse了, 慢慢等, 还是挺慢的, 你可以顺便喝口茶, 欣赏欣赏eclipse的splash画面, 呵呵
启动好后, eclipse的开发平台就展现你的眼前了.

在这个启动的时候, eclipse自动创建了一个workspace, 你可以在的目
录下看到一个workspace的目录,这个目录下还有一个.metadata的目录,这个目录存着你
这个启动的工作区的所有配置.

当然,如果你不想把workspace这个工作区目录放到别的目录下,也没有问题,设置一下eclipse的启动参数:
eclipse.exe -data

以后,你如果同时要开发好几个项目, 当时有不想放一个工作区里, 学会启动的不同的工作区是必要的

posted @ 2006-01-18 11:29 呓语的博客 阅读(199) | 评论 (0)编辑 收藏

  • Vector 还是ArrayList,哪一个更好,为什么?

要回答这个问题不能一概而论,有时候使用Vector比较好;有时是ArrayList,有时候这两个都不是最好的选择。你别指望能够获得一个简单肯定答案,因为这要看你用它们干什么。下面有4个要考虑的因素:

(1)API

(2)同步处理

(3)数据增长性

(4)使用模式

下面针对这4个方面进行一一探讨

API
在由Ken Arnold等编著的《Java Programming Language》(Addison-Wesley, June 2000)一书中有这样的描述,Vector类似于ArrayList.。所有从API的角度来看这两个类非常相似。但他们之间也还是有一些主要的区别的。

同步性

Vector是同步的。这个类中的一些方法保证了Vector中的对象是线程安全的。而ArrayList则是异步的,因此ArrayList中的对象并不是线程安全的。因为同步的要求会影响执行的效率,所以如果你不需要线程安全的集合那么使用ArrayList是一个很好的选择,这样可以避免由于同步带来的不必要的性能开销。

数据增长

从内部实现机制来讲ArrayList和Vector都是使用数组(Array)来控制集合中的对象。当你向这两种类型中增加元素的时候,如果元素的数目超出了内部数组目前的长度它们都需要扩展内部数组的长度,Vector缺省情况下自动增长原来一倍的数组长度,ArrayList是原来的50%,所以最后你获得的这个集合所占的空间总是比你实际需要的要大。所以如果你要在集合中保存大量的数据那么使用Vector有一些优势,因为你可以通过设置集合的初始化大小来避免不必要的资源开销。

使用模式

在ArrayList和Vector中,从一个指定的位置(通过索引)查找数据或是在集合的末尾增加、移除一个元素所花费的时间是一样的,这个时间我们用O(1)表示。但是,如果在集合的其他位置增加或移除元素那么花费的时间会呈线形增长:O(n-i),其中n代表集合中元素的个数,i代表元素增加或移除元素的索引位置。为什么会这样呢?以为在进行上述操作的时候集合中第i和第i个元素之后的所有元素都要执行位移的操作。这一切意味着什么呢?

这意味着,你只是查找特定位置的元素或只在集合的末端增加、移除元素,那么使用Vector或ArrayList都可以。如果是其他操作,你最好选择其他的集合操作类。比如,LinkList集合类在增加或移除集合中任何位置的元素所花费的时间都是一样的—O(1),但它在索引一个元素的使用缺比较慢-O(i),其中i是索引的位置.使用ArrayList也很容易,因为你可以简单的使用索引来代替创建iterator对象的操作。LinkList也会为每个插入的元素创建对象,所有你要明白它也会带来额外的开销。

最后,在《Practical Java》一书中Peter Haggar建议使用一个简单的数组(Array)来代替Vector或ArrayList。尤其是对于执行效率要求高的程序更应如此。因为使用数组(Array)避免了同步、额外的方法调用和不必要的重新分配空间的操作。

posted @ 2006-01-16 09:47 呓语的博客 阅读(177) | 评论 (0)编辑 收藏

http://forum.javaeye.com/viewtopic.php?p=17108&highlight=Serializable+generate+SessionImplementor+sessionImplementor%2C#17108

看了看。真是万分高兴,可能别人用了一个星期才解决的问题。我现在就可以不劳而获。惭愧啊。。

由于担心Hibernate自身提供的increment id generator有性能影响,我对increment进行了扩展,改为InMemoryIncrement:

InMemoryIncrement.java

代码:
//Declare Classes Import here,Do not use .* to import all the classes in package.
import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import java.util.Map;
import java.util.HashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import net.sf.hibernate.HibernateException;
import net.sf.hibernate.MappingException;
import net.sf.hibernate.dialect.Dialect;
import net.sf.hibernate.engine.SessionImplementor;
import net.sf.hibernate.type.Type;
import net.sf.hibernate.id.IdentifierGenerator;
import net.sf.hibernate.id.PersistentIdentifierGenerator;
import net.sf.hibernate.id.Configurable;

/**
* Hibernate high performence id generator extend IncrementGenerator.<br>
* This generator use ’select max(column) from table ’ to get id’s init value
* in first time.Then generate increment id in memory .
* <p>
* 原理:
* <li>使用long[]数组在内存中存储每个表的最大值,使用Map存储表名在数组中对应的索引值。
* <li>第一次访问某个表的id的最大值时会从数据库中取出并存放到long数组中,
* 同时在Map中保存索引值
* <li>第二次以后访问会首先从Map中读出索引值,根据索引值把long数组对应的值
* 加上递增步长并返回
* </p>
* @author fenghm
* @version $Revision$
*/
public final class InMemoryIncrement implements IdentifierGenerator, Configurable {

    private static final Log log = LogFactory.getLog(InMemoryIncrement.class);
    
    //存储最大值的数组的容量
    private static final int MAX_CAPACITY = 200;
    
    /**同步锁*/
    private static final Object lock = new Object();

    //存储表存储在数组中的索引值
    private static Map map = new HashMap();
    
    //递增步长,默认加1
    private int step = 1;
    
    //最大值数组
    private static long[] seqs = new long[MAX_CAPACITY];
    
    //最大值数组已经使用的容量
    private static int lastIndex;
    
    private String key;
    private String sql;
    private Connection connection;
    private Class returnClass;
    
    /**
     * (none java doc)
     * @see net.sf.hibernate.id.IdentifierGenerator#
     * generate(net.sf.hibernate.engine.SessionImplementor, java.lang.Object)
     */
    public Serializable generate(SessionImplementor session, Object object)
    throws SQLException, HibernateException {
        connection = session.connection();
        
        long seq = -1;
        
        //找到索引值
        int index = findIndex();
        
        //把最大值加1
        seqs[index] = seqs[index] + step;
        
        seq = seqs[index];
        
        return new Long(seq);
    }

    /**
     * 找到表中自动增长字段存储在数组中的索引值
     * @return 索引值
     */
    private int findIndex(){
        int index = 0;
        
        //首先中缓存中取出索引值
        Integer integer = (Integer)map.get(key);
        
        //如果没有找到就从数据库中读出最大值并进行cache
        if(null == integer){
            //double check lock
            synchronized(lock){
                integer = (Integer)map.get(key);
                if(null == integer){
                    long maxvalue = 1;
                    try{
                        maxvalue = getMaxvalue();
                    }catch(SQLException e){
                        log.error(e);
                    }
                    integer = new Integer(lastIndex++);
                    seqs[integer.intvalue()] = maxvalue;
                    map.put(key,integer);                
                }
            }
        }
        
        index = integer.intvalue();
        return index;
    }    
    
    /**
     * (none java doc)
     * @see net.sf.hibernate.id.Configurable#configure(net.sf.hibernate.type.Type,
     * java.util.Properties, net.sf.hibernate.dialect.Dialect)
     */
    public void configure(Type type, Properties params, Dialect d)
    throws MappingException {
        
        //取出table参数
        String table = params.getProperty("table");
        if (table == null){
            table = params.getProperty(PersistentIdentifierGenerator.TABLE);
        }
        //取出column参数
        String column = params.getProperty("column");
        if (column == null){
            column = params.getProperty(PersistentIdentifierGenerator.PK);
        }
        
        //表的sehcma参数
        String schema = params.getProperty(PersistentIdentifierGenerator.SCHEMA);
        
        returnClass = type.getReturnedClass();
        
        //取出step参数
        String stepvalue = params.getProperty("step");
        if(null != stepvalue && !"".equals(stepvalue.trim())){
            try{
                step = Integer.parseInt(stepvalue);
            }catch(Exception e){
                log.error(e);
            }
        }
        

        //构造存储在Map中的索引值的key name
        key = table + "_$_" + column;
        
        //根据参数构造取最大值的SQL
        sql = "select max(" + column + ") from ";
        if(null != schema){
            sql += schema + ".";
        }
        sql += table;
    }
    
    /**
     * 取指定表中id字段的最大值,不存在记录返回0
     * @return 最大值
     * @throws SQLException if sql error occurs.
     */
    private long getMaxvalue() throws SQLException {
        long maxvalue = 0;
        
        PreparedStatement st = connection.prepareStatement(sql);
        ResultSet rs = null;
        try {
            rs = st.executeQuery();
            if(rs.next()) {
                maxvalue = rs.getLong(1);
            }
            sql = null;
        }finally {
            if (rs != null){
                rs.close();
            }
            st.close();
        }
        return maxvalue;
    }



测试代码:
Data.java
代码:
public class Data {

    private long id;
    private String name;
    private String email;

    /**
     * @return
     */
    public String getEmail() {
        return email;
    }

    /**
     * @return
     */
    public long getId() {
        return id;
    }

    /**
     * @return
     */
    public String getName() {
        return name;
    }

    /**
     * @param string
     */
    public void setEmail(String string) {
        email = string;
    }

    /**
     * @param l
     */
    public void setId(long l) {
        id = l;
    }

    /**
     * @param string
     */
    public void setName(String string) {
        name = string;
    }
}


Data.hbm.xml
代码:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping package="com.kmview.test.hibernate.model">
   <class name="Data" table="data">
      <id name="id" type="long" column="id">
         <generator class="com.kmview.test.hibernate.Generator"/>
      </id>
      <property name="name" type="java.lang.String" length="20" column="name"/>
      <property name="email" type="java.lang.String" column="email"/>
   </class>
</hibernate-mapping>



ThreadMain.java
代码:

public class ThreadMain extends Thread {

    public static void main(String[] args) {
        try{
            Configuration cfg = new Configuration().configure();
            SchemaExport schema = new SchemaExport(cfg);      
            schema.create(false,true);  

            for(int i=0;i<100;i++){
                new ThreadMain().start();
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    
    public void run(){
        try{
            for(int i=0;i<20;i++){
            Session session = HibernateSession.openSession();

            Data data = new Data();
            data.setName("sdfsfsdf");
            data.setEmail("sdflkas;lfdalsfjasljf@d.d");

            session.save(data);
        
            session.flush();
            HibernateSession.closeSession();
            }              
        }catch(Exception e){
            e.printStackTrace();
        }
              
    }
}


HibernateSession.java
代码:
public final class HibernateSession {

    private HibernateSession(){}
    
    //synchronized lock for getSessionFactory
    private static Object lock = new Object();
    
    //common log object
    private static Log log = LogFactory.getLog(HibernateSession.class);
    
    //Hibernate SessionFactory
    private static SessionFactory sessionFactory;
    
    //Implement Hibatenate ThreadLocal pattern
    private static final ThreadLocal session = new ThreadLocal();
    
    
    /**
     * 自动在ClassPath的根路径寻找hibernate.properties与hibernate.cfg.xml文件<br>
     * 并初始化SessionFactory
     * @return SessionFactory
     */
    private static SessionFactory buildSessionFactory(){
        SessionFactory factory = null;
        try{
            Configuration config = new Configuration();
            config = config.configure();
            
            factory = config.buildSessionFactory();
            log.info("ok,SessionFactory builded!");
        }catch(HibernateException e){
            log.error(e);    
        }
        return factory;
    }
      
    /**
     * 取得当前会话的Hibernae Session对象,并打开数据库连接
     * @return Hibernate Session Object for Data Access
     * @exception HibernateException throw HibernateException
     */
    public static Session openSession() throws HibernateException{
        Session s = (Session)session.get();
        if(null == s){
            getSessionFactory();            
            if(null != sessionFactory){
                s = sessionFactory.openSession();
                session.set(s);
            }else{
                log.error("Error,SessionFactory object is not inited!");                  
            }
        }else if(!s.isConnected()){
            s.reconnect();
        }
        return s;
    }
    
    /**
     * 关闭数据库连接,在每次Session用完的时候,应该马上调用closeSession,<br>
     * 以把数据库连接归还到数据库连接池中,提高并发性能。
     */
    public static void closeSession(){
        try{
            Session s = (Session)session.get();
            if(null != s && s.isConnected()){
                s.disconnect();        
            }            
        }catch(HibernateException e ){
            log.error(e);
        }
    }
    
    /**
     * 释放Hibernate Session对象,此方法应该在当前线程消亡之前调用。<br>
     * 例如:在Web应用中可以使用filter统一在最后进行调用<br>
     * <code><pre>
     * public class HibernateFilter extends HttpServlet implements Filter{
     *  private FilterConfig filterConfig;
     *  public void init(FilterConfig filterConfig){
     *     this.filterConfig = filterConfig;
     *  }
     *
     *  public void doFilter(ServletRequest request,ServletResponse response,    
     *                       FilterChain filterChain){
     *     try{
     *        filterChain.doFilter(request,response);
     *     } catch(ServletException sx){
     *        filterConfig.getServletContext().log(sx.getMessage());
     *     } catch(IOException iox){
     *        filterConfig.getServletContext().log(iox.getMessage());
     *     }finally{
     *        HibernateSession.releaseSession();      
     *     }
     *  }
     * }
     * </pre></code>
     * 在Hibernate的API文档提到,Session的close()方法不是必须要被调用的,<br>
     * 但disconnect()方法是必须的;所以此方法也不是必须要被调用的,但是<br>
     * closeSession()方法是必须的。
     * @exception HibernateException throw HibernateException
     */
    public static void releaseSession() throws HibernateException{
        Session s = (Session)session.get();
        session.set(null);
        if(null != s){
            s.close();
        }
    }
    
    /**
     * 设置SessionFactory,提供此方法是为了可以在外部初始化SessionFactory.
     * @param factory SessionFactory
     */
    public static void setSessionFactory(SessionFactory factory){
        if(null == sessionFactory){
            sessionFactory = factory;
        }
    }
    
    /**
     * 获取SessionFactory,第一次访问时会自动在ClassPath的根路径寻找<br>
     * hibernate.properties与hibernate.cfg.xml文件并初始化。
     * @return SessionFactory
     */
    public static SessionFactory getSessionFactory(){
        //Double check lock.
        if(null == sessionFactory){
            synchronized(lock){
                if(null == sessionFactory){
                    sessionFactory = buildSessionFactory();
                }
            }
        }
        return sessionFactory;
    }
}

posted @ 2006-01-13 11:34 呓语的博客 阅读(369) | 评论 (0)编辑 收藏

从网上当了一个例子,发觉上传图片文件时图片文件中内容发生了变化,最大的变化时所有的FF变成了3F

也就是说输出时把字符当成有符号传送了?(是这样把?)

使用代码如下:

PrintWriter pw = new PrintWriter(new BufferedWriter(new

FileWriter((savePath==null? "" : savePath) + filename)));

while (i != -1 && !newLine.startsWith(boundary)) {

// 文件内容的最后一行包含换行字符

// 因此我们必须检查当前行是否是最

// 后一行

i = in.readLine(line, 0, 1280);

if ((i==boundaryLength+2 || i==boundaryLength+4)

&& (new String(line, 0, i).startsWith(boundary)))

pw.print(newLine.substring(0, newLine.length()-2));

else

pw.print(newLine);

newLine = new String(line, 0, i);

 

}

pw.close();

 

但是总是不行,后来我把PrintWriter用FileOutputStream替代,代码如下:

FileOutputStream pw=new FileOutputStream((savePath==null? "" : savePath) + filename);

//PrintWriter pw = new PrintWriter(new BufferedWriter(new

//FileWriter((savePath==null? "" : savePath) + filename)));

while (i != -1 && !newLine.startsWith(boundary)) {

// 文件内容的最后一行包含换行字符

// 因此我们必须检查当前行是否是最

// 后一行

i = in.readLine(line, 0, 1280);

if ((i==boundaryLength+2 || i==boundaryLength+4)

&& (new String(line, 0, i).startsWith(boundary)))

pw.write(newLine.substring(0, newLine.length()-2).getBytes("ISO8859_1"));

else

pw.write(newLine.getBytes("ISO8859_1"));

newLine = new String(line, 0, i,"ISO8859_1");

 

}

pw.close();

竟然就可以了,那个转换的编码还一定要上才行,不然也不干活的,真是郁闷,这跨平台就不能让写CODE的人解放解放,一个输出都N多类,看着心寒,想着简直想死啊

忙了我一个下午,原来就是这回事

 

下面把原代码附上,是把别人的代码改吧改吧放上来的,还请原作者不要见意:

Java bean 文件:

package cr.web;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.ServletInputStream;

import java.util.Dictionary;

import java.util.Hashtable;

import java.io.PrintWriter;

import java.io.BufferedWriter;

import java.io.FileWriter;

import java.io.IOException;

import java.io.*;

public class FileUploadBean {

 

private String savePath, filepath, filename, contentType;

private byte[] b;

byte t;

private Dictionary fields;

 

public String getFilename() {

return filename;

}

 

public String getFilepath() {

return filepath;

}

 

public void setSavePath(String savePath) {

this.savePath = savePath;

}

 

public String getContentType() {

return contentType;

}

 

public String getFieldValue(String fieldName) {

if (fields == null || fieldName == null)

return null;

return (String) fields.get(fieldName);

}

 

private void setFilename(String s) {

if (s==null)

return;

 

int pos = s.indexOf("filename=\"");

if (pos != -1) {

filepath = s.substring(pos+10, s.length()-1);

// Windows浏览器发送完整的文件路径和名字

// 但Linux/Unix和Mac浏览器只发送文件名字

pos = filepath.lastIndexOf("\\");

if (pos != -1)

filename = filepath.substring(pos + 1);

else

filename = filepath;

}

}

private void setContentType(String s) {

if (s==null)

return;

 

int pos = s.indexOf(": ");

if (pos != -1)

contentType = s.substring(pos+2, s.length());

}

 

public void getByte(HttpServletRequest request)

{

DataInputStream is;

 

int i=0;

try

{

is=new DataInputStream(request.getInputStream());

b=new byte[request.getContentLength()];

 

while (true)

{

try

{

t=is.readByte();

b[i]=t;

i++;

}

catch(EOFException e)

{ break;}

}

is.close();}

catch(IOException e)

{}

}

 

public void doUpload1(HttpServletRequest request) throws

IOException {

byte[] line=new byte[128];

FileOutputStream os=new FileOutputStream("c:\\Demo.out");

ServletInputStream in = request.getInputStream();

getByte(request);

String temp="";

temp=new String(b,"ISO8859_1");

byte[] img=temp.getBytes("ISO8859_1");

for (int i=0;i<img.length;i++)

{ os.write(img[i]); }

os.close();

}

 

 

 

public void doUpload(HttpServletRequest request) throws IOException {

request.setCharacterEncoding("GB2312");

ServletInputStream in = request.getInputStream();

 

byte[] line = new byte[1280];

int i = in.readLine(line, 0, 1280);

if (i < 3)

return;

int boundaryLength = i - 2;

 

String boundary = new String(line, 0, boundaryLength); //-2丢弃换行字符

fields = new Hashtable();

 

while (i != -1) {

String newLine = new String(line, 0, i);

if (newLine.startsWith("Content-Disposition: form-data; name=\"")) {

if (newLine.indexOf("filename=\"") != -1) {

setFilename(new String(line, 0, i-2));

if (filename==null)

return;

//文件内容

i = in.readLine(line, 0, 1280);

setContentType(new String(line, 0, i-2));

i = in.readLine(line, 0, 1280);

//空行

i = in.readLine(line, 0, 1280);

newLine = new String(line, 0, i,"ISO8859_1");

FileOutputStream pw=new FileOutputStream((savePath==null? "" : savePath) + filename);

//PrintWriter pw = new PrintWriter(new BufferedWriter(new

//FileWriter((savePath==null? "" : savePath) + filename)));

while (i != -1 && !newLine.startsWith(boundary)) {

// 文件内容的最后一行包含换行字符

// 因此我们必须检查当前行是否是最

// 后一行

i = in.readLine(line, 0, 1280);

if ((i==boundaryLength+2 || i==boundaryLength+4)

&& (new String(line, 0, i).startsWith(boundary)))

pw.write(newLine.substring(0, newLine.length()-2).getBytes("ISO8859_1"));

else

pw.write(newLine.getBytes("ISO8859_1"));

newLine = new String(line, 0, i,"ISO8859_1");

 

}

pw.close();

 

}

else {

// 普通表单输入元素

// 获取输入元素名字

int pos = newLine.indexOf("name=\"");

String fieldName = newLine.substring(pos+6, newLine.length()-3);

 

i = in.readLine(line, 0, 1280);

i = in.readLine(line, 0, 1280);

newLine = new String(line, 0, i);

StringBuffer fieldValue = new StringBuffer(1280);

while (i != -1 && !newLine.startsWith(boundary)) {

// 最后一行包含换行字符

// 因此我们必须检查当前行是否是最后一行

i = in.readLine(line, 0, 1280);

if ((i==boundaryLength+2 || i==boundaryLength+4)

&& (new String(line, 0, i).startsWith(boundary)))

fieldValue.append(newLine.substring(0, newLine.length()-2));

else

fieldValue.append(newLine);

newLine = new String(line, 0, i);

}

fields.put(fieldName, fieldValue.toString());

}

}

i = in.readLine(line, 0, 1280);

 

}

}

}

 

HTML页面:

<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312">

<title>文件上载</title>

</head>

 

<body>

<form action=jsp1.jsp enctype="MULTIPART/FORM-DATA" method=post>

作者: <input type=text name=author>

<br>

公司: <input type=text name=company>

<br>

说明: <textarea name=comment></textarea>

<br>

选择要上载的文件<input type=file name=filename>

<br>

文件描述: <input type=text name=description>

<br>

<input type=submit value="Upload">

</form>

</body>

</html>

 

JSP页面:

<%@ page contentType="text/html;charset=gb2312"%>

<jsp:useBean id="TheBean" scope="page" class="cr.web.FileUploadBean" />

<%

TheBean.setSavePath("d:\\");

 

//TheBean.doUpload1(request);

TheBean.doUpload(request);

out.println("Filename:" + TheBean.getFilename());

out.println("<BR>内容类型:" + TheBean.getContentType());

out.println("<BR>作者:" + TheBean.getFieldValue("Author"));

out.println("<BR>公司:" + TheBean.getFieldValue("Company"));

out.println("<BR>说明:" + TheBean.getFieldValue("Comment"));

%>

 

posted @ 2006-01-13 10:47 呓语的博客 阅读(194) | 评论 (0)编辑 收藏

用户定义类型

单值类型是与现存类型(它的“源”类型)共享其内部表示的用户定义数据类型,但对于大多数运算来说,认为单值类型是独立和不兼容的。例如,您可能想定义年龄类型、重量类型以及高度类型,所有这些类型都有相当不同的语义,但都使用内部数据类型 INTEGER 作为它们的内部表示。

下列示例说明了命名为 PAY 的单值类型的创建:

     CREATE DISTINCT TYPE PAY AS DECIMAL(9,2) WITH COMPARISONS

虽然 PAY 有与内部数据类型 DECIMAL(9,2)相同的表示,但还是认为它是与 DECIMAL(9,2)或任何其他类型不可比的独立类型。它只能与相同的单值类型比较。并且,会影响到按 DECIMAL 使用的运算符和函数将在此不适用。例如,具有 PAY 数据类型的值不能与具有 INTEGER 数据类型的值相乘。因此,您必须编写只应用于 PAY 数据类型的函数。

使用单值数据类型可限制偶然错误。例如,如果将 EMPLOYEE 表的 SALARY 列定义为 PAY 数据类型,则不能将该列添加至 COMM,即使它们的源类型相同。

单值数据类型支持类型转换。源类型可以转换为单值数据类型,单值数据类型也可以转换为源类型。例如,如果将表 EMPLOYEE 的 SALARY 列定义为 PAY 数据类型,则下列示例将不会在比较运算符处出错。

     SELECT * FROM EMPLOYEE
        WHERE DECIMAL(SALARY) = 41250

DECIMAL(SALARY)返回一个十进制数据类型。相反地,数字数据类型可以转换为 PAY 类型。例如,可以使用 PAY(41250) 来转换数字 41250 的类型。

用户定义函数

使用函数中所述,DB2 通用数据库提供内部函数和用户定义函数 (UDF)。然而,此函数集从不会满足所有需求。您常常需要为特别的任务创建定制函数。用户定义函数允许您创建定制函数。

用户定义函数有两种类型:外部函数

源用户定义函数允许允许用户定义类型有选择地引用另一个已为数据库所知的内部函数或用户定义函数。您可以既使用标量函数又使用列函数。

在下一示例中,基于内部 MAX 列函数来创建称为 MAX 的用户定义函数,它采用 DECIMAL 数据类型作为输入。MAX UDF 采用 PAY 类型作为输入且返回一个 PAY 类型作为输出。

     CREATE FUNCTION MAX(PAY) RETURNS PAY
        SOURCE MAX(DECIMAL)

外部用户定义函数由用户用程序设计语言编写。有外部标量函数外部表函数,在 SQL Reference 中讨论了这两个函数。

假定您已编写了一个计算字符串中字数的函数,则您可以使用 CREATE FUNCTION 语句以名称 WORDCOUNT 向数据库注册该函数。然后就可在 SQL 语句中使用此函数。

例如,下列语句返回雇员编号和他们简历的 ASCII 格式的字数。WORDCOUNT 是用户已向数据库注册并且现正在语句中使用的外部标量函数。

 
     SELECT EMPNO, WORDCOUNT(RESUME)
        FROM EMP_RESUME
        WHERE RESUME_FORMAT = 'ascii'

有关编写用户定义函数的更详细信息,参考 Application Development Guide

大对象 (LOB)

术语大对象及其缩写词 LOB 用于表示三种数据类型:BLOB、CLOB 或 DBCLOB。这些类型可以包含诸如音频、图片以及文档等对象的大量数据。

二进制大对象(BLOB)是变长字符串,以字节进行量度,最大长度可达 2 吉字节。BLOB 主要用来保存非传统数据,如图片、声音以及混合媒体等。

字符大对象(CLOB)是变长字符串,以字节进行量度,最大长度可达 2 吉字节。 CLOB 用于存储大的单字节字符集数据,如文档等。CLOB 被认为是字符串。

双字节字符大对象(DBCLOB)是最大长度可达 2 吉字节的双字节字符变长字符串(1 073 741 823 双字节字符)。 DBCLOB 用于存储大的双字节字符集数据,如文档等。DBCLOB 被认为是图形字符串。

操作大对象(LOB)

由于 LOB 值可以很大,所以将它们从数据库服务器传送至客户机应用程序可能要花费一些时间。然而,一般一次处理 LOB 值的一部分,而不是将它们作为一个整体处理。对于应用程序不需要(或不想要)将整个 LOB 值存储在应用程序内存中的那些情况,应用程序可以通过大对象定位器变量引用此值。

然后后续语句可以使用定位器对数据执行操作,而不必检索整个大对象。定位器变量用来减少应用程序的存储器需求,并通过减少客户机与服务器之间的数据流而改进性能。

另一个机制是文件引用变量。它们用来直接对文件检索大对象或直接从文件来更新表中的大对象。文件引用变量用来减少应用程序的存储器需求,因为这些变量不必存储大对象数据。有关更多信息,参考 Application Development GuideSQL Reference

专用寄存器

专用寄存器是由数据库管理程序为连接定义的存储区,用于存储可以用 SQL 语句引用的信息。下列是几个较常用的专用寄存器的示例。有关所有专用寄存器的列表和更详细的信息,参考 SQL Reference

  • CURRENT DATE:保存对应于执行 SQL 语句时的日时钟的日期。

  • CURRENT FUNCTION PATH:保存一个指定用于分解函数和数据类型引用的函数路径的值。

  • CURRENT SERVER:指定当前应用程序服务器。

  • CURRENT TIME:保存对应于执行 SQL 语句时的日时钟的时间。

  • CURRENT TIMESTAMP:指定对应于执行 SQL 语句时的日时钟的时间戳记。

  • CURRENT TIMEZONE:指定世界时与应用程序服务器本地时间之间的差别。

  • USER:指定运行期权限 ID。

您可以用 VALUES 语句显示专用寄存器的内容。例如:

     VALUES (CURRENT TIMESTAMP)

也可以使用:

     SELECT CURRENT TIMESTAMP FROM ORG

这将为表中的每一行项返回 TIMESTAMP。

目录视图的介绍

DB2 为每个数据库创建并维护一个系统目录表扩充集。这些表包含关于数据库对象(如表、视图、程序包、参考完整性关系、函数、单值类型以及触发器等)的逻辑结构和物理结构的信息。这些表是在创建数据库时创建的,并在正常操作过程中得到更新。不能显式创建或卸下它们,但可以查询和查看其内容。

有关更多信息,参考 SQL Reference

从系统目录中选择行

目录视图象任何其他数据库视图一样。可以使用 SQL 语句来查看数据,确切地说,就是使用查看系统中任何其他视图的相同方式。

可以在 SYSCAT.TABLES 目录中找到关于表的非常有用的信息。要寻找已创建的现存表的名称,发出一个类似以下的语句:

 
     SELECT TABNAME, TYPE, CREATE_TIME
        FROM SYSCAT.TABLES
        WHERE DEFINER = USER

此语句产生下列结果:

     TABNAME            TYPE CREATE_TIME
     ------------------ ---- --------------------------
     ORG                T    1997-05-22-11.15.27.850000
     STAFF              T    1997-05-22-11.15.29.470000
     DEPARTMENT         T    1997-05-22-11.15.30.850000
     EMPLOYEE           T    1997-05-22-11.15.31.310000
     EMP_ACT            T    1997-05-22-11.15.32.850000
     PROJECT            T    1997-05-22-11.15.34.410007
     EMP_PHOTO          T    1997-05-22-11.15.35.190000
     EMP_RESUME         T    1997-05-22-11.15.40.600000
     SALES              T    1997-05-22-11.15.43.000000

下列列表包括与本书讨论的与主题有关的目录视图。还有很多其他目录视图,它们详细地在 SQL Reference管理指南中列出。
说明 目录视图
检查约束 SYSCAT.CHECKS
SYSCAT.COLUMNS
检查约束引用的列 SYSCAT.COLCHECKS
关键字中使用的列 SYSCAT.KEYCOLUSE
数据类型 SYSCAT.DATATYPES
函数参数或函数结果 SYSCAT.FUNCPARMS
参考约束 SYSCAT.REFERENCES
模式 SYSCAT.SCHEMATA
表约束 SYSCAT.TABCONST
SYSCAT.TABLES
触发器 SYSCAT.TRIGGERS
用户定义函数 SYSCAT.FUNCTIONS
视图 SYSCAT.VIEWS

posted @ 2006-01-10 20:29 呓语的博客 阅读(172) | 评论 (0)编辑 收藏

用约束和触发器实施商业规则

在商界,我们的确通常需要确保始终实施某些规则。例如,参与项目的雇员必须被雇用。或者想要某些事件有计划地发生。例如,如果销售员售出一批商品,则应增加其佣金。

DB2 通用数据库为此提供了一套有用的方法。 唯一约束是禁止在表的一列或多列中出现重复值的规则。 参考完整性约束确保在整个指定的表中数据一致性。 表检查约束是一些条件,它们定义为表定义的一部分,限制一列或多列中使用的值。触发器允许您定义一组操作,这些操作通过对指定的表进行删除、插入或更新操作来执行或触发。触发器可用于写入其他表、修改输入值以及发布警报信息。

第一节提供关键字的概念性概述。接着,通过示例和图表进一步探讨参考完整性、约束以及触发器。

关键字

关键字是可用来标识或存取特定行的一组列。

由不止一列组成的关键字称为组合关键字。在具有组合关键字的表中,组合关键字中各列的排序不受这些列在表中排序的约束。

唯一关键字

唯一关键字被定义为它的任何值都不相同。唯一关键字的列不能包含空值。在执行 INSERT 和 UPDATE 语句期间,数据库管理程序强制执行该约束。一个表可以有多个唯一关键字。唯一关键字是可选的,并且可在 CREATE TABLE 或 ALTER TABLE 语句中定义。

主关键字

主关键字是一种唯一关键字,表定义的一部分。一个表不能有多个主关键字,并且主关键字的列不能包含空值。主关键字是可选的,并且可在 CREATE TABLE 或 ALTER TABLE 语句中定义。

外部关键字

外部关键字在参考约束的定义中指定。一个表可以有零个或多个外部关键字。如果组合外部关键字的值的任何部分为空,则该值为空。外部关键字是可选的,并且可在 CREATE TABLE 语句或 ALTER TABLE 语句中定义。

唯一约束

唯一约束确保关键字的值在表中是唯一的。唯一约束是可选的,并且可以通过使用指定 PRIMARY KEY 或 UNIQUE 子句的 CREATE TABLE 或 ALTER TABLE 语句来定义唯一约束。例如,可在一个表的雇员编号列上定义一个唯一约束,以确保每个雇员有唯一的编号。

参考完整性约束

通过定义唯一约束和外部关键字,可以定义表与表之间的关系,从而实施某些商业规则。唯一关键和外部关键字约束的组合通常称为参考完整性约束。外部关键字所引用的唯一约束称为父关键字。外部关键字表示特定的父关键字,或与特定的父关键字相关。例如,某规则可能规定每个雇员(EMPLOYEE 表)必须属于某现存的部门(DEPARTMENT 表)。因此,将 EMPLOYEE 表中的“部门号”定义为外部关键字,而将 DEPARTMENT 表中的“部门号”定义为主关键字。下列图表提供参考完整性约束的直观说明。

图 4. 外部约束和主约束定义关系并保护数据

表检查约束

表检查约束指定对于表的每行都要进行判定的条件。可对个别列指定检查约束。可使用 CREATE 或 ALTER TABLE 语句添加检查约束。

下列语句创建具有下列约束的表:

  • 部门编号的值必须在范围 10 至 100 内
  • 雇员的职务只能为下列之一: "Sales"、"Mgr"或"Clerk"
  • 1986 年之前雇用的每个雇员的工资必须超过 $40,500。

 
     CREATE TABLE EMP
           (ID           SMALLINT NOT NULL,
            NAME         VARCHAR(9),
            DEPT         SMALLINT CHECK (DEPT BETWEEN 10 AND 100),
            JOB          CHAR(5)   CHECK (JOB IN ('Sales', 'Mgr', 'Clerk')),
            HIREDATE     DATE,
            SALARY       DECIMAL(7,2),
            COMM         DECIMAL(7,2),
            PRIMARY KEY (ID),
            CONSTRAINT YEARSAL CHECK (YEAR(HIREDATE) >= 1986 OR SALARY > 40500) )

仅当条件判定为假时才会违反约束。例如,如果插入行的 DEPT 为空值,则插入继续进行而不出错,尽管 DEPT 的值应该象约束中定义的那样在 10 和 100 之间。

下列语句将一个约束添加至名为 COMP 的 EMPLOYEE 表中,该约束为雇员的总报酬必须超过 $15,000:

 
     ALTER TABLE EMP
        ADD CONSTRAINT COMP CHECK (SALARY + COMM > 15000)

将检查表中现存的行以确保这些行不违反新约束。可通过使用如下的 SET CONSTRAINTS 语句将此检查延期:

     SET CONSTRAINTS FOR EMP OFF
     ALTER TABLE EMP ADD CONSTRAINT COMP CHECK (SALARY + COMM > 15000)
     SET CONSTRAINTS FOR EMP IMMEDIATE CHECKED
 

首先使用 SET CONSTRAINTS 语句以延期对表的约束检查。然后可将一个或多个约束添加至表而不检查这些约束。接着再次发出 SET CONSTRAINTS 语句,反过来将约束检查打开并执行任何延期的约束检查。

触发器

一个触发器定义一组操作,这组操作通过修改指定基表中数据的操作来激活。

可使用触发器来执行对输入数据的验证;自动生成新插入行的值;为了交叉引用而读取其他表;为了审查跟踪而写入其他表;或通过电子邮件信息支持警报。使用触发器将导致应用程序开发及商业规则的全面实施更快速并且应用程序和数据的维护更容易。

DB2 通用数据库支持几种类型的触发器。可定义触发器在 DELETE、INSERT 或 UPDATE 操作之前或之后激活。每个触发器包括一组称为触发操作的 SQL 语句,这组语句可包括一个可选的搜索条件。

可进一步定义后触发器以对每一行都执行触发操作,或对语句执行一次触发操作,而前触发器总是对每一行都执行触发操作。

在 INSERT、UPDATE 或 DELETE 语句之前使用触发器,以便在执行触发操作之前检查某些条件,或在将输入值存储在表中之前更改输入值。使用后触发器,以便在必要时传播值或执行其他任务,如发送信息等,这些任务可能是触发器操作所要求的。

下列示例说明了前触发器和后触发器的使用。考虑一个记录并跟踪股票价格波动的应用程序。该数据库包含两个表,CURRENTQUOTE 和 QUOTEHISTORY,定义如下:

 
     CREATE TABLE CURRENTQUOTE
     (SYMBOL VARCHAR(10),
      QUOTE DECIMAL(5,2),
      STATUS VARCHAR(9))
     
     CREATE TABLE  QUOTEHISTORY
     (SYMBOL VARCHAR(10),
      QUOTE DECIMAL(5,2),
      TIMESTAMP TIMESTAMP)

当使用如下语句更新 CURRENTQUOTE 的 QUOTE 列时:

 
     UPDATE CURRENTQUOTE
        SET QUOTE = 68.5
        WHERE SYMBOL = 'IBM'

应更新 CURRENTQUOTE 的 STATUS 列以反映股票是否:

  • 在升值
  • 处于本年度的新高
  • 在下跌
  • 处于本年度的新低
  • 价位稳定

这通过使用下列前触发器来实现:

(1)

     CREATE TRIGGER STOCK_STATUS
        NO CASCADE BEFORE UPDATE OF QUOTE ON CURRENTQUOTE
        REFERENCING NEW AS NEWQUOTE OLD AS OLDQUOTE
     FOR EACH ROW MODE DB2SQL                                   

(2)

      SET NEWQUOTE.STATUS =

(3)

         CASE

(4)

            WHEN NEWQUOTE.QUOTE >=
                      (SELECT MAX(QUOTE)
                          FROM QUOTEHISTORY         
                          WHERE SYMBOL = NEWQUOTE.SYMBOL                 
                          AND YEAR(TIMESTAMP) = YEAR(CURRENT DATE) )     
            THEN 'High'

(5)

          WHEN NEWQUOTE.QUOTE <=                             
                      (SELECT MIN(QUOTE)
                          FROM QUOTEHISTORY
                          WHERE SYMBOL = NEWQUOTE.SYMBOL                       
                          AND YEAR(TIMESTAMP) = YEAR(CURRENT DATE) )      
          THEN 'Low'

(6)

          WHEN NEWQUOTE.QUOTE > OLDQUOTE.QUOTE
             THEN 'Rising'
          WHEN NEWQUOTE.QUOTE < OLDQUOTE.QUOTE                       
             THEN 'Dropping'
          WHEN NEWQUOTE.QUOTE = OLDQUOTE.QUOTE
             THEN 'Steady'
     END                                                  
 

(1)
此代码块将名为 STOCK_STATUS 的触发器定义为一个应该在更新 CURRENTQUOTE 表的 QUOTE 列之前激活的触发器。第二行指定,在将 CURRENTQUOTE 表的实际更新所引起的任何更改应用于数据库之前,要应用触发操作。第二行也意味着触发操作将不会激活任何其他触发器。第三行指定一些名称,必须将这些名称作为列名的限定符用于新值 (NEWQUOTE) 和旧值 (OLDQUOTE)。用这些相关名(NEWQUOTE 和 OLDQUOTE)限定的列名称为转换变量。第四行表示应对每一行都执行触发操作。

(2)
这标记此触发器的触发操作中第一个也是唯一的一个 SQL 语句的开始。 SET 转换变量语句在一个触发器中用来将值赋给表的行中的列,该表正在由激活该触发器的语句进行更新。此语句正将一个值赋给 CURRENTQUOTE 表的 STATUS 列。

(3)
该赋值语句右边使用的表达式为 CASE 表达式。 CASE 表达式扩充为 END 关键字。

(4)
第一种情况检查新报价 (NEWQUOTE.QUOTE) 是否超过当前日历年度中股票符号的最高价。子查询正在使用由跟在后面的后触发器更新的 QUOTEHISTORY 表。

(5)
第二种情况检查新报价 (NEWQUOTE.QUOTE) 是否小于当前日历年度中股票符号的最低价。子查询正在使用由跟在后面的后触发器更新的 QUOTEHISTORY 表。

(6)
最后三种情况将新报价 (NEWQUOTE.QUOTE) 与表 (OLDQUOTE.QUOTE) 中的报价比较,以确定新报价是大于、小于还是等于旧报价。 SET 转换变量语句在此处结束。

除了更新 CURRENTQUOTE 表中的项之外,还需要通过将新报价连同时间戳记一起复制到 QUOTEHISTORY 表中来创建一个审查记录。这通过使用下列后触发器来实现:

(1)

     CREATE TRIGGER RECORD_HISTORY
     AFTER UPDATE OF QUOTE ON CURRENTQUOTE
     REFERENCING NEW AS NEWQUOTE
     FOR EACH ROW MODE DB2SQL
     BEGIN ATOMIC

(2)

     INSERT INTO QUOTEHISTORY
        VALUES (NEWQUOTE.SYMBOL, NEWQUOTE.QUOTE, CURRENT TIMESTAMP);
     END

(1)
此代码块将命名为 RECORD_HISTORY 的触发器定义为应该在更新 CURRENTQUOTE 表的 QUOTE 列之后激活的触发器。第三行指定应该作为列名的限定符用于新值 (NEWQUOTE) 的名称。第四行表示应对每一行都执行触发操作。

(2)
此触发器的触发操作包括单个 SQL 语句,该语句使用已更新的行中的数据(NEWQUOTE.SYMBOL 和 NEWQUOTE.QUOTE)和当前的时间戳记将该行插入 QUOTEHISTORY 表。

CURRENT TIMESTAMP 是包含时间戳记的专用寄存器。专用寄存器中提供了列表和解释。

连接

从两个或更多个表中组合数据的过程称为连接表。数据库管理程序从指定的表中形成行的所有组合。对于每个组合,它都测试连接条件。连接条件是带有一些约束的搜索条件。有关约束的列表,参考 SQL Reference

注意:连接条件涉及的列的数据类型不必相同;然而,这些数据类型必须相容。计算连接条件的方式与计算其他搜索条件的方式相同,并且使用相同的比较规则。

如果未指定连接条件,则返回在 FROM 子句中列出的表中行的所有组合,即使这些行可能完全不相关。该结果称为这两个表的交叉积

本节中的示例基于下面两个表。这两个表只是样本数据库中表的简化形式,在样本数据库中并不存在。这两个表一般用来概述关于连接的重点。 SAMP_STAFF 列出未作为合同工雇用的雇员的姓名以及这些雇员的职务说明,而 SAMP_PROJECT 则列出雇员(合同工和全职人员)的姓名以及这些雇员所参与的项目。

这些表如下:

图 5. SAMP_PROJECT 表


REQTEXT

图 6. SAMP_STAFF 表


REQTEXT

下列示例产生两个表的交叉积。因未指定连接条件,所以给出了行的所有组合:

     SELECT SAMP_PROJECT.NAME,
            SAMP_PROJECT.PROJ, SAMP_STAFF.NAME, SAMP_STAFF.JOB
        FROM SAMP_PROJECT, SAMP_STAFF

此语句产生下列结果:

     NAME       PROJ   NAME       JOB
     ---------- ------ ---------- --------
     Haas       AD3100 Haas       PRES
     Thompson   PL2100 Haas       PRES
     Walker     MA2112 Haas       PRES
     Lutz       MA2111 Haas       PRES
     Haas       AD3100 Thompson   MANAGER
     Thompson   PL2100 Thompson   MANAGER
     Walker     MA2112 Thompson   MANAGER
     Lutz       MA2111 Thompson   MANAGER
     Haas       AD3100 Lucchessi  SALESREP
     Thompson   PL2100 Lucchessi  SALESREP
     Walker     MA2112 Lucchessi  SALESREP
     Lutz       MA2111 Lucchessi  SALESREP
     Haas       AD3100 Nicholls   ANALYST
     Thompson   PL2100 Nicholls   ANALYST
     Walker     MA2112 Nicholls   ANALYST
     Lutz       MA2111 Nicholls   ANALYST

两个主要的连接类型是内连接外连接。到目前为止,所有示例中使用的都是内连接。内连接只保留交叉积中满足连接条件的那些行。如果某行在一个表中存在,但在另一个表中不存在,则结果表中不包括该信息。

下列示例产生两个表的内连接。该内连接列出分配给某个项目的全职雇员信息:

     SELECT SAMP_PROJECT.NAME,
            SAMP_PROJECT.PROJ, SAMP_STAFF.NAME, SAMP_STAFF.JOB
        FROM SAMP_PROJECT, SAMP_STAFF
        WHERE SAMP_STAFF.NAME = SAMP_PROJECT.NAME

或者,也可以指定如下内连接:

     SELECT SAMP_PROJECT.NAME,
            SAMP_PROJECT.PROJ, SAMP_STAFF.NAME, SAMP_STAFF.JOB
        FROM SAMP_PROJECT INNER JOIN SAMP_STAFF
          ON SAMP_STAFF.NAME = SAMP_PROJECT.NAME

结果是:

     NAME       PROJ   NAME       JOB
     ---------- ------ ---------- --------
     Haas       AD3100 Haas       PRES
     Thompson   PL2100 Thompson   MANAGER

注意:该内连接的结果由右表和左表中姓名列的值匹配的行组成- 'Haas' 和 'Thompson' 都包括在列出所有全职雇员的 SAMP_STAFF 表中以及列出分配给某个项目的专职和合同雇员的 SAMP_PROJECT 表中。

外连接是内连接和左表和/或右表中未包括内连接中的那些行的并置。当对两个表执行外连接时,可任意将一个表指定为左表而将另一个表指定为右表。外连接有三种类型:

  1. 左外连接包括内连接和左表中未包括在内连接中的那些行。

  2. 右外连接包括内连接和右表中未包括在内连接中的那些行。

  3. 全外连接包括内连接以及左表和右表中未包括在内连接中的行。

使用 SELECT 语句来指定要显示的列。在 FROM 子句中,列出后跟关键字 LEFT OUTER JOIN、RIGHT OUTER JOIN 或 FULL OUTER JOIN 的第一个表的名称。接着需要指定后跟 ON 关键字的第二个表。在 ON 关键字后面,指定表示要连接的表之间关系的连接条件。

在下列示例中,将 SAMP_STAFF 指定为右表,而 SAMP_PROJECT 则被指定为左表。通过使用 LEFT OUTER JOIN,列出所有全职和合同雇员(在 SAMP_PROJECT 中列出)的姓名和项目编号,如果是全职雇员(在 SAMP_STAFF 中列出),还列出这些雇员的职位:

     SELECT SAMP_PROJECT.NAME, SAMP_PROJECT.PROJ,
            SAMP_STAFF.NAME, SAMP_STAFF.JOB
        FROM SAMP_PROJECT LEFT OUTER JOIN SAMP_STAFF
          ON SAMP_STAFF.NAME = SAMP_PROJECT.NAME

此语句产生下列结果:

     NAME       PROJ                 NAME       JOB
     ---------- -------------------- ---------- --------------------
     Haas       AD3100               Haas       PRES
     Lutz       MA2111               -          -
     Thompson   PL2100               Thompson   MANAGER
     Walker     MA2112               -          -

所有列中都具有值的那些行是该内连接的结果。这些都是满足连接条件的行: 'Haas' 和 'Thompson' 既在 SAMP_PROJECT(左表)中列出又在 SAMP_STAFF(右表)中列出。对于不满足连接条件的行,右表的列上出现空值: 'Lutz' 和 'Walker' 都是在 SAMP_PROJECT 表中列出的合同雇员,因而未在 SAMP_STAFF 表中列出。注意:左表中的所有行都包括在结果集中。

在下一个示例中,将 SAMP_STAFF 指定为右表而 SAMP_PROJECT 则被指定为左表。通过使用 RIGHT OUTER JOIN 列出所有专职雇员(在 SAMP_STAFF 中列出)的姓名和工作职位,如果将这些雇员分配给了某个项目(在 SAMP_PROJECT 中列出),还列出他们的项目编号:

     SELECT SAMP_PROJECT.NAME,
            SAMP_PROJECT.PROJ, SAMP_STAFF.NAME, SAMP_STAFF.JOB
        FROM SAMP_PROJECT RIGHT OUTER JOIN SAMP_STAFF
          ON SAMP_STAFF.NAME = SAMP_PROJECT.NAME

结果为:

    NAME       PROJ                 NAME       JOB
    ---------- -------------------- ---------- --------------------
    Haas       AD3100               Haas       PRES
    -          -                    Lucchessi  SALESREP
    -          -                    Nicholls   ANALYST
    Thompson   PL2100               Thompson   MANAGER

象在左外连接中一样,所有列中都具有值的那些行是内连接的结果。这些都是满足连接条件的行: 'Haas'和'Thompson'既在 SAMP_PROJECT(左表)中列出又在 SAMP_STAFF(右表)中列出。对于不满足连接条件的行,右表的列上出现空值: 'Lucchessi'和'Nicholls'都是未分配项目的专职雇员。虽然他们在 SAMP_STAFF 中列出,但未在 SAMP_PROJECT 中列出。注意:右表中的所有行都包括在结果集中。

下一个示例对 SAMP_PROJECT 表和 SAMP_STAFF 表使用 FULL OUTER JOIN。该示例列出所有专职雇员(包括未分配项目的雇员)和合同雇员的姓名:

     SELECT SAMP_PROJECT.NAME, SAMP_PROJECT.PROJ,
            SAMP_STAFF.NAME, SAMP_STAFF.JOB
        FROM SAMP_PROJECT FULL OUTER JOIN SAMP_STAFF
          ON SAMP_STAFF.NAME = SAMP_PROJECT.NAME

结果为:

     NAME       PROJ                 NAME       JOB
     ---------- -------------------- ---------- --------------------
     Haas       AD3100               Haas       PRES
     -          -                    Lucchessi  SALESREP
     -          -                    Nicholls   ANALYST
     Thompson   PL2100               Thompson   MANAGER
     Lutz       MA2111               -          -
     Walker     MA2112               -          -

此结果包括左外连接、右外连接以及内连接。列出所有专职雇员和合同雇员。正如左外连接和右外连接一样,对于不满足连接条件的值,相应列中出现空值。 SAMP_STAFF 和 SAMP_PROJECT 中的每一行都包括在结果集中。

复杂查询

DB2 通用数据库允许您通过使用 ROLLUP 和 CUBE 分组、合并及查看单个结果集中的多列。这种新型而强大的功能增强并简化了基于数据分析的 SQL。

有很多方法可从数据库中抽取有用信息。可执行递归查询从现存数据集中产生结果表。

ROLLUP 和 CUBE 查询

在查询的 GROUP BY 子句中指定 ROLLUP 和 CUBE 运算。 ROLLUP 分组产生包含常规分组行和小计行的结果集。CUBE 分组产生包含来自 ROLLUP 和交叉制表行中的行的结果集。所以对于 ROLLUP,可获取每人每月的销售量以及每月销售总量和总部总量。对于 CUBE,将包括每人销售总量的附加行。参见 SQL Reference 以了解更详细的情况。

递归查询

递归查询是迭代使用结果数据来确定进一步结果的查询。可以把这想象成在一棵树上或一幅图中来回移动。使用递归查询的常见示例包括材料单应用程序、订票系统、网络计划和调度。递归查询是使用包括引用自己名称的的公共表表达式来编写的。参见 SQL Reference 以获取递归查询的示例。

posted @ 2006-01-10 20:28 呓语的博客 阅读(224) | 评论 (0)编辑 收藏

用集合运算符组合查询

UNION、EXCEPT 以及 INTERSECT 集合运算符使您能够将两个或更多外层查询组合成单个查询。执行用这些集合运算符连接的每个查询并组合各个查询的结果。根据运算符不同,产生不同的结果。

UNION 运算符

UNION 运算符通过组合其他两个结果表(例如 TABLE1 和 TABLE2)并消去表中任何重复行而派生出一个结果表。当 ALL 随 UNION 一起使用时(即 UNION ALL),不消除重复行。两种情况下,派生表的每一行不是来自 TABLE1 就是来自 TABLE2。

在下列 UNION 运算符的示例中,查询返回薪水高于 $21,000、有管理责任且工龄少于 8 年的人员的姓名:

(1)

     SELECT ID, NAME FROM STAFF WHERE SALARY > 21000
     UNION

(2)

     SELECT ID, NAME FROM STAFF WHERE JOB='Mgr' AND YEARS < 8
     ORDER BY ID
     

各个查询的结果如下:

(1)

                                                                          
     ID     NAME
     ------ ---------                                                        
        140 Fraye
        160 Molinare
        260 Jones
                                                                               

(2)

     ID     NAME
     ------ --------- 
         10 Sanders
         30 Marenghi
        100 Plotz
        140 Fraye
        160 Molinare
        240 Daniels

数据库管理程序组合这两个查询的结果,消除重复行,并按升序返回最终结果。

                                            
     ID     NAME
     ------ ---------
         10 Sanders
         30 Marenghi
        100 Plotz
        140 Fraye
        160 Molinare
        240 Daniels
        260 Jones

如果在带有任何集合运算符的查询中使用 ORDER BY 子句,则必须在最后一个查询之后写该子句。系统对组合的回答集进行排序。如果两个表中的列名不同,则组合的结果表没有相应列的名称。替代地,将这些列按其出现的顺序编号。因此,如果想要对结果表进行排序,则必须在 ORDER BY 子句中指定列号。

EXCEPT 运算符

EXCEPT 运算符通过包括所有在 TABLE1 中但不在 TABLE2 中的行并消除所有重复行而派生出一个结果表。当 ALL 随 EXCEPT 一起使用时 (EXCEPT ALL),不消除重复行。

在下列 EXCEPT 运算符的示例中,查询返回收入超过 $21,000 但没有经理职位且工龄为 8 年或更长的所有人员的姓名。

     SELECT ID, NAME FROM STAFF WHERE SALARY > 21000
     EXCEPT
     SELECT ID, NAME FROM STAFF WHERE JOB='Mgr' AND YEARS < 8
     

各个查询的结果在关于 UNION 的一节中列出。上面的语句产生下列结果:

     ID     NAME
     ------ ---------
        260 Jones
 

INTERSECT 运算符

INTERSECT 运算符通过只包括 TABLE1 和 TABLE2 中都有的行并消除所有重复行而派生出一个结果表。当 ALL 随 INTERSECT 一起使用时 (INTERSECT ALL),不消除重复行。

在下列 INTERSECT 运算符的示例中,查询返回收入超过 $21,000 有管理责任且工龄少于8年的雇员的姓名和 ID。

     SELECT ID, NAME FROM STAFF WHERE SALARY > 21000
     INTERSECT
     SELECT ID, NAME FROM STAFF WHERE JOB='Mgr' AND YEARS < 8

各个查询的结果在关于 UNION 的一节中列出。这两个使用 INTERSECT 的查询的结果为:

     ID     NAME
     ------ ---------
        140 Fraye
        160 Molinare
     

当使用 UNION、EXCEPT 以及 INTERSECT 运算符时,记住下列事项:

  • 运算符的查询选择列表中的所有对应项必须是相容的。有关更多信息,参见 SQL Reference 中的数据类型相容性表。

  • ORDER BY 子句(如果使用该子句的话)必须放在最后一个带有集合运算符的查询后面。对于每个运算符来说,如果列的名称与查询的选择列表中对应项的名称相同,则该列名只能在 ORDER BY 子句中使用。

  • 在具有相同数据类型和相同长度的列之间进行的运算会产生一个具有该类型和长度的列。针对 UNION、EXCEPT 以及 INTERSECT 集合运算符的结果,参见 SQL Reference 中结果数据类型的规则。

谓词

谓词允许您构造条件,以便只处理满足这些条件的那些行。基本谓词在选择行讨论。本节讨论 IN、BETWEEN、LIKE、EXISTS 以及定量谓词。

使用 IN 谓词

使用 IN 谓词将一个值与其他几个值进行比较。例如:

     SELECT NAME
        FROM STAFF
        WHERE DEPT IN (20, 15)

此示例相当于:

     SELECT NAME
        FROM STAFF
        WHERE DEPT = 20  OR DEPT = 15

当子查询返回一组值时,可使用 IN 和 NOT IN 运算符。例如,下列查询列出负责项目 MA2100 和 OP2012 的雇员的姓:

     SELECT LASTNAME
            FROM EMPLOYEE
        WHERE EMPNO IN
           (SELECT RESPEMP
               FROM PROJECT
               WHERE PROJNO = 'MA2100'
               OR PROJNO = 'OP2012')

计算一次子查询,并将结果列表直接代入外层查询。例如,上面的子查询选择雇员编号 10 和 330,对外层查询进行计算,就好象 WHERE 子句如下:

     WHERE EMPNO IN (10, 330)

子查询返回的值列表可包含零个、一个或多个值。

使用 BETWEEN 谓词

使用 BETWEEN 谓词将一个值与某个范围内的值进行比较。范围两边的值是包括在内的,并考虑 BETWEEN 谓词中用于比较的两个表达式。

下一示例寻找收入在 $10,000 和 $20,000 之间的雇员的姓名:

     SELECT LASTNAME
            FROM EMPLOYEE
        WHERE SALARY BETWEEN 10000 AND 20000

这相当于:

     SELECT LASTNAME
            FROM EMPLOYEE
        WHERE SALARY >= 10000 AND SALARY <= 20000

下一个示例寻找收入少于 $10,000 或超过 $20,000 的雇员的姓名:

     SELECT LASTNAME
            FROM EMPLOYEE
        WHERE SALARY NOT BETWEEN 10000 AND 20000

使用 LIKE 谓词

使用 LIKE 谓词搜索具有某些模式的字符串。通过百分号和下划线指定模式。

  • 下划线字符(_)表示任何单个字符。

  • 百分号(%)表示零或多个字符的字符串。

  • 任何其他表示本身的字符。

下列示例选择以字母'S'开头长度为 7 个字母的雇员名:

     SELECT NAME
        FROM STAFF
        WHERE NAME LIKE 'S_ _ _ _ _ _'

下一个示例选择不以字母'S'开头的雇员名:

     SELECT NAME
        FROM STAFF
        WHERE NAME  NOT LIKE 'S%'

使用 EXISTS 谓词

可使用子查询来测试满足某个条件的行的存在性。在此情况下,谓词 EXISTS 或 NOT EXISTS 将子查询链接到外层查询。

当用 EXISTS 谓词将子查询链接到外层查询时,该子查询不返回值。相反,如果子查询的回答集包含一个或更多个行,则 EXISTS 谓词为真;如果回答集不包含任何行,则 EXISTS 谓词为假。

通常将 EXISTS 谓词与相关子查询一起使用。下面示例列出当前在项目(PROJECT) 表中没有项的部门:

     SELECT DEPTNO, DEPTNAME
        FROM DEPARTMENT X
        WHERE NOT EXISTS
                 (SELECT *
                     FROM PROJECT
                     WHERE DEPTNO = X.DEPTNO)
        ORDER BY DEPTNO

可通过在外层查询的 WHERE 子句中使用 AND 和 OR 将 EXISTS 和 NOT EXISTS 谓词与其他谓词连接起来。

定量谓词

定量谓词将一个值和值的集合进行比较。如果全查询返回多个值,则必须通过附加后缀 ALL、ANY 或 SOME 来修改谓词中的比较运算符。这些后缀确定如何在外层谓词中处理返回的这组值。使用>比较运算符作为示例(下面的注释也适用于其他运算符):

表达式 > ALL (全查询)
如果该表达式大于由全查询返回的每个单值,则该谓词为真。如果全查询未返回值,则该谓词为真。如果指定的关系至少对一个值为假,则结果为假。注意:<>ALL 定量谓词相当于 NOT IN 谓词。

下列示例使用子查询和> ALL 比较来寻找收入超过所有经理的所有雇员的姓名和职业:

     SELECT LASTNAME, JOB
            FROM EMPLOYEE
        WHERE SALARY > ALL
        (SELECT SALARY
            FROM EMPLOYEE
            WHERE JOB='MANAGER')

表达式 > ANY (全查询)
如果表达式至少大于由全查询返回的值之一,则该谓词为真。如果全查询未返回值,则该谓词为假。注意:=ANY 定量运算符相当于 IN 谓词。

表达式 > SOME(全查询)
SOME 与 ANY 同义。

posted @ 2006-01-10 20:27 呓语的博客 阅读(284) | 评论 (0)编辑 收藏

标量全查询

标量全查询是在括号中的全查询,该查询返回的一行只包含一个列值。标量全查询对从数据库中检索数据值供表达式使用是很有用的。

  • 下列示例列出其薪水超过全部雇员平均薪水的雇员的姓名:
     
         SELECT LASTNAME, FIRSTNME
            FROM EMPLOYEE
            WHERE  SALARY > (SELECT AVG(SALARY)
                               FROM EMPLOYEE)
    

  • 下列示例在两个不同的表中查寻雇员的平均薪水:
     
         SELECT AVG(SALARY) AS "Average_Employee",
                (SELECT AVG(SALARY) AS "Average_Staff" FROM STAFF)
                    FROM EMPLOYEE
    

转换数据类型

有时可能需要将一些值从一种数据类型转换成另一种数据类型,例如,从数字值转换成字符串。要将一个值转换成另一个不同的类型,使用 CAST 说明。

转换说明的另一个可能用途是截断很长的字符串。在 EMP_RESUME 表中,RESUME 列是 CLOB(5K)。您可能只想显示包含应聘者个人信息的前 370 个字符。要从 EMP_RESUME 表中显示简历的 ASCII 格式的前 370 个字符,发出下列请求:

 
     SELECT EMPNO, CAST(RESUME AS VARCHAR(370))
        FROM EMP_RESUME
        WHERE RESUME_FORMAT = 'ascii'

会发出一个警告,通知您超过 370 个字符的值被截断。

可将空值转换为更便于在查询中进行处理的其他数据类型。 公共表表达式是一个为此目的使用转换的示例。

条件表达式

可在 SQL 语句中使用 CASE 表达式以便于处理表的数据表示。这提供了一种功能强大的条件表达式能力,在概念上与某些程序设计语言中的 CASE 语句类似。

  • 要从 ORG 表中的 DEPTNAME 列将部门编号更改为有意义的词,输入下列查询:

         SELECT DEPTNAME,
            CASE DEPTNUMB
              WHEN 10 THEN 'Marketing'
              WHEN 15 THEN 'Research'
              WHEN 20 THEN 'Development'
              WHEN 38 THEN 'Accounting'
              ELSE 'Sales'
            END  AS FUNCTION
            FROM ORG
    

    结果为:

         DEPTNAME       FUNCTION
         -------------- -----------
         Head Office    Marketing
         New England    Research
         Mid Atlantic   Development
         South Atlantic Accounting
         Great Lakes    Sales
         Plains         Sales
         Pacific        Sales
         Mountain       Sales
    

  • 可使用 CASE 表达式来防止出现异常情况,如被零除等。在下列示例中,如果雇员没有奖金或佣金报酬,则语句条件通过避免除法运算来防止出错:

     
         SELECT LASTNAME, WORKDEPT FROM EMPLOYEE
            WHERE(CASE
                     WHEN BONUS+COMM=0 THEN NULL
                     ELSE SALARY/(BONUS+COMM)
                  END ) > 10
    

  • 可在单个语句中使用 CASE 表达式,根据一个列中值的子集的总和与该列中所有值的总和的比来产生一个比率。使用 CASE 表达式的语句只需要传送数据一次。在没有 CASE 表达式的情况下,执行同样的计算至少需要传送两次。

    下列示例使用 CASE 表达式计算部门 20 的薪水之和与全部薪水总额的比率:

         SELECT CAST(CAST (SUM(CASE
                                  WHEN DEPT = 20 THEN SALARY
                                  ELSE 0
                               END) AS DECIMAL(7,2))/
                           SUM(SALARY) AS DECIMAL (3,2))
            FROM STAFF
    

    结果为 0.11。注意:CAST 函数确保结果的精度得到保持。

  • 可使用 CASE 表达式来计算简单的函数,而不必调用函数本身,调用函数将需要额外开销。例如:

         CASE
            WHEN X<0 THEN -1
            WHEN X=0 THEN 0
            WHEN X>0 THEN 1
         END
    

    此表达式与 SYSFUN 模式中 SIGN 用户定义函数有相同的结果。

表表达式

如果只需要单个查询的视图定义,可使用表表达式

表表达式是临时的,只在 SQL 语句的使用期限内有效;表表达式不能共享,但它们比视图更灵活。任何授权的用户都可共享视图定义。

本节描述如何在查询中使用公共表表达式和嵌套表表达式。

嵌套表表达式

嵌套表表达式是一个临时视图,其中的定义被嵌套(直接定义)在主查询的 FROM子 句中。

下列查询使用嵌套表表达式来寻找那些教育级别超过 16 的雇员的平均总收入、教育级别以及雇用年份:

 
     SELECT EDLEVEL, HIREYEAR, DECIMAL(AVG(TOTAL_PAY), 7,2)
        FROM (SELECT YEAR(HIREDATE) AS HIREYEAR, EDLEVEL,
                     SALARY+BONUS+COMM AS TOTAL_PAY
                 FROM EMPLOYEE
                 WHERE EDLEVEL > 16 )  AS PAY_LEVEL
        GROUP BY EDLEVEL, HIREYEAR
        ORDER BY EDLEVEL, HIREYEAR
 

结果如下:

     EDLEVEL HIREYEAR    3
     ------- ----------- ---------
          17        1967  28850.00
          17        1973  23547.00
          17        1977  24430.00
          17        1979  25896.50
          18        1965  57970.00
          18        1968  32827.00
          18        1973  45350.00
          18        1976  31294.00
          19        1958  51120.00
          20        1975  42110.00

此查询使用嵌套表表达式,首先从 HIREDATE 列中抽取雇用年份,以便可以在后续的 GROUP BY 子句中使用该雇用年份。您可能不想将此查询作为视图来创建,因为您打算用不同的 EDLEVEL 值来执行相似查询。

此示例中使用了标量内部函数 DECIMAL。 DECIMAL 返回数字或字符串的十进制表示。有关函数的更多详情,参考 SQL Reference

公共表表达式

公共表表达式是在全查询的开头使用 WITH 关键字定义的命名结果表。公共表表达式是您创建以在复杂查询之中使用的表表达式。在查询的开头使用 WITH 子句定义并命名公共表表达式。对公共表表达式的重复引用使用同一个结果集。相比之下,如果使用嵌套表表达式或视图,则每次都将重新生成结果集,其结果可能各不相同。

下列示例列出公司中教育级别大于 16、平均工资比那些同时雇用的且有同样教育级别的人低的所有人。在该查询后面会更详细地描述查询的各个部分。

(1)

     WITH
           PAYLEVEL AS
              (SELECT EMPNO, YEAR(HIREDATE) AS HIREYEAR, EDLEVEL,
                      SALARY+BONUS+COMM AS TOTAL_PAY
                  FROM EMPLOYEE
                  WHERE EDLEVEL > 16),
(2)
           PAYBYED (EDUC_LEVEL, YEAR_OF_HIRE, AVG_TOTAL_PAY) AS
              (SELECT EDLEVEL, HIREYEAR, AVG(TOTAL_PAY)
                  FROM PAYLEVEL
                  GROUP BY EDLEVEL, HIREYEAR)

(3)

     SELECT EMPNO, EDLEVEL, YEAR_OF_HIRE, TOTAL_PAY, DECIMAL(AVG_TOTAL_PAY,7,2)
        FROM PAYLEVEL, PAYBYED
        WHERE EDLEVEL=EDUC_LEVEL
          AND HIREYEAR = YEAR_OF_HIRE
          AND TOTAL_PAY < AVG_TOTAL_PAY                        

(1)
这是名为 PAYLEVEL 的公共表表达式。此结果表包括雇用某个人的年份、该雇员的总收入以及他(或她)的教育级别。只包括雇员的教育级别大于 16 的那些行。

(2)
这是名为 PAYBYED(或 PAY by education)的公共表表达式。该表达式使用在前一个公共表表达式中创建的 PAYLEVEL 表来确定每个教育级别同一年雇用的雇员的教育级别、雇用年份以及平均收入。此表返回的列被赋予的名称与选择列表中所使用的列名不同(如 EDUC_LEVEL)。这会生成命名为 PAYBYED 的结果集,与嵌套表表达式示例中产生的结果相同。

(3)
最后,我们获得能产生期望结果的实际查询。连接这两个表(PAYLEVEL,PAYBYED)以确定总收入比同年雇用的人的平均收入低的那些人。注意:PAYBYED 是以 PAYLEVEL 为基础。所以在完整语句中有效地存取了 PAYLEVEL 两次。两次都使用同一组行来计算查询。

最终结果如下:

     EMPNO  EDLEVEL YEAR_OF_HIRE TOTAL_PAY     5
     ------ ------- ------------ ------------- ---------
     000210      17         1979      20132.00  25896.50

相关名

相关名是用于识别一个对象的多种用途的标识符。可在查询的 FROM 子句中和 UPDATE 或 DELETE 语句的第一个子句中定义相关名。相关名可与表、视图或嵌套表表达式关联,但只限于定义相关名的上下文中。

例如,子句 FROM STAFF S、ORG O 分别指定 S 和 O 作为 STAFF 和 ORG 的相关名。

      SELECT NAME, DEPTNAME
         FROM STAFF S, ORG O
         WHERE O.MANAGER = S.ID

一旦定义了相关名,则只可以使用相关名来限定对象。例如,上例中如果写成 ORG.MANAGER=STAFF.ID 的话,则该语句就会失效。

也可以使用相关名作为表示数据库对象的简称。只输入 S 比输入 STAFF 更容易。

通过使用相关名,可复制对象。这在需要将表中各项与自己本身作比较时很有用。在下列示例中,EMPLOYEE 表与它自己的另一个实例比较以寻找所有雇员的经理。该示例显示非设计员的雇员的姓名、这些雇员的经理的姓名以及部门编号。

     SELECT E2.FIRSTNME, E2.LASTNAME,
            E2.JOB, E1.FIRSTNME, E1.LASTNAME, E1.WORKDEPT
        FROM EMPLOYEE E1, EMPLOYEE E2
        WHERE E1.WORKDEPT = E2.WORKDEPT
          AND E1.JOB = 'MANAGER'
          AND E2.JOB <> 'MANAGER'
          AND E2.JOB <> 'DESIGNER'
 

此语句产生下列结果:

     FIRSTNME     LASTNAME        JOB      FIRSTNME     LASTNAME        WORKDEPT
     ------------ --------------- -------- ------------ --------------- --------
     DOLORES      QUINTANA        ANALYST  SALLY        KWAN            C01
     HEATHER      NICHOLLS        ANALYST  SALLY        KWAN            C01
     JAMES        JEFFERSON       CLERK    EVA          PULASKI         D21
     MARIA        PEREZ           CLERK    EVA          PULASKI         D21
     SYBIL        JOHNSON         CLERK    EVA          PULASKI         D21
     DANIEL       SMITH           CLERK    EVA          PULASKI         D21
     SALVATORE    MARINO          CLERK    EVA          PULASKI         D21
     ETHEL        SCHNEIDER       OPERATOR EILEEN       HENDERSON       E11
     MAUDE        SETRIGHT        OPERATOR EILEEN       HENDERSON       E11
     PHILIP       SMITH           OPERATOR EILEEN       HENDERSON       E11
     JOHN         PARKER          OPERATOR EILEEN       HENDERSON       E11
     RAMLAL       MEHTA           FIELDREP THEODORE     SPENSER         E21
     JASON        GOUNOT          FIELDREP THEODORE     SPENSER         E21
     WING         LEE             FIELDREP THEODORE     SPENSER         E21

相关子查询

允许引用先前提到的任何表的子查询称为相关子查询。我们也说该子查询具有对主查询中表的相关引用

下列示例是一个不相关子查询,该子查询列出部门 'A00' 中薪水超过该部门平均薪水的雇员的雇员编号和姓名:

 
     SELECT EMPNO, LASTNAME
        FROM EMPLOYEE
        WHERE WORKDEPT = 'A00'
          AND SALARY > (SELECT AVG(SALARY)
                           FROM EMPLOYEE
                           WHERE WORKDEPT = 'A00')

如果想要知道每个部门的平均薪水,则需要对每个部门计算一次子查询。对在外层查询中标识的表的每一行,各使用一次 SQL 的相关功能(该能力允许您编写重复执行的子查询),就可做到这一点。此类型的相关子查询用来计算外层表的每一行的某个特性,该特性是在子查询中计算谓词所需要的。

此示例显示薪水高于部门平均薪水的所有雇员:

 
     SELECT E1.EMPNO, E1.LASTNAME, E1.WORKDEPT
        FROM EMPLOYEE E1
        WHERE SALARY > (SELECT AVG(SALARY)
                           FROM EMPLOYEE E2
                           WHERE E2.WORKDEPT = E1.WORKDEPT)
        ORDER BY E1.WORKDEPT

在此查询中,对每个部门计算一次子查询。结果为:

     EMPNO  LASTNAME        WORKDEPT
     ------ --------------- --------
     000010 HAAS            A00
     000110 LUCCHESSI       A00
     000030 KWAN            C01
     000060 STERN           D11
     000220 LUTZ            D11
     000200 BROWN           D11
     000170 YOSHIMURA       D11
     000150 ADAMSON         D11
     000070 PULASKI         D21
     000270 PEREZ           D21
     000240 MARINO          D21
     000090 HENDERSON       E11
     000280 SCHNEIDER       E11
     000100 SPENSER         E21
     000340 GOUNOT          E21
     000330 LEE             E21

要编写带有相关子查询的查询,使用与带有子查询的普通外部查询相同的基本格式。然而,在外部查询的 FROM 子句中,只是在表名后面放置一个相关名。于是子查询可能包含由该相关名限定的列引用。例如,如果 E1 是相关名,则 E1.WORKDEPT 表示外部查询中表的当前行的工作部门值。在外部查询中对表的每一行(概念上)重新计算子查询。

通过使用相关子查询,可以使系统为您工作并减少需要在应用程序中编写的代码量。

DB2 中允许非限定相关引用。例如,表 EMPLOYEE 有一个命名为 LASTNAME 的列,表 SALES 有一个命名为 SALES_PERSON 的列,但没有命名为 LASTNAME 的列。

 
     SELECT LASTNAME, FIRSTNME, COMM
        FROM EMPLOYEE
        WHERE 3 > (SELECT AVG(SALES)
                      FROM SALES
                      WHERE LASTNAME = SALES_PERSON)

在此示例中,系统检查最内层的 FROM 子句,以获取 LASTNAME 列。如果未找到 LASTNAME 列,则系统检查次最内层的 FROM 子句(此情况下为外部 FROM 子句)。虽然不总是必要的,还是建议限定相关引用以改进查询的可读性并确保获取想要的结果。

实现相关子查询

想何时使用相关子查询?列函数的使用有时是一条线索。

假定您想要列出教育级别高于部门平均值的雇员。

首先,您必须确定选择列表项。问题为 "List the employees"。这隐含着来自 EMPLOYEE 表中的 EMPNO 应该足以唯一标识雇员。该问题也将 EDLEVEL 和雇员的部门 WORKDEPT 说明为条件。当问题未明确要求显示列时,在选择列表中包括这些列将会有助于说明解法。现在可构造查询的一部分:

     SELECT LASTNAME, WORKDEPT, EDLEVEL
        FROM EMPLOYEE

接着需要搜索条件(WHERE子句)。问题语句为 "...whose level of education is higher than the average for that employee's department"。这意味着对于表中每个雇员,必须计算该雇员所在部门的平均教育级别。此语句适合相关子查询的说明。正在对每行计算某个特性(当前雇员所在部门的平均教育级别)。 EMPLOYEE 表需要相关名:

     SELECT LASTNAME, WORKDEPT, EDLEVEL
        FROM EMPLOYEE E1

需要的子查询较简单。该子查询计算每个部门的平均教育级别。完整的 SQL 语句为:

     SELECT LASTNAME, WORKDEPT, EDLEVEL
        FROM EMPLOYEE E1
        WHERE EDLEVEL > (SELECT AVG(EDLEVEL)
                            FROM EMPLOYEE  E2
                            WHERE E2.WORKDEPT = E1.WORKDEPT)

结果为:

     LASTNAME        WORKDEPT EDLEVEL
     --------------- -------- -------
     HAAS            A00           18
     KWAN            C01           20
     PULASKI         D21           16
     HENDERSON       E11           16
     LUCCHESSI       A00           19
     PIANKA          D11           17
     SCOUTTEN        D11           17
     JONES           D11           17
     LUTZ            D11           18
     MARINO          D21           17
     JOHNSON         D21           16
     SCHNEIDER       E11           17
     MEHTA           E21           16
     GOUNOT          E21           16

假定不列出雇员的部门编号,则应列出部门名称。需要的信息(DEPTNAME)在独立表(DEPARTMENT)中。定义相关变量的外层查询也可以是连接查询(参见从多个表中选择数据以了解详情)。

当在外层查询中使用连接时,列出要在 FROM 子句中连接的表,并将相关名放在这些表名的任何一个表名旁边。

要修改查询以列出部门名称而不是部门编号,在选择列表中用 DEPTNAME 替换 WORKDEPT。 FROM 子句现在也必须包括 DEPARTMENT 表,并且 WHERE 子句必须表示适当的连接条件。

以下是修改的查询:

     SELECT LASTNAME, DEPTNAME, EDLEVEL
        FROM EMPLOYEE E1, DEPARTMENT
        WHERE E1.WORKDEPT = DEPARTMENT.DEPTNO
        AND EDLEVEL > (SELECT AVG(EDLEVEL)
                          FROM EMPLOYEE E2
                          WHERE E2.WORKDEPT = E1.WORKDEPT)

上例显示,必须在包含相关子查询的某个查询的 FROM 子句中定义用于子查询中的相关名。然而,这种包含可能涉及若干层嵌套。

假定某些部门只有几个雇员,因此这些部门的平均教育级别可能是错误的。可以决定,为了使平均教育级别在用于比较雇员时是有意义的数字,一个部门中必须至少有 5 个雇员。因此现在必须列出教育级别高于雇员所在部门平均值的雇员,并只考虑至少有 5 个雇员的部门。

该问题暗含另一个子查询,因为对于外层查询中每个雇员来说,必须计算该雇员所在部门的雇员总数:

     SELECT COUNT(*)
        FROM EMPLOYEE E3
        WHERE E3.WORKDEPT = E1.WORKDEPT

仅当计数大于或等于 5 时才计算平均值:

     SELECT AVG(EDLEVEL)
        FROM EMPLOYEE E2
        WHERE E2.WORKDEPT = E1.WORKDEPT
        AND 5 <= (SELECT COUNT(*)
                     FROM EMPLOYEE  E3
                     WHERE E3.WORKDEPT = E1.WORKDEPT)

最后,只包括其教育级别高于部门平均值的那些雇员:

     SELECT LASTNAME, DEPTNAME, EDLEVEL
        FROM EMPLOYEE E1, DEPARTMENT
        WHERE E1.WORKDEPT = DEPARTMENT.DEPTNO
        AND EDLEVEL >
        (SELECT AVG(EDLEVEL)
            FROM EMPLOYEE E2
            WHERE E2.WORKDEPT = E1.WORKDEPT
            AND 5 <=
            (SELECT COUNT(*)
                FROM EMPLOYEE E3
                WHERE E3.WORKDEPT = E1.WORKDEPT))

此语句产生下列结果:

     LASTNAME        DEPTNAME                      EDLEVEL
     --------------- ----------------------------- -------
     PIANKA          MANUFACTURING SYSTEMS              17
     LUTZ            MANUFACTURING SYSTEMS              18
     JONES           MANUFACTURING SYSTEMS              17
     SCOUTTEN        MANUFACTURING SYSTEMS              17
     PULASKI         ADMINISTRATION SYSTEMS             16
     JOHNSON         ADMINISTRATION SYSTEMS             16
     MARINO          ADMINISTRATION SYSTEMS             17
     HENDERSON       OPERATIONS                         16
     SCHNEIDER       OPERATIONS                         17

posted @ 2006-01-10 20:26 呓语的博客 阅读(316) | 评论 (0)编辑 收藏

连接数据库

您必须先与数据库连接,才能使用 SQL 语句来查询或操作该数据库。CONNECT 语句使数据库连接与用户名相关联。

例如,要连接 SAMPLE 数据库,在 DB2 命令行处理器中输入下列命令:

  CONNECT TO SAMPLE USER USERID USING  PASSWORD

(确保选择服务器系统上有效的 USER 和 USING 值)。

在此示例中,USER 的值为 USERID,USING 的值为 PASSWORD。

下列信息告诉您连接成功:

        数据库连接信息
                                                    
           数据库产品             = DB2/6000 6.0.0
           SQL 权限 ID            = USERID
           本地数据库别名         = SAMPLE
 

通过 CONNECT 语句设置连接时,建立显式连接。在隐式连接中已设置缺省服务器。在此情况下可以使用 CONNECT,或开始发出语句,将自动建立连接。

一旦连接上,就可以开始操作数据库。有关隐式和显式连接的详情,参考 SQL Reference 中的 CONNECT 语句。

调查错误

无论何时在任何示例中输入出错时,或者如果执行 SQL 语句期间出错,则数据库管理程序返回错误信息。错误信息由信息标识符、简要说明以及 SQLSTATE 组成。

SQLSTATE 是 DB2 系列产品的公共错误码。 SQLSTATE 符合 ISO/ANSI SQL92 标准。

例如,如果 CONNECT 语句中用户名或口令不正确,则数据库管理程序将返回信息标识符 SQL1403N 和 SQLSTATE 为 08004。该信息如下:

     SQL1403N 提供的用户名和/或口令不正确。  SQLSTATE=08004

可以通过输入一个问号(?),然后输入信息标识符或 SQLSTATE 来获取关于错误信息的更多信息:

        ? SQL1403N
或
        ? SQL1403
或
        ? 08004

注意:错误 SQL1403N 的说明中倒数第二行表明 SQLCODE为-1403。 SQLCODE 为产品特定错误码。以 N(通知)或 C(严重)结尾的信息标识符表示一个错误,并且具有负 SQLCODE。以 W(警告)结尾的信息标识符表示一个警告,并且具有正 SQLCODE。

选择列

使用 SELECT 语句从表中选择特定的列。在该语句中指定用逗号分隔的列名列表。此列表称为选择列表

下列语句从 SAMPLE 数据库的 ORG 表中选择部门名称 (DEPTNAME) 和部门号 (DEPTNUMB):

 
     SELECT DEPTNAME, DEPTNUMB
        FROM ORG

上面语句产生下列结果:

 
     DEPTNAME       DEPTNUMB
     -------------- --------
     Head Office          10
     New England          15
     Mid Atlantic         20
     South Atlantic       38
     Great Lakes          42
     Plains               51
     Pacific              66
     Mountain             84
 

通过使用星号 (*) 可从表中选择所有列。下一个示例列出 ORG 表中的所有的列和行:

   
     SELECT *
        FROM ORG

此语句产生下列结果:

       DEPTNUMB DEPTNAME       MANAGER DIVISION   LOCATION
       -------- -------------- ------- ---------- -------------
             10 Head Office        160 Corporate  New York
             15 New England         50 Eastern    Boston
             20 Mid Atlantic        10 Eastern    Washington
             38 South Atlantic      30 Eastern    Atlanta
             42 Great Lakes        100 Midwest    Chicago
             51 Plains             140 Midwest    Dallas
             66 Pacific            270 Western    San Francisco
             84 Mountain           290 Western    Denver

选择行

要从表中选择特定行,在 SELECT 语句之后使用 WHERE 子句指定要选择的行必须满足的条件。从表中选择行的标准是搜索条件

搜索条件由一个或多个谓词组成。谓词指定关于某一行是真或是假(或未知)的条件。可使用下列基本谓词在 WHERE 子句中指定条件:


谓词 功能
x = y x 等于 y
x <> y x 不等于 y
x < y x 小于 y
x > y x 大于 y
x <= y x 小于或等于 y
x >= y x 大于或等于 y
IS NULL/IS NOT NULL 测试空值

在构造搜索条件时,要注意只对数字数据类型执行算术运算,并只在相容数据类型之间进行比较。例如,不能将字符串与数字进行比较。

如果正在基于字符值来选择行,则该值必须用单引号括起来(例如,WHERE JOB = 'Clerk'),并且输入的每个字符值必须与数据库中的完全一样。如果数据值在数据库中是小写的,而您用大写形式来输入它,则将不选择行。如果正在基于数字值来选择行,则该值不得用引号括起来(例如,WHERE DEPT = 20)。

下列示例只从 STAFF 表中选择部门 20 的行:

     SELECT DEPT, NAME, JOB
        FROM STAFF
        WHERE DEPT = 20

此语句产生下列结果:

      DEPT   NAME      JOB
      ------ --------- -----
          20 Sanders   Mgr
          20 Pernal    Sales
          20 James     Clerk
          20 Sneider   Clerk

下一示例使用 AND 来指定多个条件。可以指定任意多个条件。该示例从 STAFF 表中选择部门 20 中的 clerk:

   
     SELECT DEPT, NAME, JOB
        FROM STAFF
        WHERE JOB = 'Clerk'
        AND DEPT = 20

此语句产生下列结果:

     DEPT   NAME      JOB
     ------ --------- -----
         20 James     Clerk
         20 Sneider   Clerk
 

未在其中输入值且不支持缺省值的列中出现空值。将值特别设置为空值的地方也可以出现空值。空值只能在定义为支持空值的列中出现。在表中定义和支持空值在创建表中讨论。

使用谓词 IS NULL 和 IS NOT NULL 来检查空值。

下列语句列出佣金未知的雇员:

     SELECT ID, NAME
        FROM STAFF
        WHERE COMM IS NULL

此语句产生下列结果:

     ID     NAME
     ------ ---------
         10 Sanders
         30 Marenghi
         50 Hanes
        100 Plotz
        140 Fraye
        160 Molinare
        210 Lu
        240 Daniels
        260 Jones
        270 Lea
        290 Quill
        

值零与空值不相同。下列语句选择表中佣金为零的每个人:

     SELECT ID, NAME
        FROM STAFF
        WHERE COMM = 0
     

因为样本表中的 COMM 列中没有零值,所以返回的结果集为空。

下一个示例选择 STAFF 表中 YEARS 的值大于 9 的所有行:

     SELECT NAME, SALARY, YEARS
        FROM STAFF
        WHERE YEARS > 9
     

此语句产生下列结果:

     NAME      SALARY    YEARS
     --------- --------- ------
     Hanes      20659.80     10
     Lu         20010.00     10
     Jones      21234.00     12
     Quill      19818.00     10
     Graham     21000.00     13

将行进行排序

您可能想要信息按特定次序返回。使用 ORDER BY 子句将信息按一个或多个列中的值进行排序。

下列语句显示部门 84 中按雇用年数排序的雇员:

     SELECT NAME, JOB, YEARS
        FROM STAFF
        WHERE DEPT = 84
        ORDER BY YEARS

此语句产生下列结果:

     NAME      JOB   YEARS
     --------- ----- ------
     Davis     Sales      5
     Gafney    Clerk      5
     Edwards   Sales      7
     Quill     Mgr       10

指定 ORDER BY 作为整个 SELECT 语句中的最后一个子句。在此子句中命名的列可以是表达式或表的任何列。ORDER BY 子句中的列名不必在选择列表中指定。

可通过在 ORDER BY 子句中显式指定 ASC 或 DESC 将行按升序或降序进行排序。如果既未指定 ASC,也未指定 DESC,则自动按升序将行进行排序。下列语句按雇用年数以降序显示部门 84 中的雇员:

     SELECT NAME, JOB, YEARS
        FROM STAFF
        WHERE DEPT = 84
        ORDER BY YEARS DESC

此语句产生下列结果:

     NAME      JOB   YEARS
     --------- ----- ------
     Quill     Mgr       10
     Edwards   Sales      7
     Davis     Sales      5
     Gafney    Clerk      5

可以按字符值以及数字值将行进行排序。下列语句按姓名字母顺序显示部门 84 的雇员:

     SELECT NAME, JOB, YEARS
        FROM STAFF
        WHERE DEPT = 84
        ORDER BY NAME

此语句产生下列结果:

 
     NAME      JOB   YEARS
     --------- ----- ------
     Davis     Sales      5
     Edwards   Sales      7
     Gafney    Clerk      5
     Quill     Mgr       10

除去重复行

当使用 SELECT 语句时,您可能不想要返回重复信息。例如,STAFF 有一个其中多次列出了几个部门编号的 DEPT 列,以及一个其中多次列出了几个工作说明的 JOB 列。

要消除重复行,在 SELECT 子句上使用 DISTINCT 选项。例如,如果将 DISTINCT 插入该语句,则部门中的每项工作仅列出一次:

     SELECT DISTINCT DEPT, JOB
        FROM STAFF
        WHERE DEPT < 30
        ORDER BY DEPT, JOB

此语句产生下列结果:

     DEPT   JOB
     ------ -----
         10 Mgr
         15 Clerk
         15 Mgr
         15 Sales
         20 Clerk
         20 Mgr
         20 Sales

DISTINCT 已消除了在 SELECT 语句中指定的一组列中所有包含重复数据的行。

运算次序

考虑运算次序是很重要的。一个子句的输出是下一个子句的输入,如下面列表中所示。给表达式命名中给出一个要考虑其中运算次序的示例。

并且注意,此说明允许以一种更直观的方式对查询进行考虑。此说明不一定是在内部执行运算的方式。运算顺序如下:

  1. FROM 子句

  2. WHERE 子句

  3. GROUP BY 子句

  4. HAVING 子句

  5. SELECT 子句

使用表达式来计算值

表达式是包括在语句中的计算或函数。下列语句计算,如果部门 38 中每个雇员都收到 $500 的奖金,则每人的薪水将是多少:

  
     SELECT DEPT, NAME, SALARY + 500
        FROM STAFF
        WHERE DEPT = 38
        ORDER BY 3

此结果为:

     DEPT   NAME      3
     ------ --------- ----------------
         38 Abrahams          12509.75
         38 Naughton          13454.75
         38 Quigley           17308.30
         38 Marenghi          18006.75
         38 O'Brien           18506.00

注意第三列的列名是一个数字。这是一个系统生成的数字,因为 SALARY+500 未指定列名。以后此数字在 ORDER BY 子句中用来表示第三列。给表达式命名论及如何给表达式取有意义的名称。

可使用基本算术运算符加(+)、减(-)、乘(*)、除(/)来形成算术表达式。

这些运算符可对几种不同类型操作数的值进行运算,其中某些操作数为:

  • 列名(例如在 RATE*HOURS 中)
  • 常数值(例如在 RATE*1.07 中)
  • 标量函数(例如在 LENGTH(NAME) + 1 中)。

给表达式命名

可选的 AS 子句允许您给表达式指定有意义的名称,这就使得以后再引用该表达式更容易。可使用 AS 子句为选择列表中的任何项提供名称。

下列语句显示其薪水加佣金少于 $13,000 的所有雇员。表达式 SALARY + COMM 命名为 PAY:

     SELECT NAME, JOB, SALARY + COMM AS PAY
        FROM STAFF
        WHERE (SALARY + COMM) < 13000
        ORDER BY PAY

此语句产生下列结果:

     NAME      JOB   PAY
     --------- ----- ----------
     Yamaguchi Clerk   10581.50
     Burke     Clerk   11043.50
     Scoutten  Clerk   11592.80
     Abrahams  Clerk   12246.25
     Kermisch  Clerk   12368.60
     Ngan      Clerk   12714.80

通过使用 AS 子句,可以在 ORDER BY 子句中引用特定的列名而不是系统生成的数字。在此示例中,我们在 WHERE 子句中将(SALARY + COMM)与 13000 进行比较,而不是使用名称 PAY。这是运算次序的结果。在将(SALARY + COMM)命名为 PAY 之前计算 WHERE 子句的值。因此,不能在该谓词中使用 PAY。

从多个表中选择数据

可使用 SELECT 语句从两个或多个表中生成包含信息的报告。这通常称为 连接。例如,可以连接 STAFF 和 ORG 表中的数据以形成一个新表。要连接两个表,在 SELECT 子句中指定想要显示的列,在 FROM 子句中指定表名,在 WHERE 子句中指定搜索条件。WHERE 子句是可选的。

下一个示例使每个经理的姓名与部门名称关联。需要从两个表中选择信息,因为雇员信息(STAFF 表)和部门信息(ORG 表)是独立存储的。下列查询分别选择 STAFF 和 ORG 表的 NAME 和 DEPTNAME 列。搜索条件将选择范围缩小为 MANAGER 列中的值与 ID 列中的值相同的行:

     SELECT DEPTNAME, NAME
        FROM ORG, STAFF
        WHERE MANAGER = ID

图 3演示如何比较两个不同表中的列。加框线的值表示满足搜索条件的匹配项。

图 3. 从 STAFF 和 ORG 表中选择

SELECT 语句产生下列结果:

     DEPTNAME       NAME
     -------------- --------- 
     Mid Atlantic   Sanders
     South Atlantic Marenghi
     New England    Hanes
     Great Lakes    Plotz
     Plains         Fraye
     Head Office    Molinare
     Pacific        Lea
     Mountain       Quill

该结果列出每个经理的姓名和他或她的部门。

使用子查询

在编写 SELECT 语句时,可在 WHERE 子句中放置另一个 SELECT 语句。每个附加的 SELECT 启动一个子查询

子查询本身又可包括其值代入其 WHERE 子句的另一个子查询。另外,WHERE 子句可将子查询包括在多个搜索条件中。子查询可引用与主查询中所使用的不同的表和列。

下列语句从 ORG 表中选择 STAFF 表中其 ID 为 280 的雇员的分部和位置:

     SELECT DIVISION, LOCATION
        FROM ORG
        WHERE DEPTNUMB = (SELECT DEPT
                             FROM STAFF
                             WHERE ID = 280)

在处理此语句时,DB2 首先确定子查询的结果。结果为 66,因为具有 ID 280 的雇员在部门 66。则最终结果从其部门号列具有值 66 的 ORG 表的行中得出。最终结果是:

     DIVISION   LOCATION
     ---------- -------------
     Western    San Francisco

当使用子查询时,数据库管理程序计算该子查询并将结果值直接代入 WHERE 子句。

相关子查询中进一步讨论子查询。

使用函数

本节简要介绍了将用于全书示例的函数。 数据库函数是一组输入数据值和一个结果值之间的关系。

函数可以是内部的或用户定义的。 DB2 通用数据库提供很多内部函数和预安装的用户定义函数。可找到 SYSIBM 模式的内部函数和 SYSFUN 模式的预安装的用户定义函数。SYSIBM 和 SYSFUN 是保留模式。

内部函数和预安装的用户定义函数从不会满足所有需求。所以应用程序开发者可能需要创建自己的一套特定于他们的应用程序的函数。用户定义函数使这成为可能,例如通过扩展 DB2 通用数据库的范围以包括定制的商业或科学函数。这在用户定义函数中进一步讨论。

列函数

列函数对列中的一组值进行运算以得到单个结果值。下列就是一些列函数的示例。有关完整列表,参考 SQL Reference

AVG
返回某一组中的值除以该组中值的个数的和

COUNT
返回一组行或值中行或值的个数

MAX
返回一组值中的最大值

MIN
返回一组值中的最小值

下列语句从 STAFF 表中选择最高薪水:

     SELECT MAX(SALARY)
        FROM STAFF

此语句从员工 STAFF 样本表中返回值 22959.20。

下一个示例选择其收入超过平均收入但在公司的年数少于平均年数的雇员姓名和薪水。

     SELECT NAME, SALARY
        FROM STAFF
        WHERE SALARY > (SELECT AVG(SALARY) FROM STAFF)
        AND YEARS < (SELECT AVG(YEARS) FROM STAFF)

此语句产生下列结果:

      NAME      SALARY
     --------- ---------
     Marenghi   17506.75
     Daniels    19260.25
     Gonzales   16858.20

在上面示例中的 WHERE 子句中,与直接实现列函数 (WHERE SALARY > AVG(SALARY)) 相反,在子查询中说明列函数。不能在 WHERE 子句中说明列函数。这是由于运算次序的结果。可认为 WHERE 子句是在 SELECT 子句之前进行计算的。因此,当正在计算 WHERE 子句时,列函数没有对该组值的存取权。稍后由 SELECT 子句选择这组值。

可指定 DISTINCT 作为列函数自变量的一部分,以在应用函数之前消除重复值。因此,COUNT(DISTINCT WORKDEPT) 计算不同部门的个数。

标量函数

标量函数对值进行某个运算以返回另一个值。下列就是一些由DB2 通用数据库提供的标量函数的示例。

ABS
返回数的绝对值

HEX
返回值的十六进制表示

LENGTH
返回自变量中的字节数(对于图形字符串则返回双字节字符数。)

YEAR
抽取日期时间值的年份部分

有关标量函数的详细列表和说明,参考 SQL Reference

下列语句返回 ORG 表中的部门名称以及其每个名称的长度:

     SELECT DEPTNAME, LENGTH(DEPTNAME)
        FROM ORG

此语句产生下列结果:

     DEPTNAME       2
     -------------- -----------
     Head Office             11
     New England             11
     Mid Atlantic            12
     South Atlantic          14
     Great Lakes             11
     Plains                   6
     Pacific                  7
     Mountain                 8

注意:由于未使用 AS 子句给 LENGTH(DEPTNAME) 取一个有意义的名称,所以第二列中出现系统生成的数字。

分组

DB2 通用数据库具有基于表的特定列对数据进行分析的能力。

可按照在 GROUP BY 子句中定义的组对行进行分组。以其最简单的形式,组由称为分组列的列组成。 SELECT 子句中的列名必须为分组列或列函数。列函数对于 GROUP BY 子句定义的每个组各返回一个结果。下列示例产生一个列出每个部门编号的最高薪水的结果:

     SELECT DEPT, MAX(SALARY) AS MAXIMUM
        FROM STAFF
        GROUP BY DEPT

此语句产生下列结果:

     DEPT   MAXIMUM
     ------ ---------
         10  22959.20
         15  20659.80
         20  18357.50
         38  18006.00
         42  18352.80
         51  21150.00
         66  21000.00
         84  19818.00

注意:计算的是每个部门(由 GROUP BY 子句定义的组)而不是整个公司的 MAX(SALARY)。

将 WHERE 子句与 GROUP BY 子句一起使用

分组查询可以在形成组和计算列函数之前具有消除非限定行的标准 WHERE 子句。必须GROUP BY 子句之前指定 WHERE 子句。例如:

     SELECT WORKDEPT, EDLEVEL, MAX(SALARY) AS MAXIMUM
        FROM EMPLOYEE
        WHERE HIREDATE > '1979-01-01'
        GROUP BY WORKDEPT, EDLEVEL
        ORDER BY WORKDEPT, EDLEVEL
 

结果为:

     WORKDEPT EDLEVEL MAXIMUM
     -------- ------- -----------
     D11           17    18270.00
     D21           15    27380.00
     D21           16    36170.00
     D21           17    28760.00
     E11           12    15340.00
     E21           14    26150.00

注意:在 SELECT 语句中指定的每个列名也在 GROUP BY 子句中提到。未在这两个地方提到的列名将产生错误。GROUP BY 子句对 WORKDEPT 和 EDLEVEL 的每个唯一组合各返回一行。

在 GROUP BY 子句之后使用 HAVING 子句

可应用限定条件进行分组,以便系统仅对满足条件的组返回结果。为此,GROUP BY 子句后面包含一个 HAVING 子句。 HAVING 子句可包含一个或多个用 AND 和 OR 连接的谓词。每个谓词将组特性(如 AVG(SALARY))与下列之一进行比较:

  • 该组的另一个特性

    例如:

    HAVING AVG(SALARY) > 2 * MIN(SALARY)
    

  • 常数

    例如:

     HAVING AVG(SALARY) > 20000
    

例如,下列查询寻找雇员数超过 4 的部门的最高和最低薪水:

     SELECT WORKDEPT, MAX(SALARY) AS MAXIMUM, MIN(SALARY) AS MINIMUM
        FROM EMPLOYEE
        GROUP BY WORKDEPT
        HAVING COUNT(*) > 4
        ORDER BY WORKDEPT

此语句产生下列结果:

     WORKDEPT MAXIMUM     MINIMUM
     -------- ----------- -----------
     D11         32250.00    18270.00
     D21         36170.00    17250.00
     E11         29750.00    15340.00

有可能(虽然很少见)查询有 HAVING 子句但没有 GROUP BY 子句。在此情况下,DB2 将整个表看作一个组。因为该表被看作是单个组,所以最多可以有一个结果行。如果 HAVING 条件对整个表为真,则返回选择的结果(该结果必须整个由列函数组成);否则不返回任何行。

posted @ 2006-01-10 20:24 呓语的博客 阅读(544) | 评论 (0)编辑 收藏

创建表

使用 CREATE TABLE 语句创建自己的表,指定列名和类型以及约束。约束在用约束和触发器实施商业规则中讨论。

下列语句创建一个命名为 PERS 的表,该表与 STAFF 表类似,但有一个附加列 BIRTH_DATE 。

     CREATE TABLE PERS
         ( ID              SMALLINT        NOT NULL,
           NAME            VARCHAR(9),
           DEPT            SMALLINT WITH DEFAULT 10,
           JOB             CHAR(5),
           YEARS           SMALLINT,
           SALARY          DECIMAL(7,2),
           COMM            DECIMAL(7,2),
           BIRTH_DATE      DATE) 

此语句创建一个其中无数据的表。下一节描述如何将数据插入新表。

如示例中所示,为每一列都指定名称和数据类型。数据类型在数据类型中讨论。 NOT NULL 是可选的,可以指定它以表示列中不允许有空值。缺省值也是可选的。

可以在 CREATE TABLE 语句中指定许多其他选项,如唯一约束或参考约束。有关所有选项的详情,参见 SQL Reference 中的 CREATE TABLE 语句。

插入数据

当创建新表时,新表不包含任何数据。要将新的行输入表中,使用 INSERT 语句。此语句有两种一般格式:

  • 一种格式,使用 VALUES 子句来指定一行或多行的列值。下面三个示例使用此一般格式将数据插入表中。

  • 另一种格式,指定全查询而非指定 VALUES 来标识来自包含在其他表和/或视图中的行的列。

全查询是 INSERT 或 CREATE VIEW 语句中所使用的选择语句、或者是跟在谓词后面的选择语句。括在括号中的全查询一般称为子查询。

根据创建表时已选择的缺省选项,对于每个插入的行,为每一列提供一个值或者接受一个缺省值。各种数据类型的缺省值在 SQL Reference 中讨论。

下列语句使用 VALUES 子句将一行数据插入 PERS 表中:

  
     INSERT INTO PERS
        VALUES (12, 'Harris', 20, 'Sales', 5, 18000, 1000, '1950-1-1')
     

下列语句使用 VALUES 子句将三行插入其中只有 ID、名称以及工作是已知的 PERS 表中。如果列定义为 NOT NULL 且没有缺省值,则必须为该列指定一个值。 CREATE TABLE 语句中的列定义上的 NOT NULL 子句可以用单词 WITH DEFAULT 扩充。如果某一列定义为 NOT NULL WITH DEFAULT 或常数缺省值(如 WITH DEFAULT 10),并且您未在列列表中指定该列,则缺省值插入至已插入行的该列中。例如,在 CREATE TABLE 语句中,仅为 DEPT 列指定了缺省值并将该值定义为 10。因此,DEPT 设置为 10 而所有其他列都为空。

 
     INSERT INTO PERS (NAME, JOB, ID)
        VALUES ('Swagerman', 'Prgmr', 500),
               ('Limoges', 'Prgmr', 510),
               ('Li', 'Prgmr', 520)

下列语句返回插入的结果:

     SELECT *
        FROM PERS
 
     ID     NAME      DEPT   JOB   YEARS  SALARY    COMM      BIRTH_DATE
     ------ --------- ------ ----- ------ --------- --------- ----------
         12 Harris        20 Sales      5  18000.00   1000.00 01/01/1950
        500 Swagerman     10 Prgmr      -         -         - -
        510 Limoges       10 Prgmr      -         -         - -
        520 Li            10 Prgmr      -         -         - -

注意:在此情况下,并未给每个列指定值。空值显示为 -。为此,列名列表的次序和数据类型都必须与 VALUES 子句中提供的值对应。如果省略列名列表(如第一个示例中那样),则 VALUES 之后的数据值列表的次序必须与它们所插入至的表中的列次序相同,值的数目必须等于表中列的数目。

每个值必须与它所插入至的列的数据类型相容。如果某列定义为可空,且未指定该列的值,则将空值赋给插入行中的该列。

下列示例将空值插入 YEARS、COMM 和 BIRTH_DATE 中,因为未给行中的那些列指定值。

     INSERT INTO PERS (ID, NAME, JOB, DEPT, SALARY)
        VALUES (410, 'Perna', 'Sales', 20, 20000)

INSERT 语句的第二种格式对于用来自另一表中行的值填充表非常方便。如所述的那样,指定全查询而非指定 VALUES 以标识来自包含在其他表和/或视图中的行中的列。

下列示例从员工 STAFF 表中选择部门 38 的成员的数据,并将它插入 PERS 表中:

  
     INSERT INTO PERS (ID, NAME, DEPT, JOB, YEARS, SALARY)
        SELECT ID, NAME, DEPT, JOB, YEARS, SALARY
           FROM STAFF
           WHERE DEPT = 38

在此插入之后,下列 SELECT 语句与 INSERT 语句中全查询产生的结果相同。

     SELECT ID, NAME, DEPT, JOB, YEARS, SALARY
        FROM PERS
        WHERE DEPT = 38
 

结果为:

                                                                               
ID     NAME      DEPT   JOB   YEARS  SALARY
------ --------- ------ ----- ------ ---------
    30 Marenghi      38 Mgr        5  17506.75
    40 O'Brien       38 Sales      6  18006.00
    60 Quigley       38 Sales      -  16808.30
   120 Naughton      38 Clerk      -  12954.75
   180 Abrahams      38 Clerk      3  12009.75

更改数据

使用 UPDATE 语句来更改表中的数据。使用此语句,可以更改满足 WHERE 子句搜索条件的每行中的一列或多列的值。

下列示例更新其 ID 为 410 的雇员的信息:

     UPDATE PERS
        SET JOB='Prgmr', SALARY = SALARY + 300
        WHERE ID = 410

SET 子句指定要更新的列并提供值。

WHERE 子句是可选的,它指定要更新的行。如果省略 WHERE 子句,则数据库管理程序用您提供的值更新表或视图中的每一行。

在此示例中,首先命名表 (PERS),然后指定要更新行的条件。雇员编号 410 的信息已更改:该雇员的工作职位更改为 Prgmr,它的薪水增加了 300$。

可以通过包括应用于两行或更多行的 WHERE 子句来更改多行数据。下列示例给每个销售员的薪水增加 15%:

     UPDATE PERS
        SET SALARY = SALARY * 1.15
        WHERE JOB = 'Sales'

删除数据

使用 DELETE 语句,基于在 WHERE 子句中指定的搜索条件从表中删除数据行。下列示例删除其中雇员 ID 为 120 的行:

 
     DELETE FROM PERS
        WHERE ID = 120

WHERE 子句是可选的,它指定要删除的行。如果省略 WHERE 子句,则数据库管理程序删除表或视图中的所有行。

可以使用 DELETE 语句删除多行。下列示例删除其中雇员部门 (DEPT) 为 20 的所有行:

     DELETE FROM PERS
        WHERE DEPT = 20

当删除某一行时,是除去整行,而不是除去行中的特定列值。

要删除表的定义及其内容,发出 DROP TABLE 语句,如 SQL Reference 中所述。

创建视图

视图中所讨论的,视图提供在一个或多个表中查看数据的替代方法。通过创建视图,可以对想要各种用户查看的信息进行限制。下列图表显示视图和表之间的关系。

图 2. 表和视图之间的关系

图 2中,View_A 被限制仅存取 TABLE_A 的列 AC1 和 AC2。 View_AB 允许存取 TABLE_A 中的列 AC3 和 TABLE_B 中的列 BC2。通过创建 View_A,将用户可以具有的存取权限制于 TABLE_A,通过创建 VIEW_AB,将存取权限制于某些列并创建查看数据的替代方式。

下列语句创建 STAFF 表 中 20 部门的非经理人员视图,其中薪水和佣金不通过基表显示。

  
     CREATE VIEW STAFF_ONLY
        AS SELECT ID, NAME, DEPT, JOB, YEARS
              FROM STAFF
              WHERE JOB <> 'Mgr' AND DEPT=20

在创建视图之后,下列语句显示视图的内容:

     SELECT *
        FROM STAFF_ONLY

此语句产生下列结果:

      
ID     NAME      DEPT   JOB   YEARS
------ --------- ------ ----- ------
    20 Pernal        20 Sales      8
    80 James         20 Clerk      -
   190 Sneider       20 Clerk      8

早些时候,我们把 STAFF 和 ORG 表连接起来产生一个列出每个部门名称及其部门经理姓名的结果。下列语句创建可重复用于相同目的的视图:

  
     CREATE VIEW DEPARTMENT_MGRS
        AS SELECT NAME, DEPTNAME
              FROM STAFF, ORG
              WHERE MANAGER = ID

创建视图时,可以使用 WITH CHECK OPTION 子句将附加约束添加到通过视图插入和更新表。此子句导致数据库管理程序验证对视图的任何更新或插入是否符合该视图的定义,并拒绝那些不符合定义的更新或插入。如果省略此子句,则不检查违反视图定义的插入和更新。有关 WITH CHECK OPTION 如何起作用的详情,参考 SQL Reference 中的 CREATE VIEW 语句。

使用视图来处理数据

象 SELECT 语句一样,INSERT、DELETE 以及 UPDATE 语句可以应用于视图,就好象视图是一个实表一样。这些语句处理基本基表中的数据。因此当再次存取该视图时,使用最新的基表对它进行计算。如果未使用 WITH CHECK OPTION,则使用视图修改的数据可能由于不再满足原始视图定义而不在视图的重复存取中出现。

下列是一个将更新应用于视图 FIXED_INCOME 的示例:

FIXED_INCOME 的视图定义:

     CREATE VIEW FIXED_INCOME (LNAME, DEPART, JOBTITLE, NEWSALARY)
        AS SELECT NAME, DEPT, JOB, SALARY
              FROM PERS
              WHERE JOB <> 'Sales' WITH CHECK OPTION

     UPDATE FIXED_INCOME
        SET NEWSALARY = 19000
        WHERE LNAME = 'Li'

除了校验选项以外,先前视图中的更新等效于对基表 PERS 的更新:

     UPDATE PERS
        SET SALARY = SALARY * 1.10
        WHERE NAME = 'Li'
          AND JOB <> 'Sales'

注意:由于视图是在 CREATE VIEW FIXED_INCOME 中对约束 JOB <> 'Sales'使用 WITH CHECK OPTION 创建的,所以当 Limoges 调去做销售时不允许下列更新:

     UPDATE FIXED_INCOME
        SET JOBTITLE = 'Sales'
        WHERE LNAME = 'Limoges'

不能更新由表达式 SALARY + COMM or SALARY * 1.25 定义的列。如果定义的视图包含一列或多个这样的列,则拥有者不接受对这些列的更新(UPDATE)特权。在包含这样的列的视图上不允许 INSERT 语句,但允许 DELETE 语句。

考虑一个没有一列定义为 NOT NULL 的 PERS 表。可以通过 FIXED_INCOME 视图将行插入 PERS 表中,即使该视图不包含基本表 PERS 的 ID、YEARS、COMM 或 BIRTHDATE。整个视图中看不到的列被适当地设置为空值或缺省值。

然而,PERS 表确实已将列 ID 定义为 NOT NULL。如果尝试通过 FIXED_INCOME 视图插入行,则系统试图将空值插入在整个视图中“看不到”的所有 PERS 列。由于 ID 列未包括在视图中并且该列不允许空值,所以系统不允许通过该视图进行插入。

posted @ 2006-01-10 20:21 呓语的博客 阅读(644) | 评论 (0)编辑 收藏

表是由定义的列数和可变的行数组成的逻辑结构。是一组相同数据类型的值。在表中不必对行进行排序。要对结果集进行排序,必须在从表中选择数据的 SQL 语句中显式指定排序。在每个列和行的相交处是一个称为的特定数据项。在图 1中,'Sanders' 是表中值的一个示例。

基表是用 CREATE TABLE 语句创建的,用于保存用户数据。结果表是一组行,数据库管理程序从一个或多个基表选择或生成这组行以满足查询要求。

图 1说明了表的一部分。列和行已标记。

图 1. 表的可视化

视图

视图提供了在一个或多个表中查看数据的替代方法。它是表上的一个动态窗口。

视图允许多个用户查看同一数据的不同表示。例如,几个用户可以同时存取一个关于雇员的数据表。一个用户可以查看关于某些雇员而非其他雇员的数据,而另一个用户可以查看关于所有雇员的某些数据而非他们的薪水。这些用户的每一个都在操作一个从该实表派生的视图。每个视图都显示为一个表并有自己的名称。

使用视图的优点是您可以使用它们来控制对敏感数据的存取。所以,不同的人可以存取数据的不同列或行。

模式

模式是命名对象的集合,并提供了数据库中对象的逻辑分类。模式可以包含数据库对象,如表和视图等。

模式本身也可以认为是数据库中的一个对象。当创建表或视图时隐式创建了模式。或者,可以使用 CREATE SCHEMA 语句显式创建它。

创建对象时,可以用特定模式的名称来限定对象的名称。命名对象有两部分名称,其中第一部分名称是指定给对象的模式名。如果未指定模式名,则给对象指定其名称是用户执行语句的权限 ID 的缺省模式。对于交互式 SQL,该方法用于执行本书中的示例,权限 ID 为用 CONNECT 语句指定的用户 ID。例如,如果表名为 STAFF,CONNECT 语句中指定的用户 ID 为 USERID,则限定名为 USERID.STAFF。参见连接数据库以获取关于 CONNECT 语句的详情。

某些模式名是系统保留的。例如,当预安装的用户定义函数 属于 SYSFUN 模式时,内部函数处于 SYSIBM 模式。参考 SQL Reference 以获取关于 CREATE SCHEMA 语句的详情。

数据类型

数据类型定义常数、列、宿主变量、函数、表达式以及专用寄存器可接受的值。本节描述示例中引用的数据类型。有关其他数据类型的完整列表和说明,参考 SQL Reference

字符串

字符串为一个字节序列。字符串的长度为序列中的字节数。如果长度为零,则该字符串的值称为空字符串
定长字符串

CHAR(x)是定长字符串。长度属性 x 必须在 1 和 254 之间,并包括 1 和 254。

变长字符串

变长字符串有三种类型:VARCHAR、LONG VARCHAR 以及 CLOB。 VARCHAR(x)类型是变长字符串,因此,可以将长度为 9 的字符串插入 VARCHAR(15)中,而该字符串的长度将仍然为 9。参见大对象 (LOB)以获取关于 CLOB 的详情。

图形字符串

图形字符串是一个双字节字符数据序列。

定长图形字符串

GRAPHIC(x)是定长字符串。长度属性 x 必须在 1 和 127 之间,并包括 1 和 127。

变长图形字符串

变长图形字符串有三种类型:VARGRAPHIC、LONG VARGRAPHIC 以及 DBCLOB。参见大对象 (LOB)以获取关于 DBCLOB 的详情。

二进制字符串

二进制字符串是一个字节序列。它用于保存非传统数据,如图象等。“二进制大对象”(BLOB)是二进制字符串。参见大对象 (LOB)以了解更多信息。

数字

所有数字都有符号和精度。精度是除符号位以外的位数或数字数。

SMALLINT
SMALLINT(小型整数)是精度为 5 位的两字节整数。

INTEGER
INTEGER(大型整数)是精度为 10 位的四字节整数。

REAL
REAL(单精度浮点数)是实数的 32 位近似值。

DOUBLE
DOUBLE(双精度浮点数)是实数的 64 位近似值。 DOUBLE 也称 FLOAT。

DECIMAL(p,s)

DECIMAL 是一个十进制数。小数点的位置由数字的 精度(p)小数位(s) 确定。精度是数字的总位数,必须小于 32。小数位是小数部分数字的位数且总是小于或等于精度值。如果未指定精度和小数位,则十进制值的缺省精度为 5,缺省小数位为 0。

日期时间值

日期时间值是日期、时间以及时间戳记的表示。日期时间值可以用于某些算术运算和字符串运算并且与某些字符串是相容的,然而它们既非字符串也非数字。 (1)

日期
日期值分为三个部分(年、月以及日)。

时间
时间是用 24 小时制式来指定一天内的时间的值,分为三个部分(小时、分钟以及秒)。

时间戳记
时间戳记为指定日期和时间的值,分为七个部分(年、月、日、小时、分钟、秒以及微秒)。

值是一个区别于所有非空值的特殊值。它意味着行中的那一列无任何其他值。所有数据类型都存在空值。

下表突出显示示例中所使用的数据类型的特性。所有数字数据类型都定义在某一确定范围内。该数字数据类型范围也包括在此表中。可以使用此表作为正确数据类型用法的快速参考。
数据类型 类型 特性 示例或范围
CHAR(15)

定长字符串

最大长度为 254

'Sunny day      '

VARCHAR(15)

变长字符

最大长度为 4000

'Sunny day'

SMALLINT

数字

长度为 2 字节
精度为 5 位

范围为-32768 至 32767

INTEGER

数字

长度为 4 字节
精度为 10 位

范围为-2147483648 至 2147483647

REAL

数字

单精度
浮点
32 位近似值

范围为 
-3.402E+38 至-1.175E-37
或 1.175E-37 至-3.402E+38
或零

DOUBLE

数字

双精度
浮点
64 位近似值
范围为
-1.79769E+308 至-2.225E-307
或 2.225E-307 至 1.79769E+308
或零

DECIMAL(5,2)

数字
精度为 5
小数位为 2

范围为 
-10**31+1 至 10**31-1
DATE

日期时间

三部分值

1991-10-27

TIME

日期时间

三部分值

13.30.05

TIMESTAMP

日期时间

七部分值

1991-10-27-13.30.05.000000

posted @ 2006-01-10 20:20 呓语的博客 阅读(182) | 评论 (0)编辑 收藏

第1章 关系数据库和SQL
    关系数据库中,数据存储在表中。表是行和列的集合。 结构化查询语言(SQL)通过指定列、表以及它们之间的各种关系来检索或更新数据。
 
    SQL 是在关系数据库中定义和处理数据的标准化语言。SQL 语句由数据库管理程序执行。数据库管理程序是管理数据的计算机程序。 

    分区关系数据库是在多个分区(也称为节点)上管理数据的关系数据库。本书中我们将把重点集中在单个分区数据库上。 

    可以使用一个象命令行处理器(CLP)或命令中心那样的界面,通过交互式 SQL 来存取样本数据库并试验本书中的所有示例。 

 

posted @ 2006-01-10 20:16 呓语的博客 阅读(169) | 评论 (0)编辑 收藏