4.5.5 用户定义的类型
独特类型
如何自定义新类型
删除或就该自定义类型
域
域和自定义类型的区别
在数据库实现中对类型和域的支持
`PostgreSQL`
`IBM DB2`
`SQL Server`
Oracle
`MySQL`
面向对象类型
4.5.5 用户定义的类型
SQL
支持两种形式的用户定义数据类型。
- 第一种称为独特类型(
distinct type
),我们将在这里介绍。 - 另一种称为结构化数据类型(
structured data type
),允许创建具有嵌套
记录结构、数组
和多重集的复杂数据类型
。
在本章我们不介绍结构化数据类型,而是在后面第22章描述。
一些属性可能会有相同的数据类型。例如,用于学生名和教师名的name
属性就可能有相同的域:所有人名的集合。
然而, budget
和dept_name
的域肯定应该是不同的。name
和 dept_name
是否应该有相同的域,这一点就不那么明显了。在实现层,教师姓名和系的名字都是字符串。然而,我们通常不认为“找出所有与某个系同名的教师”是一个有意义的查询。因此,如果我们在概念层而不是物理层来看待数据库的话,name
和 dept_name
应该有不同的域。
独特类型
更重要的是,在现实中,把一个教师的姓名赋给一个系名可能是一个程序上的错误;
类似地,把个以美元表示的货币值直接与一个以英镑表示的货币值进行比较几乎可以肯定是程序上的错误。
一个好的类型系统应该能够检测出这类赋值或比较。为了支持这种检测,SQL
提供了独特类型(distincttype
)的概念。
如何自定义新类型
可以用create type
子句来定义新类型。例如,下面的语句:
1 | create type Dollars as numeric(12,2) final; |
把两个用户定义类型Dollars
和Pounds
定义为总共12位数字的十进制数,其中两位放在十进制小数点后。(在此关键字final
并不是真的有意义,它是SQL:1999
标准要求的,其原因我们不在这里讨论了;一些系统实现允许忽略血final
关键字。)然后新创建的类型就可以用作关系属性的类型。例如,我们可以把department
表定义为:
1 | create table department ( |
尝试为Pounds
类型的变量赋予一个Dollars
类型的值会导致一个编译时错误,尽管这两者都是相同的数值类型。这样的赋值很可能是由程序错误引起的,或许是程序员忘记了货币之间的区别。为不同的货币声明不同的类型能帮助发现这些错误。
由于有强类型检查,表达式( department.budget+20
)将不会被接受,因为属性和整型常数20具有不同的类型。一种类型的数值可以被转换(也即cast
)到另一个域,如下所示:
1 | cast(department.budget to numeric( 12, 2)); |
我们可以在数值类型上做加法,但是为了把结果存回到一个Dollars
类型的属性中,我们需要用另一个类型转换表达式来把数值类型转换回Dollars
类型。
删除或就该自定义类型
SQL
提供了drop type
和alter type
子句来删除或修改以前创建过的类型。
域
在把用户定义类型加入到SQL
(在SQL:1999
中)之前,SQL
有一个相似但稍有不同的概念:域
(domain
)(domain
在SQL-92
中引入),它可以在基本类型上施加完整性约束。例如,我们可以定义一个域DDollars
,如下所示:
1 | create domain DDollars as numeric(12, 2) not null; |
域和自定义类型的区别
DDollars
域可以用作属性类型,正如我们用Dollars
类型一样。然而,类型和域之间有两个重大的差别
- 在域上可以声明约束,例如
not null
,也可以为域类型变量定义默认值,然而在用户自定义类型上不能
声明约束或默认值。设计用户定义类型不仅是用它来指定属性类型,而且还将它用在不能施加约束的地方对SQL
进行过程扩展。 - 域并不是
强类型
的。因此一个域类型的值可以被赋给另一个域类型,只要它们的基本类型是相容的
当把check
子句应用到域上时,允许模式设计者指定一个谓词,被声明为来自该域的任何变量都必须满足这个谓词。例如, check
子句可以保证教师工资域中只允许出现大于给定值的值:
1 | create domain YearlySalary numeric(8,2) |
YearlySalary
域有一个约束来保证年薪大于或等于290000美元。 constraint salary_value_test
子句是可选的,它用来将该约束命名为salary_value_test
。系统用这个名字来指出一个更新违反了哪个约束。
作为另一个例子,使用in
子句可以限定一个域只包含指定的一组值
在数据库实现中对类型和域的支持
尽管本节描述的create type
和create domain
结构是SQL
标准的部分,但这里描迷的这些结构形式还没有被大多数数据库实现完全支持。
PostgreSQL
PostgreSQL
支持create domain
结构,但是其create type
结构具有不同的语法和解释。
IBM DB2
IBM DB2
支持create type
的一个版本,它使用create distinct type
语法,但不支持create domain
。
SQL Server
微软的SQL Server
实现了create type
结构的一个版本,支持域约束
,与SQL
的create domain
结构类似。
Oracle
Oracle
不支持在此描述的任何一种结构。
MySQL
经过我的测试MySQL
也不支持自定义类型create type
结构和自定义域create domain
结构。
面向对象类型
然而,SQL
还定义了一个更复杂的面向对象类型系统,我们将在后面第22章学习。通过使用不同形式的create type
结构, Oracle
、 IBM DB2
、 PostgreSQL
和SQL Server
都支持面向对象类型系统
。