Java之IDEA配置JDK

IDEA下载与安装

  • 点击DOWNLOAD,选择想要的版本

  • 然后选择自己的安装目录开始安装就可以.

  • 安装好后第一次开始运行时会弹出下面提示框,询问是否导入之前的设置项.

JDK下载与安装

环境变量配置

  • 右击我的电脑,点击属性,点击高级系统设置

  • 点击高级里面的环境变量

  • 在系统变量中新建,变量为JAVA_HOME,值为 JDK 的安装路径

  • 找到Path,点击编辑,新建一项,所填内容是 %JAVA_HOME%\bin ,并将其上移到第一项

IDEA创建Java项目配置SDK

  • 点击configure下的Structure for new project

  • 在 project settings中编辑SDK,选择ADD SDK

  • 选择JDK的安装目录,点击OK,再点击Apply

  • 点击New Project时新建JAVA项目,便不需要再选择SDK了

多态

多态

1 多态的基本概念

多态是C++面向对象三大特性之一

多态分为两类

  • 静态多态: 函数重载 和 运算符重载属于静态多态,复用函数名
  • 动态多态: 派生类和虚函数实现运行时多态

多态满足条件:

  • 有继承关系
  • 子类重写父类中的虚函数

多态使用:父类指针或引用指向子类对象

静态多态和动态多态区别:

  • 静态多态的函数地址早绑定 - 编译阶段确定函数地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Animal{
public:
void speak(){
cout<<"动物在说话"<<endl;
}
};
class Cat:public Animal{
public:
void speak(){
cout<<"小猫在说话"<<endl;
}
};
//地址早绑定,在编译阶段就确定了函数地址
void dospeak(Animal &animal){
animal.speak();
}
void test01(){
Cat cat;
dospeak(cat); //Animal &animal = cat
}

运行结果:

1
动物在说话
  • 动态多态的函数地址晚绑定 - 运行阶段确定函数地址

    如果函数地址在编译阶段就能确定,那么静态联编
    如果函数地址在运行阶段才能确定,就是动态联编

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Animal{
public:
//函数前面加上virtual关键字,变成虚函数,那么编译器在编译的时候就不能确定函数调用了。
virtual void speak(){ //虚函数
cout<<"动物在说话"<<endl;
}
};
class Cat:public Animal{
public:
//重写 函数返回值类型 函数名 参数列表完全相同
void speak(){
cout<<"小猫在说话"<<endl;
}
};

运行结果:

1
小猫在说话

2 纯虚函数和抽象类

  • 在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容,因此可以将虚函数改为纯虚函数。当类中有了纯虚函数,这个类也称为抽象类

  • 纯虚函数语法:virtual 返回值类型 函数名 (参数列表)= 0 ;

  • 抽象类特点

    • 无法实例化对象
    • 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

class Animal{
public:
//类中只要有一个纯虚函数就称为抽象类
//抽象类无法实例化对象
//子类必须重写父类中的纯虚函数,否则也属于抽象类
virtual void speak() = 0;//纯虚函数
};
class Cat:public Animal{
public:
void speak(){
cout<<"小猫在说话"<<endl;
}
};
void dospeak(Animal &animal){
animal.speak();
}
void test01(){
Animal *base = NULL;
//Base b;//错误,抽象类无法实例化对象
//base = new Animal; //错误,抽象类无法实例化对象
base = new Cat;
base->speak();
delete base;
}

3 虚析构和纯虚析构

  • 多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码。

  • 解决方式:将父类中的析构函数改为虚析构或者纯虚析构

  • 虚析构和纯虚析构共性:

    • 可以解决父类指针释放子类对象
    • 都需要有具体的函数实现
  • 虚析构和纯虚析构区别:如果是纯虚析构,该类属于抽象类,无法实例化对象

  • 虚析构语法:virtual ~类名(){}

  • 纯虚析构语法:

    virtual ~类名() = 0;

    类名::~类名(){}

  • **示例:下面这个例子,父类指针析构时不会调用子类的析构函数,导致如果子类有数据在堆区但是释放不了,内存泄漏 **

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Animal{
public:
virtual void speak() = 0;//纯虚函数
Animal(){
cout<<"Animal的构造函数"<<endl;
}
~Animal(){
cout<<"Animal的析构函数"<<endl;
}
};
class Cat:public Animal{
public:
Cat(string name){
cout<<"Cat的构造函数"<<endl;
m_name = new string(name);//创建在堆区
}
void speak(){
cout<<*m_name<<"小猫在说话"<<endl;
}
~Cat(){
if(m_name!=NULL){
cout<<"Cat的析构函数"<<endl;
delete m_name;
m_name = NULL;
}
}
string *m_name;
};
void dospeak(Animal &animal){
animal.speak();
}
void test01(){
Animal *base = new Cat("Tom");
base->speak();
delete base;
}

运行结果:

1
2
3
4
Animal的构造函数
Cat的构造函数
Tom小猫在说话
Animal的析构函数

将父类的析构函数修改为虚函数后

1
2
3
virtual ~Animal(){
cout<<"Animal的析构函数"<<endl;
}

运行结果:

1
2
3
4
5
Animal的构造函数
Cat的构造函数
Tom小猫在说话
Cat的析构函数
Animal的析构函数

将父类的析构函数修改为纯虚析构后,必须要在类外实现,防止父类有堆区数据

1
2
3
4
5
6
7
8
9
10
11
class Animal{
public:
virtual void speak() = 0;//纯虚函数
Animal(){
cout<<"Animal的构造函数"<<endl;
}
virtual ~Animal() = 0;
};
Animal::~Animal(){
cout<<"Animal的纯虚析构"<<endl;
}

继承

继承

1 语法

  • 继承的好处:可以减少重复的代码

  • class A : public B;

    A 类称为子类 或 派生类

    B 类称为父类 或 基类

  • 派生类中的成员,包含两大部分

    一类是从基类继承过来的,一类是自己增加的成员。

    从基类继承过过来的表现其共性,而新增的成员体现了其个性。

2 继承方式

继承方式一共有三种:

  • 公共继承
  • 保护继承
  • 私有继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

class Base{
public:
int A;
private:
int B;
protected:
int C;
};
class Son1:public Base{
public:
void func(){
A = 10;
//B = 20;//报错, 父类中的私有成员,子类不可访问
C = 30;
}
};
class Son2:protected Base{
public:
void func(){
A = 10;//仍是公有成员
//B = 20;//报错, 父类中的私有成员,子类不可访问
C = 30;//仍是保护成员
}
};
class Son3:private Base{
public:
void func(){
A = 10;//仍是公有成员
//B = 20;//报错, 父类中的私有成员,子类不可访问
C = 30;//仍是保护成员
}
};
void test01(){
//公有继承
Son1 s1;
s1.func();
s1.A = 100;
// s1.C = 200;//报错,在公有继承后仍是保护成员,不支持类外访问
//保护继承
Son2 s2;
s2.func();
// s2.A = 1000;//报错,保护继承后除私有成员外,其余在子类中均为保护成员
Son3 s3;
s3.func();
// s3.A = 1000;//报错
}

3 继承中的对象模型

父类中私有成员也被子类继承下去了,只是由编译器隐藏后访问不到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Base{
public:
int A;
private:
int B;
protected:
int C;
};
class Son1:public Base{
public:
int D;
};
void test01(){
//父类中所有非静态成员属性都会被子类继承下去
Son1 s1;
cout<<"sizeof Son1 = "<<sizeof(Son1)<<endl;//输出 16
}

4 继承中的构造和析构顺序

子类继承父类后,当创建子类对象,也会调用父类的构造函数。先构造父类,析构一般和构造顺序相反。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Base{
public:
Base(){
cout<<"Base的构造函数"<<endl;
}
~Base(){
cout<<"Base的析构函数"<<endl;
}
};
class Son:public Base{
public:
Son(){
cout<<"Son的构造函数"<<endl;
}
~Son(){
cout<<"Son的析构函数"<<endl;
}
};
void test01(){
Base b;
cout<<endl;
Son s1;
}

运行结果:

1
2
3
4
5
6
Base的构造函数
Base的构造函数
Son的构造函数
Son的析构函数
Base的析构函数
Base的析构函数

5 继承中同名处理方式

子类与父类出现同名的成员:

  • 访问子类同名成员 直接访问即可
  • 访问父类同名成员 需要加作用域
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Base{
public:
int A;
Base(){
A = 100;
}
void func(){
cout << "Base - func()调用" << endl;
}
void func(int a){
cout << "Base - func(int a)调用" << endl;
}
};
class Son:public Base{
public:
int A;
Son(){
A = 200;
}
void func(){
cout << "Son - func()调用" << endl;
}
};
void test01(){
Son s;
cout << "Son下的m_A = " << s.A << endl;
cout << "Base下的m_A = " << s.Base::A << endl;
s.func();
s.Base::func();
s.Base::func(10);
}

6 继承中同名静态成员处理方式

静态成员和非静态成员出现同名,处理方式一致

  • 访问子类同名成员 直接访问即可
  • 访问父类同名成员 需要加作用域

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

class Base{
public:
static int A;
static void func(){
cout<<"Base的静态成员函数"<<endl;
}
};
int Base::A = 100;
class Son:public Base{
public:
static int A;
};
int Son::A = 200;
void test01(){
//通过对象访问
Son s;
cout << "Son下的m_A = " << s.A << endl;
cout << "Base下的m_A = " << s.Base::A << endl;
//通过类名访问
cout << "Son下的m_A = " << Son::A << endl;
cout << "Base下的m_A = " << Son::Base::A << endl;
Son::Base::func();
}

7 多继承语法

C++允许一个类继承多个类,多继承可能会引发父类中有同名成员出现,需要加作用域区分

语法: class 子类 :继承方式 父类1 , 继承方式 父类2...

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Base1 {
public:
Base1(){
m_A = 100;
}
public:
int m_A;
};

class Base2 {
public:
Base2(){
m_A = 200; //开始是m_B 不会出问题,但是改为mA就会出现不明确
}
public:
int m_A;
};

//语法:class 子类:继承方式 父类1 ,继承方式 父类2
class Son : public Base2, public Base1
{
public:
Son(){
m_C = 300;
m_D = 400;
}
public:
int m_C;
int m_D;
};
//多继承容易产生成员同名的情况
//通过使用类名作用域可以区分调用哪一个基类的成员
void test01()
{
Son s;
cout << "sizeof Son = " << sizeof(s) << endl;
cout << s.Base1::m_A << endl;
cout << s.Base2::m_A << endl;
}
int main() {
test01();
return 0;
}

类模板

类模板

1 类模板语法

类模板作用:

  • 建立一个通用类,类中的成员 数据类型可以不具体制定,用一个虚拟的类型来代表。

语法:

1
2
template<typename T>

解释:

template — 声明创建模板

typename — 表面其后面的符号是一种数据类型,可以用class代替

T — 通用的数据类型,名称可以替换,通常为大写字母

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//类模板
template<class Nametype,class agetype>
class Person
{
public:
Person(Nametype name, agetype age)
{
this->m_Name = name;
this->m_Age = age;
}
void showPerson()
{
cout << "name: " << this->m_Name << " age: " << this->m_Age << endl;
}
Nametype m_Name;
agetype m_Age;
};
void test01(){
// 指定NameType 为string类型,AgeType 为 int类型
Person<string,int>p1("Tom", 10);
p1.showPerson();
}

2 类模板和函数模板的区别

类模板与函数模板区别主要有两点:

  1. 类模板没有自动类型推导的使用方式
  2. 类模板在模板参数列表中可以有默认参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

//类模板
template<class Nametype,class agetype = int>
class Person
{
public:
Person(Nametype name, agetype age)
{
this->m_Name = name;
this->m_Age = age;
}
void showPerson()
{
cout << "name: " << this->m_Name << " age: " << this->m_Age << endl;
}
Nametype m_Name;
agetype m_Age;
};
void test01(){
// Person p("孙悟空", 1000); // 错误 类模板使用时候,不可以用自动类型推导
Person<string,int>p1("Tom", 10);
Person <string> p("猪八戒", 999); //类模板中的模板参数列表 可以指定默认参数
}

3 类模板中成员函数创建时机

类模板中成员函数和普通类中成员函数创建时机是有区别的:

  • 普通类中的成员函数一开始就可以创建
  • 类模板中的成员函数在调用时才创建

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

#include<iostream>
using namespace std;
class Person1
{
public:
void showPerson1()
{
cout << "Person1 show" << endl;
}
};

class Person2
{
public:
void showPerson2()
{
cout << "Person2 show" << endl;
}
};

template<class T>
class myclass{
public:
T obj;
//类模板中的成员函数,并不是一开始就创建的,而是在模板调用时再生成
void func1(){
obj.showPerson1();
}
void func2(){
obj.showPerson2();
}
};
int main() {
myclass<Person1>m;
m.func1();
//m.func2();//编译会出错,说明函数调用才会去创建成员函数
return 0;
}

4 类模板对象做函数参数

  • 类模板实例化出的对象,向函数传参的方式一共有三种:
  1. 指定传入的类型:直接显示对象的数据类型
  2. 参数模板化: 将对象中的参数变为模板进行传递
  3. 整个类模板化:将这个对象类型模板化进行传递

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

#include<iostream>
#include<typeinfo>
using namespace std;
template<class T1,class T2>
class Person{
public:
Person(T1 name,T2 age){
m_name = name;
m_age = age;
}
void showPerson()
{
cout << "name: " << this->m_name << " age: " << this->m_age << endl;
}
T1 m_name;
T2 m_age;
};

//指定传入类型
void printPerson1(Person<string,int>&p){
p.showPerson();
}
void test01(){
Person<string,int>P("hxx",20);
printPerson1(P);
}
//参数模板化
template<class T1,class T2>
void printPerson2(Person<T1,T2>&p){
p.showPerson();
cout<<"T1的类型是:"<<typeid(T1).name()<<endl;
cout<<"T2的类型是:"<<typeid(T2).name()<<endl;
}
void test02(){
Person<string,int>P("qzy",20);
printPerson2(P);
}
//整个类模板化
template<class T>
void printPerson3(T &p){
p.showPerson();
cout<<"T的类型是:"<<typeid(T).name()<<endl;
}
void test03(){
Person<string,int>P("hq",20);
printPerson3(P);
}
int main(){
test01();
test02();
test03();
return 0;
}

5 类模板成员函数类外实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
template<class T1,class T2>
class Person{
public:
Person(T1 name,T2 age);
void showPerson();
T1 m_name;
T2 m_age;
};
//类外实现成员函数
template<class T1,class T2>
Person<T1,T2>::Person(T1 name,T2 age){
this->m_age = age;
this->m_name = name;
}
template<class T1,class T2>
void Person<T1,T2>::showPerson(){
cout << "name: " << this->m_name << " age: " << this->m_age << endl;
}
void test01(){
Person<string,int>P("hxx",20);
P.showPerson();
}

6 类模板分文件编写

问题:

  • 类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到

解决:

  • 解决方式1:直接包含.cpp源文件
  • 解决方式2:将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制

示例:

person.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#pragma once
#include <iostream>
using namespace std;
#include <string>

template<class T1, class T2>
class Person {
public:
Person(T1 name, T2 age);
void showPerson();
public:
T1 m_Name;
T2 m_Age;
};

//构造函数 类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
this->m_Name = name;
this->m_Age = age;
}

//成员函数 类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}

.cpp文件

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<iostream>
using namespace std;
#include "person.hpp"
void test01()
{
Person<string, int> p("Tom", 10);
p.showPerson();
}

int main() {
test01();
return 0;
}

7 类模板与友元

  • 全局函数类内实现:直接在类内声明友元即可

  • 全局函数类外实现 :需要提前让编译器知道全局函数的存在

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/*类模板和友元*/ 
#include<iostream>
using namespace std;
//全局函数配合友元 类外实现 - 先做函数模板声明,下方在做函数模板定义,在做友元
template<class T1, class T2> class Person;
//如果声明了函数模板,可以将实现写到后面,否则需要将实现体写到类的前面让编译器提前看到
//template<class T1, class T2> void printPerson2(Person<T1, T2> & p);

template<class T1, class T2>
void printPerson2(Person<T1,T2>&p){
cout << "姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl;
}

template<class T1, class T2>
class Person {
public:
//全局函数类内实现
friend void printPerson(Person<T1,T2>p){
cout << "姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl;
}
//全局函数类外实现,加空模板参数列表
friend void printPerson2<>(Person<T1,T2>&p);
Person(T1 name, T2 age){
this->m_Name = name;
this->m_Age = age;
}
private:
T1 m_Name;
T2 m_Age;
};

//通过全局函数类内实现 打印Person信息
void test01(){
Person<string,int>p("hxx",20);
printPerson(p);
printPerson2(p);
}
int main(){
test01();
return 0;
}

函数模板

函数模板

1 模板函数语法

  • 函数模板作用:建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。

    • 函数模板利用关键字 template
    • 使用函数模板有两种方式:自动类型推导、显示指定类型
    • 模板的目的是为了提高复用性,将类型参数化
  • 语法:

    1
    2
    template<typename T>
    函数声明或定义

    解释

    template :声明创建模板

    typename :表面其后面的符号是一种数据类型,可以用class代替

    T :通用的数据类型,名称可以替换,通常为大写字母

  • 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    template<class T>//声明一个模板,告诉编译器后面代码中紧跟的T是一个通用数据类型 
    void Swap(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
    }
    void test01(){
    int a = 10;
    int b = 20;
    //利用模板实现交换
    //1.自动类型推导
    Swap(a,b);
    //2.显示指定类型
    Swap<int>(a,b);
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    return;
    }

2 函数模版注意事项

注意事项:

  • 自动类型推导,必须推导出一致的数据类型T,才可以使用
  • 模板必须要确定出T的数据类型,才可以使用

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

template<class T>//声明一个模板,告诉编译器后面代码中紧跟的T是一个通用数据类型
void Swap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
void test01(){
int a = 10;
int b = 20;
char c ='c';
Swap(a,b);//正确,可以推导出一致的T
//Swap(a,c);//错误,推导不出一致的T,T到底是int还是char
return;
}
template<class T>
void func(){
cout<<"func"<<endl;
}
void test02(){
//func(); //错误,模板不能独立使用,必须确定出T的类型
func<int>(); //利用显示指定类型的方式,给T一个类型,才可以使用该模板
}

3 普通函数与模板函数的区别

普通函数与函数模板区别:

  • 普通函数调用时可以发生自动类型转换(隐式类型转换)
  • 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
  • 如果利用显示指定类型的方式,可以发生隐式类型转换

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int myadd01(int a,int b){
return a + b;
}
template<class T>
T myadd02(T a,T b){
return a + b;
}
void test01(){
int a = 10;
int b = 20;
char c = 'c';
//正确,将char类型的'c'隐式转换为int类型'c'对应的ASCII码99
cout<<myadd01(a,c)<<endl;
//myadd02(a,c);//报错,使用自动类型推导时,不会发生隐式类型转换
myadd02<int>(a,c);//正确,如果显示指定类型,可以发生隐式类型转换
}

4 普通函数与函数模板的调用规则

普通函数和函数模板的函数名是

可以一样的。

调用规则:

  1. 如果函数模板和普通函数都可以实现,优先调用普通函数
  2. 可以通过空模板参数列表来强制调用函数模板
  3. 函数模板也可以发生重载
  4. 如果函数模板可以产生更好的匹配,优先调用函数模板

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//普通函数与函数模板调用规则
void myPrint(int a, int b)
{
cout << "调用的普通函数" << endl;
}

template<typename T>
void myPrint(T a, T b)
{
cout << "调用的模板" << endl;
}

template<typename T>
void myPrint(T a, T b, T c)
{
cout << "调用重载的模板" << endl;
}

void test01(){
int a = 10;
int b = 20;
// 注意 如果告诉编译器 普通函数是有的,但只是声明没有实现,
//或者不在当前文件内实现,就会报错找不到
myPrint(a,b);//调用普通函数
myPrint<>(a,b); //调用模板函数
int c = 30;
myPrint(a,b,c); //调用重载模板函数
//如果函数模板可以产生更好的匹配,优先调用函数模板
char c1 = 'a';
char c2 = 'b';
myPrint(c1, c2); //调用函数模板
}

5 模板的局限性

局限性:模板的通用性不是万能的

  • 在下面的代码中,如果传入的是一个数组,就无法实现了
  • 在下面的代码中如果传入的参数是子自定义数据类型也无法运行

示例:

1
2
3
4
5
6
7
8
9
10
template<class T>
void f(T a, T b)
{
a = b;
}
template<class T>
void f(T a, T b)
{
if(a > b) { ... }
}
  • c++为了解决这种问题,提供模板的重载,可以为这些特定的类型提供具体化的模板

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
string m_Name;
int m_Age;
};
//普通函数模板
template<class T>
bool myCompare(T& a, T& b)
{
if (a == b) return true;
else return false;
}
//具体化,显示具体化的原型和定意思以template<>开头,并通过名称来指出类型
//具体化优先于常规模板
template<> bool myCompare(Person &p1, Person &p2){
if ( p1.m_Name == p2.m_Name && p1.m_Age == p2.m_Age) return true;
else return false;
}
void test01(){
int a = 10;
int b = 20;
//内置数据类型可以直接使用通用的函数模板
bool ret = myCompare(a, b);
if (ret) cout << "a == b " << endl;
else cout << "a != b " << endl;
}

void test02(){
Person p1("Tom", 10);
Person p2("Tom", 10);
//自定义数据类型,不会调用普通的函数模板
//可以创建具体化的Person数据类型的模板,用于特殊处理这个类型
bool ret = myCompare(p1, p2);
if (ret) cout << "p1 == p2 " << endl;
else cout << "p1 != p2 " << endl;
}

《数据库系统概论》(王珊 萨师煊)复习笔记(七)

第十章 数据库恢复技术

1 事务的基本概念

  • 定义
    • 一个数据库操作序列
    • 一个不可分割的工作单位(要么全做,要么不做)
    • 恢复和并发控制的基本单位
  • 事务和程序比较
    • 在关系数据库中,一个事务可以是一条或多条SQL语句,也可以包含一个或多个程序。
    • 一个程序通常包含多个事务
  • 事务的特性
    • 原子性(Atomicity)
      事务中的所有操作要么全部执行,要么都不执行。
    • 一致性(Consistency)
      如果在执行事务之前数据库是一致的,那么在执行事务之后数据库也还应该是一致的。
    • 隔离性(Isolation)
      即使多个事务并发执行,每个事务都感觉不到系统中有其他事务在执行,以保证数据库的一致性。
    • 持续性(Durability )
      事务成功执行后它对数据库的修改是永久的,即使系统出现故障。

2 数据库恢复概述

  • 故障是不可避免的

    • 系统故障:计算机软、硬件故障
    • 人为故障:操作员的失误、恶意的破坏等。
  • 数据库的恢复

    * 把数据库从错误状态恢复到某一已知的正确状态(亦称为**一致状态或完整状态**)
    • 恢复子系统是DBMS的一个重要组成部分,而且相当庞大

3 故障的种类

  • 事务内部的故障
    • 事务内部更多的故障是非预期的,不能由应用程序处理
      • 运算溢出
      • 并发事务发生死锁
      • 违反了某些完整性限制等
      • 事务故障仅指这类非预期的故障
    • 事务故障的恢复:撤消事务(UNDO)
  • 系统故障
    • 系统故障:称为软故障,是指造成系统停止运转的任何事件,使得系统要重新启动。 如:
      • 特定类型的硬件错误(如CPU故障)
      • 操作系统故障
      • DBMS代码错误
      • 系统断电
    • 后果
      • 整个系统的正常运行突然被破坏
      • 所有正在运行的事务都非正常终止
      • 内存中数据库缓冲区的信息全部丢失
      • 不破坏数据库
    • 发生系统故障时,事务未提交
      恢复策略:强行撤消(UNDO)所有未完成事务
    • 发生系统故障时,事务已提交,但缓冲区中的信息尚未完全写回到磁盘上。
      恢复策略:重做(REDO)所有已提交的事务
  • 介质故障
    • 称为硬故障,指外存故障
      • 磁盘损坏
      • 磁头碰撞
      • 操作系统的某种潜在错误
      • 瞬时强磁场干扰
    • 介质故障的恢复
      • 装入数据库发生介质故障前某个时刻的数据副本
      • 重做自此时始的所有成功事务,将这些事务已提交的结果重新记入数据库
  • 计算机病毒
    • 计算机病毒
      一种人为的故障或破坏,是一些恶作剧者研制的一种计算机程序
      可以繁殖和传播
    • 危害
      破坏、盗窃系统中的数据
      破坏系统文件

4 恢复的实现技术

  • 恢复操作的基本原理:冗余

    利用存储在系统其它地方的冗余数据来重建数据库中已被破坏或不正确的那部分数据

  • 恢复机制涉及的关键问题

    • 如何建立冗余数据
      数据转储(backup)
      登录日志文件(logging)
    • 如何利用这些冗余数据实施数据库恢复
  • 转储

    • DBA将整个数据库复制到磁带或另一个磁盘上保存起来的过程,备用的数据称为后备副本或后援副本。
    • 如何使用
      数据库遭到破坏后可以将后备副本重新装入
      重装后备副本只能将数据库恢复到转储时的状态
    • 静态转储
      • 在系统中无运行事务时进行的转储操作
      • 转储开始时数据库处于一致性状态
      • 转储期间不允许对数据库的任何存取、修改活动
      • 优点
        • 实现简单
        • 得到的一定是一个数据一致性的副本
      • 缺点:降低了数据库的可用性
        • 转储必须等待正运行的用户事务结束
        • 新的事务必须等转储结束
    • 动态存储
      • 转储操作与用户事务并发进行
      • 转储期间允许对数据库进行存取或修改
      • 优点
        • 不用等待正在运行的用户事务结束
        • 不会影响新事务的运行
      • 动态转储的缺点
        • 不能保证副本中的数据正确有效
        • 例如:在转储期间的某个时刻Tc,系统把数据A=100转储到磁带上,而在下一时刻Td,某一事务将A改为200。转储结束后,后备副本上的A已是过时的数据了
      • 利用动态转储得到的副本进行故障恢复
        • 需要把动态转储期间各事务对数据库的修改活动登记下来,建立日志文件
        • 后备副本加上日志文件才能把数据库恢复到某一时刻的正确状态
    • 海量转储与增量转储
      • 海量转储(完全转储): 每次转储全部数据库
      • 增量转储: 只转储上次转储后更新过的数据
      • 海量转储与增量转储比较
        • 从恢复角度看,使用海量转储得到的后备副本进行恢复往往更方便
        • 但如果数据库很大,事务处理又十分频繁,则增量转储方式更实用更有效
  • 登记日志文件

    • 什么是日志文件
      日志文件(log)是用来记录事务对数据库的更新操作的文件

    • 日志文件的格式

      • 以记录为单位的日志文件
        • 各个事务的开始标记(BEGIN TRANSACTION)
        • 各个事务的结束标记(COMMIT或ROLLBACK)
        • 各个事务的所有更新操作
      • 以数据块为单位的日志文件
        • 事务标识(标明是那个事务)
        • 被更新的数据块
    • 作用

      • 进行事务故障恢复
      • 进行系统故障恢复
      • 协助后备副本进行介质故障恢复
    • 基本原则

      登记的次序严格按并行事务执行的时间次序
      必须先写日志文件,后写数据库

5 恢复策略

  • 事务故障的恢复
    • 事务故障:事务在运行至正常终止点前被终止
    • 恢复方法
      由恢复子系统应利用日志文件撤消(UNDO)此事务已对数据库进行的修改
    • 事务故障的恢复由系统自动完成,对用户是透明的,不需要用户干预
  • 系统故障的恢复
    • 系统故障造成数据库不一致状态的原因
      未完成事务对数据库的更新已写入数据库
      已提交事务对数据库的更新还留在缓冲区没来得及写入数据库
    • 恢复方法
      1. Undo 故障发生时未完成的事务
    • 恢复方法
      1. Undo 故障发生时未完成的事务
      2. Redo 已完成的事务
    • 系统故障的恢复由系统在重新启动时自动完成,不需要用户干预
  • 介质故障的恢复
    • 重装数据库
    • 重做已完成的事务

6 具有检查点的恢复技术

  • 具有检查点(checkpoint)的恢复技术
    • 在日志文件中增加检查点记录(checkpoint)
    • 增加重新开始文件
    • 恢复子系统在登录日志文件期间动态地维护日志

7 数据库镜像

  • 数据库镜像

    • DBMS自动把整个a数据库或其中的关键数据复制到另一个磁盘上
    • DBMS自动保证镜像数据与主数据库的一致性
    • 每当主数据库更新时,DBMS自动把更新后的数据复制过去
  • 出现介质故障时

    • 可由镜像磁盘继续提供使用
    • 同时DBMS自动利用镜像磁盘数据进行数据库的恢复
    • 不需要关闭系统和重装数据库副本
  • 没有出现故障时

    • 可用于并发操作
    • 一个用户对数据加排他锁修改数据,其他用户可以读镜像数据库上的数据,而不必等待该用户释放锁
  • 频繁地复制数据自然会降低系统运行效率

    • 在实际应用中用户往往只选择对关键数据和日志文件镜像,而不是对整个数据库进行镜像

《数据库系统概论》(王珊 萨师煊)复习笔记(六)

数据库设计

第七章 数据库设计

1 数据库设计概述

  • 数据库设计
    数据库设计是指对于一个给定的应用环境,构造(设计)优化的数据库逻辑模式和物理结构,并据此建立数据库及其应用系统,使之能够有效地存储和管理数据,满足各种用户的应用需求,包括信息管理要求和数据操作要求。
  • 设计阶段
    • 需求分析
      • 准确了解与分析用户需求(包括数据与处理)
    • 概念结构设计
      • 整个数据库设计的关键
      • 通过对用户需求进行综合、归纳与抽象,形成一个独立于具体DBMS的概念模型
    • 逻辑结构设计
      • 将概念结构转换为某个DBMS所支持的数据模型
      • 对其进行优化
    • 物理结构设计
      • 为逻辑数据模型选取一个最适合应用环境的物理结构(包括存储结构和存取方法)
    • 数据库实施
      • 运用DBMS提供的数据库语言(如SQL)及宿主语言,根据逻辑设计和物理设计的结果
        • 建立数据库
        • 编制与调试应用程序
        • 组织数据入库
        • 进行试运行
    • 数据库运行和维护
      • 数据库应用系统经过试运行后即可投入正式运行
      • 在数据库系统运行过程中必须不断地对其进行评价、调整与修改
  • 需求分析和概念设计独立于任何数据库管理系统
  • 逻辑设计和物理设计与选用的DBMS密切相关

2 需求分析

  • 详细调查现实世界要处理的对象(组织、部门、企业等)

    • 充分了解原系统(手工系统或计算机系统)
    • 明确用户的各种需求
    • 确定新系统的功能
    • 充分考虑今后可能的扩充和改变
  • 调查的重点是“数据”和“处理”,获得用户对数据库要求

    • 信息要求
    • 处理要求
    • 安全性与完整性要求
  • 结构化分析方法(Structured Analysis,简称SA方法)

    • 从最上层的系统组织机构入手

    • 自顶向下、逐层分解分析系统

    • 首先把任何一个系统都抽象为:

    • 分解处理功能和数据

      • 分解处理功能

        将处理功能的具体内容分解为若干子功能

      • 分解数据
        处理功能逐步分解同时,逐级分解所用数据,形成若干层次的数据流图

      • 表达方法
        处理逻辑:用判定表或判定树来描述
        数据:用数据字典来描述

    • 将分析结果再次提交给用户,征得用户的认可

  • 数据字典

    • 数据字典的用途
      进行详细的数据收集和数据分析所获得的主要结果
    • 数据字典的内容
      数据项:不可再分的数据单位
      数据结构:反映了数据项之间的组合关系
      数据流:数据结构在系统内传输的路径。
      数据存储:数据结构停留或保存的地方
      处理过程:说明性信息的描述

3 概念结构设计

  • 什么是概念结构设计

    • 将需求分析得到的用户需求抽象为信息结构即概念模型的过程就是概念结构设计
    • 概念结构是各种数据模型的共同基础,它比数据模型更独立于机器、更抽象,从而更加稳定
    • 概念结构设计是整个数据库设计的关键
  • 概念结构设计的特点

    • 能真实、充分地反映现实世界
    • 易于理解
    • 易于更改
    • 易于向关系、网状、层次等各种数据模型转换
  • 描述概念模型的工具:E-R模型

  • 设计概念结构的四类方法

    • 自顶向下
    • 自底向上
    • 逐步扩张
    • 混合策略
  • 数据抽象

    分类、聚集、概括

4 逻辑结构设计

  • 逻辑结构设计的任务
    把概念结构设计阶段设计好的基本E-R图转换为与选用DBMS产品所支持的数据模型相符合的逻辑结构
  • 逻辑结构设计的步骤

  • E-R图向关系模型的转换要解决的问题
    如何将实体型和实体间的联系转换为关系模式
    如何确定这些关系模式的属性和码
  • 转换内容
    将E-R图转换为关系模型:将实体、实体的属性和实体之间的联系转换为关系模式。

《数据库系统概论》(王珊 萨师煊)复习笔记(五)

关系数据理论

第六章 关系数据理论

1 理论依据

  • 数据依赖:函数依赖、多值依赖、范式(NF)

2 规范化

2.1 理论

规范化理论正是用来改造关系模式,通过分解关系模式来消除其中不合适的数据依赖,以解决插入异常、删除异常、更新异常和数据冗余问题。

2.2 关系模式

  • 由五部分组成,即它是一个五元组:R(U, D, DOM, F)

    • R:关系名
    • U:组成该关系的属性名集合
    • D:属性组U中属性所来自的域
    • DOM:属性向域的映象集合
    • F:属性间数据的依赖关系集合
  • 简化为一个三元组:R(U, F)

2.3 数据依赖

  • 客观世界中事物间的联系:
    • 实体与实体的联系——数据模型
    • 实体内部属性间的联系——数据依赖
  • 属性间的联系分为三类:一对一 、一对多、多对多
  • 数据依赖:关系中属性值之间相互依赖相互制约的联系。
  • 属性间的数据依赖类型主要有两种:
    • 函数依赖
    • 多值依赖

2.4 函数依赖

  • 定义:设R(U)是一个属性集U上的关系模式,X和Y是U的子集。若对于R(U)的任意一个可能的关系r,r中不可能存在两个元组在X上的属性值相等, 而在Y上的属性值不等, 则称 “X函数确定Y” 或 “Y函数依赖于X”,记作X→Y。

  • 平凡函数依赖与非平凡函数依赖

    • 在关系模式R(U)中,对于U的子集X和Y,
      如果X→Y,但Y 包含于 X,则称X→Y是非平凡的函数依赖
      若X→Y,但Y 不包含于X, 则称X→Y是平凡的(trivial)函数依赖
    • 在关系SC(Sno, Cno, Grade)中,
      非平凡函数依赖: (Sno, Cno) → Grade
      平凡函数依赖: (Sno, Cno) → Sno (Sno, Cno) → Cno
    • 若X→Y,则X称为这个函数依赖的决定属性组,也称为决定因素
      若X→Y,Y→X,则记作X←→Y。
  • 完全函数依赖与部分函数依赖

    • 定义在R(U)中,如果X→Y,并且对于X的任何一个真子集X’,都有Y不函数依赖于X’, 则称Y对X完全函数依赖,记作

    • 若X→Y,但Y不完全函数依赖于X,则称Y对X部分函数依赖,记作

  • 传递函数依赖

  • 属性间的联系决定函数依赖关系

    • X、Y有1:1关系,则X→Y,Y→X。可表示成:X←→Y。

    • X、Y有1:m关系,则Y→X,但X不函数依赖于Y。(如班主任:学

      生,则学生→班主任,但班主任不函数依赖于学生)

    • X、Y有n:m关系,则X与Y不存在任何函数依赖。

  • 举例

    • 建立一个描述学生的数据库:

      • 学生的学号(Sno)、所在系(Sdept)、学生住处(Sloc)、课程号(Cno)、成绩(Grade)

      • 单一的关系模式 : Student <U、F>
        U ={ Sno, Sdept, Sloc, Cno, Grade }

        F ={ Sno → Sdept, Sdept → Sloc, (Sno, Cno) → Grade }

      • Sno → Sdept,Sdept → Sloc Sloc传递函数依赖于Sno

      • (Sno,Cno)→Grade是完全函数依赖,

      • (Sno,Cno)→Sdept是部分函数依赖, 因为Sno →Sdept成立,且Sno是(Sno,Cno)的真子集

2.5 码

  • 候选码
  • 主码
  • 主属性和非主属性
  • 全码
  • 外部码

2.6 范式

  • 各种范式之间存在联系:

  • 某一关系模式R为第n范式,可简记为R∈nNF。

  • 一个低一级范式的关系模式,通过模式分解可以转换为若干个高一级范式的关系模式的集合,这种过程就叫规范化

2.7 1NF

  • 1NF的定义:如果模一个关系式R的所有属性都是不可分的基本数据项,则R∈1NF

  • 第一范式是对关系模式的最起码的要求。不满足第一范式的数据库模式不能称为关系数据库,但是满足第一范式的关系模式并不一定是一个好的关系模式

    • 关系模式 S-L-C(Sno, Sdept, Sloc, Cno, Grade)
    • 函数依赖

    • S-L-C的码为(Sno, Cno)
      S-L-C满足第一范式。
      非主属性Sdept和Sloc部分函数依赖于码(Sno, Cno)

2.8 2NF

  • 若R∈1NF,且每一个非主属性完全函数依赖于码,则R∈2NF。

    • S-L-C(Sno, Cno, Sdept, Sloc, Grade)

      插入异常:插入一个学生,还未选课

      删除异常:删除一个学生选的唯一课程

      数据冗余度大:一个学生选修k门课,Sdept、Sloc重复存储k次

  • 原因:dept、 Sloc部分函数依赖于码。

  • 解决方法:S-L-C分解为两个关系模式,以消除这些部分函数依赖

    • 关系模式SC的码为(Sno,Cno)
      关系模式S-L的码为Sno
      这样非主属性对码都是完全函数依赖

2.9 3NF

  • 若R∈3NF,则每一个非主属性既不部分依赖于码,也不传递依赖于码。

    • S-L中存在非主属性对码的传递函数依赖,若一个系换学生宿舍楼,则修改复杂
    • 采用投影分解法,把S-L分解为两个关系模式,以消除传递函数依赖,S-D的码为Sno, D-L的码为Sdept。分解后的关系模式S-D与D-L中不再存在传递依赖

2.10 BCNF

  • 每一个决定属性因素都包含码,没有任何属性对码的部分函数依赖和传递函数依赖

  • 若R∈BCNF

    • 所有非主属性对每一个码都是完全函数依赖

    • 所有的主属性对每一个不包含它的码,也是完全函数依赖

    • 没有任何属性完全函数依赖于非码的任何一组属性

    • 在关系模式STJ(S,T,J)中,S表示学生,T表示教师,J表示课程。

      • 函数依赖:(S,J)和(S,T)都是候选码
      • STJ∈3NF ,没有任何非主属性对码传递依赖或部分依赖
      • STJ不属于BCNF,T是决定因素,T不包含码

      ​ (S,J)→T

      ​ (S,T)→J

      ​ T→J

    • 解决方法:将STJ分解为二个关系模式:ST(S,T) ∈ BCNF, TJ(T,J)∈ BCNF

  • 已知一个关系模式的属性之间的语义,也就是相互依赖的关系,如何判断该模式满足第几范式?

    • 首先要通过语义把属性之间的函数依赖关系列出来,
    • 然后确定哪些属性组合可以候选码,从而找出非主属性和主属性。
    • 然后判断是否存在非主属性与码之间的部分函数依赖关系,如果存在,则不满足2NF,如不存在部分函数依赖,则属于2NF,
    • 继续进行下一步判断;判断非主属性与码之间存在传递依赖关系,不存在,则为3NF;
    • 决定因素是否包含码,满足条件则为BCNF

《数据库系统概论》(王珊 萨师煊)复习笔记(四)

第四章 数据库保护

数据库安全性控制

  1. 非法使用数据库的情况
    • 编写合法程序绕过及DBMS其授权机制
    • 直接或编写应用程序执行非授权操作
    • 通过多次合法查询数据库从中推导出一些保密数据
  2. 计算机系统中,安全措施是一级一级层层设置
  3. 计算机系统的安全模型

  1. 用户识别和鉴别

    • 用户标识与鉴别:系统提供的最外层安全保护措施
    • 用户标识:用户名、用户标识号
    • 口令:系统核对口令以鉴别用户身份
    • 用户名和口令易被窃取:每个用户预先约定好一个计算过程或者函数
  2. 存取控制

    • 存取控制机制组成:定义用户权限、合法权限检查 。用户权限定义和合法权检查机制一起组成了DBMS的安全子系统
    • 常用存取控制方法
      • 自主存取控制(Discretionary Access Control ,简称DAC)
        C2级
        灵活
      • 强制存取控制(Mandatory Access Control,简称 MAC)
        B1级
        严格
    • 用户权限组成:数据对象、操作类型
    • 定义用户存取权限:定义哪些用户可以在哪些数据库对象上进行哪些类型的操作,是策略问题,DBMS应提供机制
    • 关系数据库系统中存取控制对象

  3. 授权与回收

    • GRANT

      • GRANT语句的一般格式:
      1
      2
      3
      4
      GRANT <权限>[,<权限>]... 
      [ON <对象类型> <对象名>]
      TO <用户>[,<用户>]...
      [WITH GRANT OPTION];
      • 语义:将对指定操作对象的指定操作权限授予指定的用户

      • 发出GRANT的用户:

        • DBA
        • 数据库对象创建者(即属主Owner)
        • 拥有该权限的用户
      • 按受权限的用户

        • 一个或多个具体用户
        • PUBLIC(全体用户)
      • WITH GRANT OPTION子句:

        • 指定:可以再授予
        • 没有指定:不能传播
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      /*把查询Student表权限授给用户U1*/
      GRANT SELECT
      ON TABLE Student
      TO U1;
      /*把对Student表和Course表的全部权限授予用户U2和U3*/
      GRANT ALL PRIVILIGES
      ON TABLE Student, Course
      TO U2, U3;
      /*把对表SC的查询权限授予所有用户*/
      GRANT SELECT
      ON TABLE SC
      TO PUBLIC;
      /*把查询Student表和修改学生学号的权限授给用户U4*/
      GRANT UPDATE(Sno), SELECT
      ON TABLE Student
      TO U4;
      /*属性列的授权时必须明确指出相应属性列名*/
      /*把对表SC的INSERT权限授予U5用户,并允许他再将此权限授予其他用户*/
      GRANT INSERT
      ON TABLE SC
      TO U5
      WITH GRANT OPTION;
    • REVOKE

      • 授予的权限可以由DBA或其他授权者用REVOKE语句收回

      • REVOKE语句的一般格式为:

        1
        2
        3
        REVOKE <权限>[,<权限>]... 
        [ON <对象类型> <对象名>]
        FROM <用户>[,<用户>]...;
      • 级联删除

        1
        2
        3
        4
        5
        6
        /*用户U5对SC表的INSERT权限收回*/
        REVOKE INSERT
        ON TABLE SC
        FROM U5 CASCADE ;
        /*将用户U5的INSERT权限收回的时候必须级联(CASCADE)收回
        系统只收回直接或间接从U5处获得的权限 */
  4. 数据库角色:被命名的一组与数据库操作相关的权限

    • 角色是权限的集合
    • 可以为一组具有相同权限的用户创建一个角色
    • 简化授权的过程
    • 类比:Windows操作系统中的用户组

第五章 数据库完整性

  1. 数据库的完整性:数据的正确性和相容性

  2. 数据的完整性和安全性是两个不同概念

    • 数据的完整性
      • 防止数据库中存在不符合语义的数据,也就是防止数据库中存在不正确的数据
      • 防范对象:不合语义的、不正确的数据
    • 数据的安全性
      • 保护数据库防止恶意的破坏和非法的存取
      • 防范对象:非法用户和非法操作
  3. 完整性约束的分类

    • 完整性约束条件中涉及的约束对象

      • 关系
      • 元组
    • 值约束和结构约束
      例:在向“学生成绩”关系中插入数据和更新数据时,需检查新的“学号”和“课程号”是否在“学生”关系和“课程”关系中已存在,否则不允许插入和更新。

    • 静态约束与动态约束
      例:动态约束的例子,在更新“学生”关系中的“年龄”字段时,新值必须大于旧值

    • 立即执行约束和延迟执行约束
      例:银行业务中,从账户A转移资金X到账户B,约束条件是总资金不变

  4. 为维护数据库的完整性,DBMS必须:

    • 提供定义完整性约束条件的机制
    • 提供完整性检查的方法
    • 违约处理

《数据库系统概论》(王珊 萨师煊)复习笔记(三)

第三章 关系数据库标准语言SQL

1 SQL概述

  1. QL(Structured Query Language):结构化查询语言,是关系数据库的标准语言,SQL是一个通用的、功能极强的关系数据库语言

  2. 特点

    (1)综合统一

    • 集数据定义语言(DDL),数据操纵语言(DML),数据控制语(DCL)功能于一体。
    • 可以独立完成数据库生命周期中的全部活动:
      • 定义关系模式,插入数据,建立数据库;
      • 对数据库中的数据进行查询和更新;
      • 数据库重构和维护
      • 数据库安全性、完整性控制等
    • 用户数据库投入运行后,可根据需要随时逐步修改模式,不影响数据的运行。
    • 数据操作符统一

    (2)高度非过程化

    • 非关系数据模型的数据操纵语言“面向过程”,必须指定存取路径
    • SQL只要提出“做什么”,无须了解存取路径。
    • 存取路径的选择以及SQL的操作过程由系统自动完成。

    (3)面向集合的操作方式

    • 非关系数据模型采用面向记录的操作方式,操作对象是一条记录
    • SQL采用集合操作方式
      • 操作对象、查找结果可以是元组的集合
      • 一次插入、删除、更新操作的对象可以是元组的集合

    (4)多种使用方式

    • 交互式SQL
      • 一般DBMS都提供联机交互工具
      • 用户可直接键入SQL命令对数据库进行操作
      • 由DBMS来进行解释
    • 嵌入式SQL
      • 能将SQL语句嵌入到高级语言(宿主语言)
      • 使应用程序充分利用SQL访问数据库的能力、宿主语言的过程处理能力
      • 一般需要预编译,将嵌入的SQL语句转化为宿主语言编译器能处理的语句
  3. SQL的功能

    • 数据定义(DDL)

      • 定义、删除、修改基本表(Base Table)
      • 定义、删除视图(View)
      • 定义、删除索引(Index)
    • 数据操纵(DML)

      • 数据查询
      • 数据增、删、改
    • 数据控制(DCL)

      • 用户访问权限的授予、收回
  4. SQL数据库的体系结构

  5. 基本表

    • 本身独立存在的表
    • SQL中一个关系就对应一个基本表
    • 一个(或多个)基本表对应一个存储文件
    • 一个表可以带若干索引
  6. 存储文件

    • 逻辑结构组成了关系数据库的内模式
    • 物理结构是任意的,对用户透明
  7. 视图

    • 从一个或几个基本表导出的表
    • 数据库中只存放视图的定义而不存放视图对应的数据
    • 视图是一个虚表
    • 用户可以在视图上再定义视图

2 数据查询

  1. SQL查询表达式的基本结构

    select 属性名表
    from 关系名表
    where (条件表达式)

  2. 语句结构

    SELECT [ALL|DISTINCT] <目标列表达式> [,<目标列表达式>] …
    FROM <表名或视图名>[, <表名或视图名> ] …
    [ WHERE <条件表达式> ]
    [ GROUP BY <列名1> [ HAVING <条件表达式> ] ]
    [ ORDER BY <列名2> [ ASC|DESC ] ];

  3. 单表查询

    示例:

    • 学生-课程模式 S-T :
      学生表:Student(Sno,Sname,Ssex,Sage,Sdept)
      课程表:Course(Cno,Cname,Cpno,Ccredit)
      学生选课表:SC(Sno,Cno,Grade)

    (1) 选择表中的若干列

    1
    2
    3
    4
    5
    6
    SELECT Sno,Sname FROM Student; 
    SELECT * FROM Student;
    //SELECT子句的<目标列表达式>可以为:算术表达式、字符串、常量函数、列别名
    SELECT Sname,2009-Sage FROM Student;
    SELECT Sname,LOWER(Sdept) FROM Student;//小写字母表示所有系名
    SELECT Sname NAMELOWER(Sdept) DEPARTMENT FROM Student;

    (2) 选择表中的若干元组

    • 消除取值重复的行,如果没有指定DISTINCT关键词,则缺省为ALL
    • 指定DISTINCT关键词,去掉表中重复的行
    1
    2
    SELECT Sno   FROM SC;//等价于:SELECT ALL  Sno  FROM SC;
    SELECT DISTINCT Sno FROM SC;
    • 查询满足条件的元组
    • 常用的查询条件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    //查询计算机科学系全体学生的名单。
    SELECT Sname
    FROM Student
    WHERE Sdept=‘CS’;

    //查询年龄在20~23岁(包括20岁和23岁)之间的学生的姓名、系别和年龄
    SELECT Sname,Sdept,Sage
    FROM Student
    WHERE Sage BETWEEN 20 AND 23
    //查询信息系(IS)、数学系(MA)和计算机科学系(CS)学生的姓名和性别。
    SELECT Sname,Ssex
    FROM Student
    WHERE Sdept IN ( 'IS''MA''CS' );

    //匹配串为固定字符串
    //查询学号为200215121的学生的详细情况。
    SELECT *
    FROM Student
    WHERE Sno LIKE '200215121';

    //匹配串为含通配符的字符串
    //查询所有姓刘学生的姓名、学号和性别。
    SELECT Sname,Sno,Ssex
    FROM Student
    WHERE Sname LIKE ‘刘%’;

    //查询以"DB_"开头,且倒数第3个字符为 i的课程的详细情况。
    SELECT *
    FROM Course
    WHERE Cname LIKE 'DB\_%i_ _' ESCAPE '\';

    //查所有有成绩的学生学号和课程号。
    SELECT Sno,Cno
    FROM SC
    WHERE Grade IS NOT NULL

    (3)ORDER BY子句

    • ORDER BY子句
      • 可以按一个或多个属性列排序
      • 升序:ASC; 降序:DESC;缺省值为升序
    • 当排序列含空值时
      • ASC:排序列为空值的元组最后显示
      • DESC:排序列为空值的元组最先显示
    1
    2
    3
    4
    //查询全体学生情况,查询结果按所在系的系号升序排列,同一系中的学生按年龄降序排列。
    SELECT *
    FROM Student
    ORDER BY Sdept,Sage DESC

    (4) 聚集函数

    • 计数

      • COUNT([ DISTINCT | ALL ] *)
      • COUNT([ DISTINCT | ALL ] <列名>)
    • 计算总和

      • SUM([ DISTINCT | ALL ] <列名>)
    • 计算平均值

      • AVG([ DISTINCT | ALL ] <列名>)
    • 最大最小值

      • MAX([ DISTINCT | ALL ] <列名>)
      • MIN([ DISTINCT | ALL ] <列名>)
    1
    2
    3
    查询选修了课程的学生人数。
    SELECT COUNT(DISTINCT Sno)
    FROM SC;

    (5)GROUP BY子句

    • 细化聚集函数的作用对象
    • 未对查询结果分组,聚集函数将作用于整个查询结果
    • 对查询结果分组后,聚集函数将分别作用于每个组
    • 作用对象是查询的中间结果表
    • 按指定的一列或多列值分组,值相等的为一组
    1
    2
    3
    4
    5
    查询选修了3门以上课程的学生学号。
    SELECT Sno
    FROM SC
    GROUP BY Sno
    HAVING COUNT(*) >3
    • HAVING短语与WHERE子句的区别:
      作用对象不同
      WHERE子句作用于基表或视图,从中选择满足条件的元组
      HAVING短语作用于组,从中选择满足条件的组。
  4. 集合查询

    并操作UNION
    交操作INTERSECT
    差操作EXCEPT

1
2
3
4
5
6
7
8
9
10
  查询计算机科学系的学生及年龄不大于19岁的学生。
SELECT *
FROM Student
WHERE Sdept= 'CS'
UNION
SELECT *
FROM Student
WHERE Sage<=19
UNION:将多个查询结果合并起来时,系统自动去掉重复元组。
UNION ALL:将多个查询结果合并起来时,保留重复元组
  1. 连接查询:同时涉及多个表的查询

    • 连接条件或连接谓词:用来连接两个表的条件

    • 一般格式:

      • [<表名1>.]<列名1> <比较运算符> [<表名2>.]<列名2>
      • [<表名1>.]<列名1> BETWEEN [<表名2>.]<列名2> AND [<表名2>.]<列名3>
    • 连接字段:连接谓词中的列名称

    • 连接条件中的各连接字段类型必须是可比的,但名字不必是相同的

      • 等值连接:连接运算符为 =
        查询每个学生及其选修课程的情况
      1
      2
      3
      SELECT  Student.*,SC.*
      FROM Student,SC
      WHERE Student.Sno = SC.Sno;
      • 自然连接
        查询每个学生及其选修课程的情况
      1
      2
      3
      SELECT  Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade
      FROM Student,SC
      WHERE Student.Sno = SC.Sno;
      • 自身连接:一个表与其自己进行连接
        需要给表起别名以示区别
        由于所有属性名都是同名属性,因此必须使用别名前缀
      1
      2
      3
      4
      例如:查询每一门课的间接先修课(即先修课的先修课)
      SELECT FIRST.Cno,SECOND.Cpno
      FROM Course FIRST,Course SECOND
      WHERE FIRST.Cpno = SECOND.Cno;
      • 外连接与普通连接的区别
        普通连接操作只输出满足连接条件的元组
        外连接操作以指定表为连接主体,将主体表中不满足连接条件的元组一并输出
      1
      2
      3
      查询每个学生及其选修课程的情况
      SELECT Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade
      FROM Student LEFT OUT JOIN SC ON (Student.Sno=SC.Sno);
      • 复合条件连接:WHERE子句中含多个连接条件
      1
      2
      3
      4
      5
      6
      查询每个学生的学号、姓名、选修的课程名及成绩
      SELECT Student.Sno,Sname,Cname,Grade
      FROM Student,SC,Course /*多表连接*/
      WHERE Student.Sno = SC.Sno
      and SC.Cno = Course.Cno;

  1. 嵌套查询

    • 一个SELECT-FROM-WHERE语句称为一个查询块,将一个查询块嵌套在另一个查询块的WHERE子句或HAVING短语的条件中的查询称为嵌套查询

    • 子查询的限制

      • 不能使用ORDER BY子句
      • 层层嵌套方式反映了 SQL语言的结构化
      • 有些嵌套查询可以用连接运算替代
    • 带有IN谓词的子查询

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    查询与“刘晨”在同一个系学习的学生。
    SELECT Sno,Sname,Sdept
    FROM Student
    WHERE Sdept IN
    (SELECT Sdept
    FROM Student
    WHERE Sname= ‘ 刘晨 ’);
    用自身连接完成查询要求
    SELECT S1.Sno,S1.Sname,S1.Sdept
    FROM Student S1,Student S2
    WHERE S1.Sdept = S2.Sdept AND
    S2.Sname = '刘晨'
    • 带有比较运算符的子查询
      • 当能确切知道内层查询返回单值时,可用比较运算符(>,<,=,>=,<=,!=或< >)。
      • 与ANY或ALL谓词配合使用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    假设一个学生只可能在一个系学习,并且必须属于一个系,
    SELECT Sno,Sname,Sdept
    FROM Student
    WHERE Sdept =
    (SELECT Sdept
    FROM Student
    WHERE Sname= ‘刘晨’);
    找出每个学生超过他选修课程平均成绩的课程号。 SELECT Sno, Cno
    FROM SC x
    WHERE Grade >=( SELECT AVG(Grade)
    FROM SC y
    WHERE y.Sno=x.Sno ) ;
    • 带有ANY(SOME)或ALL谓词的子查询
    1
    2
    3
    4
    5
    6
    7
    查询其他系中比计算机科学某一学生年龄小的学生姓名和年龄
    SELECT Sname,Sage
    FROM Student
    WHERE Sage < ANY (SELECT Sage
    FROM Student
    WHERE Sdept= ' CS ')
    AND Sdept <> ‘CS ' ;
    • 带有EXISTS谓词的子查询
      • EXISTS谓词
        • 带有EXISTS谓词的子查询不返回任何数据,只产生逻辑值“true”或逻辑假值“false”。
        • 若内层查询结果非空,则外层的WHERE子句返回真值
        • 若内层查询结果为空,则外层的WHERE子句返回假值
        • 由EXISTS引出的子查询,其目标列表达式通常都用* ,因为带EXISTS的子查询只返回真值或假值,给出列名无实际意义
    1
    2
    3
    4
    5
    6
    7
    查询没有选修1号课程的学生姓名。
    SELECT Sna me
    FROM Student
    WHERE NOT EXISTS
    (SELECT *
    FROM SC
    WHERE Sno = Student.Sno AND Cno='1');

3 数据定义

  • SQL的数据定义功能: 模式定义、表定义、视图和索引的定义

  • 模式

    • 定义模式实际上定义了一个命名空间,在这个空间中可以定义该模式包含的数据库对象,例如基本表、视图、索引等。
    • 删除模式:DROP SCHEMA <模式名> <CASCADE | RESTRICT>
      • CASCADE(级联)
        删除模式的同时把该模式中所有的数据库对象全部删除
      • RESTRICT(限制)
        如果该模式中定义了下属的数据库对象(如表、视图等),则拒绝该删除语句的执行。当该模式中没有任何下属的对象时才能执行。
  • 基本表

    • 定义基本表
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    建立“学生”表Student,学号是主码,姓名取值唯一。
    CREATE TABLE Student
    (Sno CHAR(9) PRIMARY KEY
    Sname CHAR(20) UNIQUE/* Sname取唯一值*/
    Ssex CHAR(2),
    Sage SMALLINT
    Sdept CHAR(20)
    );
    建立一个“课程”表Course
    CREATE TABLE Course
    ( Cno CHAR(4) PRIMARY KEY
    Cname CHAR(40),
    Cpno CHAR(4) ,
    Ccredit SMALLINT
    FOREIGN KEY (Cpno) REFERENCES Course(Cno)
    );
    建立一个“学生选课”表SC
    CREATE TABLE SC
    (Sno CHAR(9),
    Cno CHAR(4),
    Grade SMALLINT
    /* 主码由两个属性构成,必须作为表级完整性进行定义*/
    PRIMARY KEY (Sno,Cno),
    /* 表级完整性约束条件,Sno是外码,被参照表是Student */
    FOREIGN KEY (Sno) REFERENCES Student(Sno),
    /* 表级完整性约束条件, Cno是外码,被参照表是Course*/
    FOREIGN KEY (Cno) REFERENCES Course(Cno)
    );
    • 修改基本表:不论基本表中原来是否已有数据,新增加的列一律为空值。
    1
    2
    3
    4
    5
    6
    /*向Student表增加“入学时间”列,其数据类型为日期型。*/
    ALTER TABLE Student ADD S_entrance DATE
    /*将年龄的数据类型由字符型(假设原来的数据类型是字符型)改为整数。*/
    ALTER TABLE Student ALTER COLUMN Sage INT
    /*增加课程名称必须取唯一值的约束条件。*/
    ALTER TABLE Course ADD UNIQUE(Cname);
  • 删除基本表:DROP TABLE <表名>[RESTRICT| CASCADE];

    • RESTRICT:删除表是有限制的。
      欲删除的基本表不能被其他表的约束所引用。如果存在依赖该表的对象,则此表不能被删除
    • CASCADE:删除该表没有限制。
      在删除基本表的同时,相关的依赖对象一起删除
  • 索引

    • 建立索引的目的:加快查询速度
    • 谁可以建立索引
      DBA 或 表的属主(即建立表的人)
      DBMS一般会自动建立以下列上的索引
        PRIMARY  KEY
        UNIQUE
    • 谁维护索引
       DBMS自动完成 
    • 使用索引
       DBMS自动选择是否使用索引以及使用哪些索引
    • RDBMS中索引一般采用B+树、HASH索引来实现
      • B+树索引具有动态平衡的优点 ,HASH索引具有查找速度快的特点,采用B+树,还是HASH索引 则由具体的RDBMS来决定
      • 索引是关系数据库的内部实现技术,属于内模式的范畴
      • CREATE INDEX语句定义索引时,可以定义索引是唯一索引、非唯一索引或聚簇索引
      • 在最经常查询的列上建立聚簇索引以提高查询效率 ,一个基本表上最多只能建立一个聚簇索引 ,经常更新的列不宜建立聚簇索引
    1
    2
    3
    4
    /*在Student表的Sname(姓名)列上建立一个聚簇索引*/
    CREATE CLUSTER INDEX Stusname ON Student(Sname);
    /* SC表按学号升序和课程号降序建唯一索引*/
    CREATE UNIQUE INDEX SCno ON SC(Sno ASC,Cno DESC);
    • 删除索引时,系统会从数据字典中删去有关该索引的描述。

4 数据更新

  1. 插入数据

  2. 修改数据

    • 语句格式
      UPDATE <表名>
      SET  <列名>=<表达式>[,<列名>=<表达式>]…
      [WHERE <条件>];
    • 功能
      修改指定表中满足WHERE子句条件的元组的指定列值
  3. 删除数据

    语句格式

       DELETE
       FROM     <表名>
       [WHERE <条件>];
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /*插入一条选课记录( '200215128','1 ')。*/
    INSERT INTO SC(Sno,Cno) VALUES (‘ 200215128 ’,‘1');

    /*将计算机科学系全体学生的成绩置零。*/
    UPDATE SC
    SET Grade=0
    WHERE 'CS'=
    (SELETE Sdept
    FROM Student
    WHERE Student.Sno = SC.Sno);
    /*删除学号为200215128的学生记录。*/
    DELETE FROM Student WHERE Sno= ‘200215128 '

5 视图

  1. 视图的特点

    • 虚表,是从一个或几个基本表(或视图)导出的表
    • 只存放视图的定义,不存放视图对应的数据
    • 基表中的数据发生变化,从视图中查询出的数据也随之改变
  2. 基于视图的操作

    • 查询
    • 删除
    • 受限更新
    • 定 义基于该视图的新视图。
  3. 定义视图

    • 语句格式
         CREATE  VIEW  <视图名>  [(<列名>  [,<列名>]…)]
         AS  <子查询>
         [WITH  CHECK  OPTION];
    • 成视图的属性列组名:全部省略或全部指定
    • 子查询不允许含有ORDER B‘’‘Y子句和DISTINCT短语
    • RDBMS执行CREATE VIEW语句时只是把视图定义存入数据字典,并不执行其中的SELECT语句。
    • 在对视图查询时,按视图的定义从基本表中将数据查出。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /*建立信息系学生的视图,并要求进行修改和插入操作时仍需保证该视图只有信息系的学生。*/
    CREATE VIEW IS_Student
    AS
    SELECT Sno,Sname,Sage
    FROM Student
    WHERE Sdept= 'IS'
    WITH CHECK OPTION
    /*
    带WITH CHECK OPTION选项时对IS_Student视图的更新操作:
    修改操作:自动加上Sdept= 'IS'的条件
    删除操作:自动加上Sdept= 'IS'的条件
    插入操作:自动检查Sdept属性值是否为'IS'
    如果不是,则拒绝该插入操作
    如果没有提供Sdept属性值,则自动定义Sdept为'IS'
    */
    • 基于多个基表的视图
    1
    2
    3
    4
    5
    6
    7
    8
    /*建立信息系选修了1号课程的学生视图。*/
    CREATE VIEW IS_S1(Sno,Sname,Grade)
    AS
    SELECT Student.Sno,Sname,Grade
    FROM Student,SC
    WHERE Sdept= 'IS' AND
    Student.Sno=SC.Sno AND
    SC.Cno= '1'
    • 基于视图的视图
    1
    2
    3
    4
    5
    6
    /* 建立信息系选修了1号课程且成绩在90分以上的学生的视图。*/
    CREATE VIEW IS_S2
    AS
    SELECT Sno,Sname,Grade
    FROM IS_S1
    WHERE Grade>=90
    • 带表达式的视图
    1
    2
    3
    4
    5
    /*定义一个反映学生出生年份的视图。*/
    CREATE VIEW BT_S(Sno,Sname,Sbirth)
    AS
    SELECT Sno,Sname,2007-Sage
    FROM Student;
    • 分组视图
    1
    2
    3
    4
    5
    6
    /*将学生的学号及他的平均成绩定义为一个视图,假设SC表中“成绩”列Grade为数字型*/
    CREATE VIEW S_G(Sno,Gavg)
    AS
    SELECT Sno,AVG(Grade)
    FROM SC
    GROUP BY Sno;
    • 不指定属性列:

      缺点:修改基表Student的结构后,Student表与F_Student视图的映象关系被破坏,导致该视图不能正确工作。

    1
    2
    3
    4
    5
    CREATE VIEW F_Student(F_Sno,name,sex,age,dept)
    AS
    SELECT *
    FROM Student
    WHERE Ssex=‘女’;
  4. 删除视图

    • 语句的格式:DROP VIEW <视图名>;
    • 该语句从数据字典中删除指定的视图定义
    • 如果该视图上还导出了其他视图,使用CASCADE级联删除语句,把该视图和由它导出的所有视图一起删除
    • 删除基表时,由该基表导出的所有视图定义都必须显式地使用DROP VIEW语句删除
  5. 查询视图

    • 用户角度:查询视图与查询基本表相同
    • RDBMS实现视图查询的方法
      • 视图消解法(View Resolution)
      • 进行有效性检查
      • 转换成等价的对基本表的查询
      • 执行修正后的查询
      • 有些情况下,视图消解法不能生成正确查询。
  6. 更新视图

    • 更新视图的限制:一些视图是不可更新的,因为对这些视图的更新不能唯一地有意义地转换成对相应基本表的更新
  7. 视图的作用

    • 视图能够简化用户的操作
      视图机制使用户可以将注意力集中在他所关心的数据上。如果这些数据不是直接来自基本表,则可以通过定义视图,使用户眼中的数据库结构简单、清晰,并且可以简化用户的数据查询操作。
    • 视图使用户能以多种角度看待同一数据
      视图机制能使不同的用户以不同的方式看待同一数据,当许多不同种类的用户使用同一个数据库时,这种灵活性是非常重要的。
    • 视图对重构数据库提供了一定程度的逻辑独立性
    • 视图能够对机密数据提供安全保护
      有了视图机制,就可以在设计数据库应用系统时,对不同的用户定义不同的视图,使机密数据不出现在不应看到这些数据的用户视图上,这样就由视图的机制自动提供了对机密数据的安全保护功能。