SQL就是专门用来做数据库的一门语言
和C语言差不多
(当然功能不紧紧是做数据了
编程啊
都可以.....
这里就不多说了)z这道题用ACCESS
就可以完成了
1
首先
用Access
2000(数据库
)做一个数据库
打开设计器创建表
在点菜单
另存为
名为班级名+自己名字英文简写
最后点保存啦
(每完成系统都是有提示的啦)
第一问
解决
2
打开设计器
上面做的表下面下
创建数据表
名字都是在最后完成时做的
先不用管
然后在输入sID,sName,sSex,sAge,sClass
注意根据后面的要求填写字段
及数据类型
以及
在做其他的问题了将T_Student表的sID字段设置为标识列,种子为1,增量为2。
为T_Student表的sSex(性别)字段设置其默认值为“男”。
为T_Student表的sAge(年龄)字段设置检查约束,规定年龄在0到30岁之间。
为T_KC表的cName(课程名称)字段设置唯一性约束。,规定课程名称不得重复。
生成一个规则t_rule,限制数据的取值范围在(0---100)之间,然后将这个规则绑定到T_CJ表的cj(成绩)字段。
为T_KC表的cName字段创建索引。
最后
当然主键当然是SID了
一定要标好哦~~·先不管作业其他要求
在依次做其他2个表
T_KC(cID,cName,sBianhao,sXuefen)
T_CJ(sID,cID,cj)
注意主键是SID
3.
做好了三个表后退出设计器
在打开以一个个表
在按要求输入(在每个表中添加3-5条记录。)
好了
基本就完成了
当然用SQL
也能完成
这些问题
并且要快
但涉及到TQL语言
编程问题
很难说清楚
就不一一解释了···
本文章是 重写 500 Lines or Less 系列的其中一篇,目标是重写 500 Lines or Less 系列的原有项目:Dagoba: an in-memory graph database。Dagoba 是作者设计用来展示如何从零开始自己实现一个图数据库( Graph Database )。该名字似乎来源于作者喜欢的一个乐队,另一个原因是它的前缀 DAG 也正好是有向无环图 ( Directed Acyclic Graph ) 的缩写。本文也沿用了该名称。
图是一种常见的数据结构,它将信息描述为若干独立的节点( vertex ,为了和下文的边更加对称,本文中称为 node ),以及把节点关联起来的边( edge )。我们熟悉的链表以及多种树结构可以看作是符合特定规则的图。图在路径选择、推荐算法以及神经网络等方面都是重要的核心数据结构。
既然图的用途如此广泛,一个重要的问题就是如何存储它。如果在传统的关系数据库中存储图,很自然的做法就是为节点和边各自创建一张表,并用外键把它们关联起来。这样的话,要查找某人所有的子女,就可以写下类似下面的查询:
还好,不算太复杂。但是如果要查找孙辈呢?那恐怕就要使用子查询或者 CTE(Common Table Expression) 等特殊构造了。再往下想,曾孙辈又该怎么查询?孙媳妇呢?
这样我们会意识到,SQL 作为查询语言,它只是对二维数据表这种结构而设计的,用它去查询图的话非常笨拙,很快会变得极其复杂,也难以扩展。针对图而言,我们希望有一种更为自然和直观的查询语法,类似这样:
为了高效地存储和查询图这种数据结构,图数据库( Graph Database )应运而生。因为和传统的关系型数据库存在极大的差异,所以它属于新型数据库也就是 NoSql 的一个分支(其他分支包括文档数据库、列数据库等)。图数据库的主要代表包括 Neo4J 等。本文介绍的 Dagoba 则是具备图数据库核心功能、主要用于教学和演示的一个简单的图数据库。
原文代码是使用 JavaScript 编写的,在定义调用接口时大量使用了原型( prototype )这种特有的语言构造。对于其他主流语言的用户来说,原型的用法多少显得有些别扭和不自然。
考虑到本系列其他数据库示例大多是用 Python 实现的,本文也按照传统,用 Python 重写了原文的代码。同样延续之前的惯例,为了让读者更好地理解程序是如何逐步完善的,我们用迭代式的方法完成程序的各个组成部分。
原文在 500lines 系列的 Github 仓库中只包含了实现代码,并未包含测试。按照代码注释说明,测试程序位于作者的另一个代码库中,不过和 500lines 版本的实现似乎略有不同。
本文实现的代码参考了原作者的测试内容,但跳过了北欧神话这个例子——我承认确实不熟悉这些神祇之间的亲缘关系,相信中文背景的读者们多数也未必了解,虽然作者很喜欢这个例子,想了想还是不要徒增困惑吧。因此本文在编写测试用例时只参考了原文关于家族亲属的例子,放弃了神话相关的部分,尽管会减少一些趣味性,相信对于入门级的代码来说这样也够用了。
本文实现程序位于代码库的 dagoba 目录下。按照本系列程序的同意规则,要想直接执行各个已完成的步骤,读者可以在根目录下的 main.py 找到相应的代码位置,取消注释并运行即可。
本程序的所有步骤只需要 Python3 ,测试则使用内置的 unittest , 不需要额外的第三方库。原则上 Python3.6 以上版本应该都可运行,但我只在 Python3.8.3 环境下完整测试过。
本文实现的程序从最简单的案例开始,通过每个步骤逐步扩展,最终形成一个完整的程序。这些步骤包括:
接下来依次介绍各个步骤。
回想一下,图数据库就是一些点( node )和边( edge )的集合。现在我们要做出的一个重大决策是如何对节点/边进行建模。对于边来说,必须指定它的关联关系,也就是从哪个节点指向哪个节点。大多数情况下边是有方向的——父子关系不指明方向可是要乱套的!
考虑到扩展性及通用性问题,我们可以把数据保存为字典( dict ),这样可以方便地添加用户需要的任何数据。某些数据是为数据库内部管理而保留的,为了明确区分,可以这样约定:以下划线开头的特殊字段由数据库内部维护,类似于私有成员,用户不应该自己去修改它们。这也是 Python 社区普遍遵循的约定。
此外,节点和边存在互相引用的关系。目前我们知道边会引用到两端的节点,后面还会看到,为了提高效率,节点也会引用到边。如果仅仅在内存中维护它们的关系,那么使用指针访问是很直观的,但数据库必须考虑到序列化到磁盘的问题,这时指针就不再好用了。
为此,最好按照数据库的一般要求,为每个节点维护一个主键( _id ),用主键来描述它们之间的关联关系。
我们第一步要把数据库的模型建立起来。为了测试目的,我们使用一个最简单的数据库模型,它只包含两个节点和一条边,如下所示:
按照 TDD 的原则,首先编写测试:
与原文一样,我们把数据库管理接口命名为 Dagoba 。目前,能够想到的最简单的测试是确认节点和边是否已经添加到数据库中:
assert_item 是一个辅助方法,用于检查字典是否包含预期的字段。相信大家都能想到该如何实现,这里就不再列出了,读者可参考 Github 上的完整源码。
现在,测试是失败的。用最简单的办法实现数据库:
需要注意的是,不管添加节点还是查询,程序都使用了拷贝后的数据副本,而不是直接使用原始数据。为什么要这样做?因为字典是可变的,用户可以在任何时候修改其中的内容,如果数据库不知道数据已经变化,就很容易发生难以追踪的一致性问题,最糟糕的情况下会使得数据内容彻底混乱。
拷贝数据可以避免上述问题,代价则是需要占用更多内存和处理时间。对于数据库来说,通常查询次数要远远多于修改,所以这个代价是可以接受的。
现在测试应该正常通过了。为了让它更加完善,我们可以再测试一些边缘情况,看看数据库能否正确处理异常数据,比如:
例如,如果用户尝试添加重复主键,我们预期应抛出 ValueError 异常。因此编写测试如下:
为了满足以上测试,代码需要稍作修改。特别是按照 id 查找主键是个常用 *** 作,通过遍历的方法效率太低了,最好是能够通过主键直接访问。因此在数据库中再增加一个字典:
完整代码请参考 Github 仓库。
在上个步骤,我们在初始化数据库时为节点明确指定了主键。按照数据库设计的一般原则,主键最好是不具有业务含义的代理主键( Surrogate key ),用户不应该关心它具体的值是什么,因此让数据库去管理主键通常是更为合理的。当然,在部分场景下——比如导入外部数据——明确指定主键仍然是有用的。
为了同时支持这些要求,我们这样约定:字段 _id 表示节点的主键,如果用户指定了该字段,则使用用户设置的值(当然,用户有责任保证它们不会重复);否则,由数据库自动为它分配一个主键。
如果主键是数据库生成的,事先无法预知它的值是什么,而边( edge )必须指定它所指向的节点,因此必须在主键生成后才能添加。由于这个原因,在动态生成主键的情况下,数据库的初始化会略微复杂一些。还是先写一个测试:
为支持此功能,我们在数据库中添加一个内部字段 _next_id 用于生成主键,并让 add_node 方法返回新生成的主键:
接下来,再确认一下边是否可以正常访问:
运行测试,一切正常。这个步骤很轻松地完成了,不过两个测试( DbModelTest 和 PrimaryKeyTest )出现了一些重复代码,比如 get_item 。我们可以把这些公用代码提取出来。由于 get_item 内部调用了 TestCase.assertXXX 等方法,看起来应该使用继承,但从 TestCase 派生基类容易引起一些潜在的问题,所以我转而使用另一个技巧 Mixin :
实现数据库模型之后,接下来就要考虑如何查询它了。
在设计查询时要考虑几个问题。对于图的访问来说,几乎总是由某个节点(或符合条件的某一类节点)开始,从与它相邻的边跳转到其他节点,依次类推。所以链式调用对查询来说是一种很自然的风格。举例来说,要知道 Tom 的孙子养了几只猫,可以使用类似这样的查询:
可以想象,以上每个方法都应该返回符合条件的节点集合。这种实现是很直观的,不过存在一个潜在的问题:很多时候用户只需要一小部分结果,如果它总是不计代价地给我们一个巨大的集合,会造成极大的浪费。比如以下查询:
为了避免不必要的浪费,我们需要另外一种机制,也就是通常所称的“懒式查询”或“延迟查询”。它的基本思想是,当我们调用查询方法时,它只是把查询条件记录下来,而并不立即返回结果,直到明确调用某些方法时才真正去查询数据库。
如果读者比较熟悉流行的 Python ORM,比如 SqlAlchemy 或者 Django ORM 的话,会知道它们几乎都是懒式查询的,要调用 list(result) 或者 result[0:10] 这样的方法才能得到具体的查询结果。
在 Dagoba 中把触发查询的方法定义为 run 。也就是说,以下查询执行到 run 时才真正去查找数据:
和懒式查询( Lazy Query )相对应的,直接返回结果的方法一般称作主动查询( Eager Query )。主动查询和懒式查询的内在查找逻辑基本上是相同的,区别只在于触发机制不同。由于主动查询实现起来更加简单,出错也更容易排查,因此我们先从主动查询开始实现。
还是从测试开始。前面测试所用的简单数据库数据太少,难以满足查询要求,所以这一步先来创建一个更复杂的数据模型:
此关系的复杂之处之一在于反向关联:如果 A 是 B 的哥哥,那么 B 就是 A 的弟弟/妹妹,为了查询到他们彼此之间的关系,正向关联和反向关联都需要存在,因此在初始化数据库时需要定义的边数量会很多。
当然,父子之间也存在反向关联的问题,为了让问题稍微简化一些,我们目前只需要向下(子孙辈)查找,可以稍微减少一些关联数量。
因此,我们定义数据模型如下。为了减少重复工作,我们通过 _backward 字段定义反向关联,而数据库内部为了查询方便,需要把它维护成两条边:
然后,测试一个最简单的查询,比如查找某人的所有孙辈:
这里 outcome/income 分别表示从某个节点出发、或到达它的节点集合。在原作者的代码中把上述方法称为 out/in 。当然这样看起来更加简洁,可惜的是 in 在 Python 中是个关键字,无法作为函数名。我也考虑过加个下划线比如 out_.in_ 这种形式,但看起来也有点怪异,权衡之后还是使用了稍微啰嗦一点的名称。
现在我们可以开始定义查询接口了。在前面已经说过,我们计划分别实现两种查询,包括主动查询( Eager Query )以及延迟查询( Lazy Query )。
它们的内在查询逻辑是相通的,看起来似乎可以使用继承。不过遵循 YAGNI 原则,目前先不这样做,而是只定义两个新类,在满足测试的基础上不断扩展。以后我们会看到,与继承相比,把共同的逻辑放到数据库本身其实是更为合理的。
接下来实现访问节点的方法。由于 EagerQuery 调用查询方法会立即返回结果,我们把结果记录在 _result 内部字段中。虽然 node 方法只返回单个结果,但考虑到其他查询方法几乎都是返回集合,为统一起见,让它也返回集合,这样可以避免同时支持集合与单结果的分支处理,让代码更加简洁、不容易出错。此外,如果查询对象不存在的话,我们只返回空集合,并不视为一个错误。
查询输入/输出节点的方法实现类似这样:
查找节点的核心逻辑在数据库本身定义:
以上使用了内部定义的一些辅助查询方法。用类似的逻辑再定义 income ,它们的实现都很简单,读者可以直接参考源码,此处不再赘述。
在此步骤的最后,我们再实现一个优化。当多次调用查询方法后,结果可能会返回重复的数据,很多时候这是不必要的。就像关系数据库通常支持 unique/distinct 一样,我们也希望 Dagoba 能够过滤重复的数据。
假设我们要查询某人所有孩子的祖父,显然不管有多少孩子,他们的祖父应该是同一个人。因此编写测试如下:
现在来实现 unique 。我们只要按照主键把重复数据去掉即可:
在上个步骤,初始化数据库指定了双向关联,但并未测试它们。因为我们还没有编写代码去支持它们,现在增加一个测试,它应该是失败的:
运行测试,的确失败了。我们看看要如何支持它。回想一下,当从边查找节点时,使用的是以下方法:
这里也有一个潜在的问题:调用 self.edges 意味着遍历所有边,当数据库内容较多时,这是巨大的浪费。为了提高性能,我们可以把与节点相关的边记录在节点本身,这样要查找边只要看节点本身即可。在初始化时定义出入边的集合:
在添加边时,我们要同时把它们对应的关系同时更新到节点,此外还要维护反向关联。这涉及对字典内容的部分复制,先编写一个辅助方法:
然后,将添加边的实现修改如下:
这里的代码同时添加正向关联和反向关联。有的朋友可能会注意到代码略有重复,是的,但是重复仅出现在该函数内部,本着“三则重构”的原则,暂时不去提取代码。
实现之后,前面的测试就可以正常通过了。
在这个步骤中,我们来实现延迟查询( Lazy Query )。
延迟查询的要求是,当调用查询方法时并不立即执行,而是推迟到调用特定方法,比如 run 时才执行整个查询,返回结果。
延迟查询的实现要比主动查询复杂一些。为了实现延迟查询,查询方法的实现不能直接返回结果,而是记录要执行的动作以及传入的参数,到调用 run 时再依次执行前面记录下来的内容。
如果你去看作者的实现,会发现他是用一个数据结构记录执行 *** 作和参数,此外还有一部分逻辑用来分派对每种结构要执行的动作。这样当然是可行的,但数据处理和分派部分的实现会比较复杂,也容易出错。
本文的实现则选择了另外一种不同的方法:使用 Python 的内部函数机制,把一连串查询变换成一组函数,每个函数取上个函数的执行结果作为输入,最后一个函数的输出就是整个查询的结果。由于内部函数同时也是闭包,尽管每个查询的参数形式各不相同,但是它们都可以被闭包“捕获”而成为内部变量,所以这些内部函数可以采用统一的形式,无需再针对每种查询设计额外的数据结构,因而执行过程得到了很大程度的简化。
首先还是来编写测试。 LazyQueryTest 和 EagerQueryTest 测试用例几乎是完全相同的(是的,两种查询只在于内部实现机制不同,它们的调用接口几乎是完全一致的)。
因此我们可以把 EagerQueryTest 的测试原样不变拷贝到 LazyQueryTest 中。当然拷贝粘贴不是个好注意,对于比较冗长而固定的初始化部分,我们可以把它提取出来作为两个测试共享的公共函数。读者可参考代码中的 step04_lazy_query/tests/test_lazy_query.py 部分。
程序把查询函数的串行执行称为管道( pipeline ),用一个变量来记录它:
然后依次实现各个调用接口。每种接口的实现都是类似的:用内部函数执行真正的查询逻辑,再把这个函数添加到 pipeline 调用链中。比如 node 的实现类似下面:
其他接口的实现也与此类似。最后, run 函数负责执行所有查询,返回最终结果;
完成上述实现后执行测试,确保我们的实现是正确的。
在前面我们说过,延迟查询与主动查询相比,最大的优势是对于许多查询可以按需要访问,不需要每个步骤都返回完整结果,从而提高性能,节约查询时间。比如说,对于下面的查询:
以上查询的意思是从孙辈中找到一个符合条件的节点即可。对该查询而言,主动查询会在调用 outcome('son') 时就遍历所有节点,哪怕最后一步只需要第一个结果。而延迟查询为了提高效率,应在找到符合条件的结果后立即停止。
目前我们尚未实现 take 方法。老规矩,先添加测试:
主动查询的 take 实现比较简单,我们只要从结果中返回前 n 条记录:
延迟查询的实现要复杂一些。为了避免不必要的查找,返回结果不应该是完整的列表( list ),而应该是个按需返回的可迭代对象,我们用内置函数 next 来依次返回前 n 个结果:
写完后运行测试,确保它们是正确的。
从外部接口看,主动查询和延迟查询几乎是完全相同的,所以用单纯的数据测试很难确认后者的效率一定比前者高,用访问时间来测试也并不可靠。为了测试效率,我们引入一个节点访问次数的概念,如果延迟查询效率更高的话,那么它应该比主动查询访问节点的次数更少。
为此,编写如下测试:
我们为 Dagoba 类添加一个成员来记录总的节点访问次数,以及两个辅助方法,分别用于获取和重置访问次数:
然后浏览代码,查找修改点。增加计数主要在从边查找节点的时候,因此修改部分如下:
此外还有 income/outcome 方法,修改都很简单,这里就不再列出。
实现后再次运行测试。测试通过,表明延迟查询确实在效率上优于主动查询。
不像关系数据库的结构那样固定,图的形式可以千变万化,查询机制也必须足够灵活。从原理上讲,所有查询无非是从某个节点出发按照特定方向搜索,因此用 node/income/outcome 这三个方法几乎可以组合出任意所需的查询。
但对于复杂查询,写出的代码有时会显得较为琐碎和冗长,对于特定领域来说,往往存在更为简洁的名称,例如:母亲的兄弟可简称为舅舅。对于这些场景,如果能够类似 DSL (领域特定语言)那样允许用户根据专业要求自行扩展,从而简化查询,方便阅读,无疑会更为友好。
如果读者去看原作者的实现,会发现他是用一种特殊语法 addAlias 来定义自己想要的查询,调用方法时再进行查询以确定要执行的内容,其接口和内部实现都是相当复杂的。
而我希望有更简单的方法来实现这一点。所幸 Python 是一种高度动态的语言,允许在运行时向类中增加新的成员,因此做到这一点可能比预想的还要简单。
为了验证这一点,编写测试如下:
无需 Dagoba 的实现做任何改动,测试就可以通过了!其实我们要做的就是动态添加一个自定义的成员函数,按照 Python 对象机制的要求,成员函数的第一个成员应该是名为 self 的参数,但这里已经是在 UnitTest 的内部,为了和测试类本身的 self 相区分,新函数的参数增加了一个下划线。
此外,函数应返回其所属的对象,这是为了链式调用所要求的。我们看到,动态语言的灵活性使得添加新语法变得非常简单。
到此,一个初具规模的图数据库就形成了。
和原文相比,本文还缺少一些内容,比如如何将数据库序列化到磁盘。不过相信读者都看到了,我们的数据库内部结构基本上是简单的原生数据结构(列表+字典),因此序列化无论用 pickle 或是 JSON 之类方法都应该是相当简单的。有兴趣的读者可以自行完成它们。
我们的图数据库实现为了提高查询性能,在节点内部存储了边的指针(或者说引用)。这样做的好处是,无论数据库有多大,从一个节点到相邻节点的访问是常数时间,因此数据访问的效率非常高。
但一个潜在的问题是,如果数据库规模非常大,已经无法整个放在内存中,或者出于安全性等原因要实现分布式访问的话,那么指针就无法使用了,必须要考虑其他机制来解决这个问题。分布式数据库无论采用何种数据模型都是一个棘手的问题,在本文中我们没有涉及。有兴趣的读者也可以考虑 500lines 系列中关于分布式和集群算法的其他一些文章。
本文的实现和系列中其他数据库类似,采用 Python 作为实现语言,而原作者使用的是 JavaScript ,这应该和作者的背景有关。我相信对于大多数开发者来说, Python 的对象机制比 JavaScript 基于原型的语法应该是更容易阅读和理解的。
当然,原作者的版本比本文版本在实现上其实是更为完善的,灵活性也更好。如果想要更为优雅的实现,我们可以考虑使用 Python 元编程,那样会更接近于作者的实现,但也会让程序的复杂性大为增加。如果读者有兴趣,不妨对照着去读读原作者的版本。
数据库关系模型(数据库逻辑模型)是将数据概念模型转换为所使用的数据库管理系统(DBMS)支持的数据库逻辑结构,即将E-R图表示成关系数据库模式。数据库逻辑设计的结果不是唯一的,需利用规范化理论对数据库结构进行优化。
在关系模型中,数据库的逻辑结构是一张二维表。在数据库中,满足下列条件的二维表称为关系模型:
1)每列中的分量是类型相同的数据
2)列的顺序可以是任意的
3)行的顺序可以是任意的
4)表中的分量是不可再分割的最小数据项,即表中不允许有子表
5)表中的任意两行不能完全相同。
由此可见,有序的航空物探测量剖面数据不满足数据库关系模型条件第3条“行的顺序可以是任意的”,因此,不能简单地直接利用关系数据库(如Oracle,SQL Server,Sybase等)来管理剖面数据,需将数据在数据库中的存储方式改为大字段存储,确保不因数据库数据的增加和删除等 *** 作改变剖面数据有序特性。
一、大字段存储
(一)大字段存储技术
大字段LOB(Large Object)技术是Oracle专门用于存放处理大对象类型数据(如多媒体材料、影像资料、文档资料等)的数据管理技术。LOB包括内部的和外部的两种类型。内部LOB又分CLOB(字符型)、BLOB(二进制型)等3种数据类型,其数据存储在数据库中,并且支持事务 *** 作外部LOB只有BFILE类型,其数据存储在 *** 作系统中,并且不支持事务 *** 作。LOB存放数据的长度最大可以达到4G字节,并且空值列(没有存放数据)不占空间(图2-6)。
图2-6 大字段存储示意图
由于外部LOB存放在 *** 作系统文件中,其安全性比内部LOB差一些。此外,大字段的存储支持事务 *** 作(批量提交和回滚等),而外部LOB不支持事务 *** 作。所以,航空物探测量剖面数据采用BLOB来存储。对于BLOB类型,如果数据量小于4000字节,数据库通常采用行内存储,而数据量大于4000字节采用行外存储。分析航空物探测量剖面数据,每个场值数据占4个字节(单精度),目前航磁数据采样率为10次/s,4000字节只能存储100s数据一般情况下航空物探测量每条测线飞行时间至少在10min以上,每条测线数据量远远大于4000字节。所以,航空物探测量剖面数据采用行外存储方式,即大字段列指定“Disable Storage In Row”的存储参数。
由于大字段类型长度可变,最大可到4G。假设测线飞行时间为T,场值采样率为n次/s,测线场值数据量为4Tn,所以有4Tn≤4G。单条测线飞行时间T不会超过10h(36000s,航空物探测量1架次至少飞行1个往返2条测线),则场值的采样率n≤4G/4T=4×1024×1024×1024/4×36000次/s=29826次/s。采用大字段来存储测量数据,不仅能够减少数据表的记录数,提高查询效率,而且使得采样率的扩展不受限制。
(二)大字段存储技术应用
由于航空物探数据的数据量较大,现有的航磁测量数据按基准点方式(点存储)存储可达几亿个数据记录。若按磁场数据采样点存储方式(简称“场值存储方式”),则记录条数=(磁场数据采样率/坐标采样率)点存储方式的记录数,达几十亿条数据记录,且随着数据采样率的扩展、测点的加密,航空物探测量数据量随着时间的推移呈现快速增长之势。显然,如果采用常规的表结构来存储,势必造成数据的存储、管理、检索、浏览和提取都非常困难。另一方面,从航空物探专业应用需求来说,很少对单个测点的场值数据进行运算、分析等 *** 作,一般至少是对一条测线或以上测线,多数时候是需要对整个测区的场值数据进行化极、上延、正反演拟合等。
因此,在航空物探数据库表结构设计时,改变过去将基准点或场值点数据记录作为数据库最小管理对象的理念,采用了大字段存储技术,将测线作为数据库最小管理对象,将测线上的测量数据,如坐标数据和磁场、重力场数据分别存储在相应大字段中。在航空物探数据库建设中,大量采用数据库的大字段存储技术(详见《航空物探信息系统数据库结构设计》)。
(三)大字段存储效率
以航磁测量数据为例分析大字段存储技术优势。如果以场值存储方式存储测线数据,则每条记录包含架次号、测线号、基准号、地理坐标、投影坐标、磁场数据等,由于坐标数据采样率2次/s,磁场数据采样率10次/s,每5个磁场数据中,只有第1个磁场数据有坐标数据,其他4个坐标数据是内插出来,因此在测线记录中会产生大量冗余的数据坐标数据。采用点存储方式存储的测线数据记录数等于线上基准点数,若采用大字段存储方式,一条测线数据只存储为1条数据记录(图2-7),一般一条测线的测点数近万个,甚至更多,可见采用大字段存储大大减少测线数据存储记录数,提高数据的存取效率。
以某测区的两条航迹线为例,分别采用3种方式测试数据库的数据存储效率。磁场数据的采样率10次/s,坐标数据采样率2次/s,两条测线上共有基准点8801个。以场值方式存储先内插坐标信息,使得每个场值数据都拥有自己的坐标,然后存入数据库,共有数据记录44005条,写入数据库时间为57.22s,读取时间为1.03s。第二种方式是以采样点的方式进行存储,共有8801条记录,写入数据库时间为9.47s,读取需要0.91s。第三种方式是以大字段的形式存储,只有2条记录,写入数据库1.03s,读取时间为0.44s(表2-2)。大字段数据存储记录数最少,存取效率最高。用整个测区数据测试效果更加明显。
表2-2 三种数据存储方法的存取效率比较
图2-7 大字段存储方式示意图
二、联合主键
主外键是关系型数据库建立表间关系的核心。在航空物探空间数据库建设过程中,要素类与要素类之间、要素类与对象类之间,以及对象类与对象类之间的关系的描述有3种形式,即拓扑关系——描述要素类与要素类之间结点、邻接和联通关系叠加关系——描述要素类与要素类之间的相交、包含与分类关系隶属关系——描述对象类与对象类之间的派生关系。前两种关系是采用空间数据模型建立的关系,而隶属关系是通过主键建立的对象类与对象类之间的关系。在建立一对一、一对多的表间关系时,需要在整个数据库表中确定具有唯一性的一个字段作为主键(主关键字)。
按照传统的航空物探数据的档案管理模式,每个项目分配一个自然数作为档案号,项目的所有资料均与此档案号相联系。勘查项目和科研项目的档案号是独立编号的,且均从001开始。加之人工管理的原因,存在1个项目2个档案号和2个项目1个档案号的情况,因此现行的档案号与项目之间的对应关系不具备唯一性,不能作为项目的唯一标识,即不能作为数据库表的主键。项目编号也不能作为数据库表的主键,项目编号也只是近十年的事,以前的项目没有项目编号。
综合考虑上述因素和项目具有分级、分类的特点,提出了构造项目唯一标识码(简称“项目标识”)的方法,并以此码作为数据库表的主键。
项目标识(主键):AGS+项目类别(2位)+项目起始年份(4位)+档案号(6位)
标识含义:AGS——航空物探的缩位代码
项目类别——2位代码,01代表勘查项目、02代表科研项目
起始年份—4位代码,项目开始年号
档案号—6位代码,为了与传统的项目管理方式相衔接,后面3~4位是
项目档案管理模式下的档案号,不足部分补零。
以上15位编码是一级项目的项目标识,二级及其以下级别的项目标识是在上一级项目标识基础上扩展2位数字代码,中间用“.”号隔开,数字为该级项目的序号。项目标识定义为30位编码,适用于六级以内的项目。例如:AGS022004000576.08.04.02,表示该项目为2004年开展的档案号为576的航空物探科研项目(一级项目)的第8课题(二级项目)第4子课题(三级项目)的第2专题。由此可见,该项目标识不仅仅是一个建立表间关系的关键字,同时还表达了不同级别项目间的隶属关系。在系统软件开发时,利用此关系生成了项目的分级树形目录,用户对项目的层次关系一目了然,便于项目查询。
数据库的主键一经确定,相应地需要确定联合主键的组成及其表达方式。所谓联合主键就是数据资料的唯一标识,在一个数据库表中选择2个或者2个以上的字段作为主键。由于航空物探数据绝大部分与项目标识有关,加之数据的种类较多,分类复杂,单凭主键确定数据库表中记录的唯一性,势必需要构建极其复杂的主键,这种方法既不利于主键的数据 *** 作,又会造成大量的数据冗余,合理地使用联合主键技术可以很好地解决资料唯一问题。以项目提交资料为例,提交的资料分为文字类资料、图件类资料和媒体类资料,我们对资料进行分类和编号,例如100代表文字资料(110——World文档,120——PDF文档),200代表图件资料(210——基础地理资料、220——基础地质资料,230——航迹线图,240——剖面图,250——等值线图等),300代表媒体资料(310——PPT文档,320——照片等),第1位(百位)表示该资料的类型,第2~3位表示该类资料的序号。
在数据库管理和项目资料查询时,采用项目标识与资料分类编号作为联合主键(图2-8),可以高效地实现复杂数据的查询。在整个数据库系统中多处(项目查询、数据提取等模块)使用联合主键技术。
图2-8 联合主键实例
三、信息标准化
为了实现数据共享,在航空物探数据库建模过程中,参考和引用了近百个国家信息化标准,编制了4个中心信息化标准和1个图件信息化工作指南。
(一)引用的国家信息化标准
1)地质矿产术语分类代码:地球物理勘查,地球化学勘查,大地构造学,工程地质学,结晶学及矿物学,矿床学,水文地质学,岩石学,地质学等。
2)国家基础信息数据分类与代码,国土基础信息数据分类与代码,地球物理勘查技术符号,地面重力测量规范,地面磁勘查技术规程,地面高精度磁测技术规程,大比例尺重力勘查规范,地理信息技术基本术语,地理点位置的纬度、经度和高程的标准表示法,地名分类与类别代码编制规则。
3)地球空间数据交换格式数学数字地理底图数据交换格式数字化地质图图层及属性文件格式。
(二)本系统建立的信息化标准
编写了“航空物探空间数据要素类和对象类划分标准”,“航空物探项目管理和资料管理分类代码标准”,“航空物探勘查分类代码标准”,“航空物探信息系统元数据标准”,“航空物探图件信息化工作指南”,以便与其他应用系统进行信息交换,实现数据库资料共享。
航空物探空间数据要素类和对象类划分标准:根据物探方法、数据处理过程以及推断解释方法和过程,把与GIS有关的数据划分为不同类型的要素类-对象类数据,按专业、比例尺、数据内容对要素类和对象类进行统一命名,使空间数据库中的每个要素类和对象类的命名具有唯一性,防止重名出现。规定要素类-对象类数据库表结构及数据项数值类型。
航空物探项目管理和资料管理分类代码标准:规定了航空物探项目管理和资料管理的相关内容,包括航空物探勘查项目和科研项目的项目立项、设计、实施、成果、评审、资料汇交等项目管理的全过程中的内容,以及项目成果资料和收集资料的归档、发送、销毁、借阅等资料管理与服务过程中的内容和数据项代码。
航空物探勘查分类代码标准:在“地质矿产术语分类代码地球物理勘查”(国家标准GB/T9649.28—1998)增加了航磁、航重专业方面所涉及的数据采集、物性参数、方法手段、仪器设备、资料数据解释及成图图件等内容和数据项代码。
航空物探信息系统元数据标准:规定了航空物探空间数据管理与服务的元数据(数据的标识、内容、质量、状况及其他有关特征)的内容。
四、航迹线数据模型
(一)航迹线模型的结构
航空物探测量是依据测量比例尺在测区内布置测网(测线和切割线)。当飞机沿着设计的测线飞行测量时,航空物探数据收录系统按照一定的采样率采集采样点的地理位置、高度和各种地球物理场信息。采用属性数据分置的方法,将测线地理位置信息从航空物探测量数据中分离出来,形成航迹线要素类表,在此表中只存储与航迹线要素类有关的数据,如项目标识、测区编号、测线号、测线类型(用于区分测线、切割线、不同高度线、重复线等)、坐标、高度值等将航迹线的对象类数据(磁场、重力场基础数据)分别以大字段形式存储在各自的二维表中,它们共享航迹线,解决了多源有序不同采样率的航空物探测量数据的数据存储问题,在满足要素类空间查询的同时,统一数据的存储方式(图2-9)。航迹线要素类隶属于测区要素类,它们之间为空间拓扑(包含)关系。测区从属于勘查项目,每个勘查项目至少有一个测区,它们之间为1对多关系。有关项目信息存放在项目概况信息对象类表中,各种表之间通过项目标识进行联接。
图2-9 航迹线数据模型结构
(二)航迹线的UML模型
统一建模语言UML(Unified Modeling Language)是一种定义良好、易于表达、功能强大且普遍适用的建模语言。它溶入了软件工程领域的新思想、新方法和新技术。UML是面向对象技术领域内占主导地位的标准建模语言,成为可视化建模语言的工业标准。在UML基础上,ESRI定义了空间数据库建模的ArcGIS包、类库和扩展原则。
图2-10 与航迹线有关的数据库表逻辑模型结构图
在确定航迹线数据模型后,以它为基础,使用UML完成与航迹的有关的项目概况信息、测区信息、原始数据等数据库表逻辑模型设计(图2-10)。
由UML模型生成Geodatabase模式时,模型中的每个类都对应生成一个要素类或对象类。类的属性映射为要素类或对象类的字段。基类属性中包含的字段,在继承类中不需重复创建。例如,每个类都包括项目标识等字段,可以创建一个包含公共属性的基类,其他类从该类继承公共的属性,而无需重复建基类中包含的属性。因为基类没有对应的要素类或对象类,所以将基类设置为抽象类型。要素类之间的关系采用依赖关系表示。
五、数据库逻辑模型
关系数据库的逻辑结构由一组关系模式组成,因而从概念结构到关系数据库逻辑结构的转换就是将概念设计中所得到的概念结构(ER图)转换成等价的UML关系模式(图2-11)。在UML模型图中,要素数据集用Geodatabase工作空间下的静态包表示。要素集包不能互相嵌套,为了容易组织,在生成物理模型后,在要素数据集包中自定义嵌套。要素数据集与空间参考有关,但是空间参考不能在UML中表达。要素类和二维表都是以类的形式创建的,区别是要素类继承Feature Class的属性,而二维表继承Object属性。为了表达每种元素的额外属性,比如设置字符型属性字段的字符串长度,设置要素类的几何类型(点、线或面)需要使用Geodatabase预定义的元素标记值。
图2-11 逻辑设计关系转换
基于航空物探数据的内在逻辑关系进行分析,使用统一建模语言(UML)构建数据实体对象间的关系类,定义了航空物探数据库的逻辑模型(图2-12)。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)