2. 钻孔数据库总体设计

钻孔数据库是柱状图自动出图的“数据底座”。柱状图上的每一根分层线、每一个取样符号、每一段水位标注,最终都要能追溯到数据库中的某一条记录。因此总体设计要解决四个问题:用什么数据库、建哪些表、字段怎么命名、表与表之间靠什么关联。设计得当,后续出图脚本只需按固定视图取数;设计不当,就会出现“图能画出来但数据对不上”的返工。

一、数据库选型:以单机文件型为主,兼顾协同

勘察项目的数据规模通常不大:一个中型工民建项目约几十到几百个钻孔,每个钻孔几十条分层记录、若干取样与测试记录,总量在万级到十万级行。这种量级下,选型的关键不是并发性能,而是部署简单、便于携带、易于备份和交付。推荐以 SQLite 作为单项目主库:单文件存储,随项目文件夹一起归档,出图脚本用 Python 的 sqlite3 或 SQLAlchemy 直接读取,无需安装服务端。对于需要多人同时录入的院级生产环境,可选用 PostgreSQL 或 MySQL,把 SQLite 作为外业离线采集库,回到内业后按统一表结构导入中心库。若单位已有 GIS 平台,也可将钻孔表挂接到空间库中,用孔口坐标直接生成孔位图,但柱状图出图仍建议走独立的关系表,避免空间库的字段约束干扰录入。

选型时还要确认字符集与数值精度:地层名称、岩性描述含中文,统一用 UTF-8;孔口高程、分层深度用定点小数而非浮点,避免 0.1+0.2 这类误差在累计深度上放大,导致分层线与取样深度错位。

二、核心表结构:一孔一主表,多事一子表

按“钻孔—事件”的思路拆表:钻孔本身的信息放主表,一个钻孔对应一条记录;分层、取样、原位测试、水位都是“在某深度发生的事件”,各自建子表,通过孔号关联。核心表建议如下:

  • 钻孔基本信息表 borehole:孔号、工程编号、孔口坐标 X/Y、孔口高程、终孔深度、钻孔类型(取土孔/标贯孔/鉴别孔)、施工日期、钻机型号、水位初见深度等。
  • 地层分层表 stratum:孔号、层序号、层底深度、层顶深度(可由上一层推出,但建议冗余存储便于校验)、岩土名称、岩性描述、地层代号、颜色、状态、密实度/稠度。
  • 取样表 sample:孔号、样品编号、取样顶深度、取样底深度、样品类型(原状/扰动/岩样/水样)、取样方式、试验项目。
  • 原位测试表 insitu_test:孔号、试验类型(标准贯入/动力触探/静力触探/波速)、试验深度、击数或比贯入阻力、试验日期。
  • 水位表 water_level:孔号、观测日期、初见水位、稳定水位、观测方式。

这里有一个容易忽略的细节:深度统一用“自孔口向下为正”的深度值,而不是标高。出图时再按“孔口高程 − 深度”换算成标高绘制,这样外业记录与内业成图参照系一致,避免正负号混乱。分层表同时存层顶、层底,是为了在导入时用“上一层底 = 下一层顶”做连续性校验,一旦出现空段或重叠立即报警。

三、字段命名规范:可读、可校验、可扩展

字段命名建议采用“小写下划线 + 业务前缀”的风格,主键统一为 id,外键统一为 bh_id(钻孔主键)而非孔号字符串,孔号另设唯一索引 bh_code。深度类字段统一以 _depth 结尾,高程类以 _elev 结尾,日期类以 _date 结尾,这样出图脚本按后缀就能批量识别深度字段。数值字段明确单位并在字段名或注释中标注,例如 spt_blow 表示标准贯入击数,water_depth 单位为米。枚举值用短代码存储(如样品类型 U 原状、D 扰动、R 岩样),另建字典表维护中文显示名,保证图面标注与库内编码解耦。

四、关联关系与出图取数

五张表以 bh_id 为纽带形成一对多关系:一个钻孔对应多条分层、多个样品、多组测试、多次水位观测。出图时按孔号取主表一条记录,再分别按深度排序取各子表记录,即可驱动柱状图的分层栏、取样栏、测试栏和水位线。为保证图面顺序稳定,所有子表查询必须显式 ORDER BY 深度字段。

钻孔数据库核心表关联与出图取数关系 钻孔基本信息 borehole id (PK) · bh_code (唯一) x_coord · y_coord · collar_elev final_depth · hole_type · drill_date water_first_depth 地层分层 stratum id (PK) · bh_id (FK) layer_no · top_depth bottom_depth · soil_name stratum_code · description 取样 sample id (PK) · bh_id (FK) sample_code · top_depth bottom_depth · sample_type test_item 原位测试 insitu_test id (PK) · bh_id (FK) test_type · test_depth spt_blow · test_date test_code 水位 water_level id (PK) · bh_id (FK) obs_date · first_depth stable_depth · obs_method 1 : N 1 : N 1 : N 1 : N 按 bh_id 取数 → 按深度排序 → 驱动柱状图各栏

上图把“一主四子”的结构和取数方向画在一起:主表提供孔口高程与终孔深度,决定图幅纵向范围;四张子表分别对应柱状图的分层栏、取样栏、测试栏和水位线。实际建库时,建议再建一个视图 v_borehole_profile,把主表与各子表按孔号拼成宽表,出图脚本只读视图,表结构后续调整时不必改动绘图代码。

建库前先定三条硬约束:孔号在项目内唯一;分层表“层顶=上层底”连续无缺口;所有深度字段非空且大于 0。这三条用数据库约束或导入校验脚本落实,能挡掉大部分出图错位问题。

最后给出一个可直接参考的建表片段(SQLite 语法,示例字段,实际项目按需增删):

CREATE TABLE borehole (
  id            INTEGER PRIMARY KEY,
  bh_code       TEXT NOT NULL UNIQUE,
  x_coord       REAL, y_coord REAL,
  collar_elev   REAL NOT NULL,
  final_depth   REAL NOT NULL,
  hole_type     TEXT,
  drill_date    TEXT,
  water_first_depth REAL
);

CREATE TABLE stratum (
  id            INTEGER PRIMARY KEY,
  bh_id         INTEGER NOT NULL REFERENCES borehole(id),
  layer_no      INTEGER NOT NULL,
  top_depth     REAL NOT NULL,
  bottom_depth  REAL NOT NULL,
  soil_name     TEXT NOT NULL,
  stratum_code  TEXT,
  description   TEXT,
  UNIQUE(bh_id, layer_no)
);

CREATE TABLE sample (
  id            INTEGER PRIMARY KEY,
  bh_id         INTEGER NOT NULL REFERENCES borehole(id),
  sample_code   TEXT NOT NULL,
  top_depth     REAL NOT NULL,
  bottom_depth  REAL NOT NULL,
  sample_type   TEXT,
  test_item     TEXT
);

CREATE TABLE insitu_test (
  id            INTEGER PRIMARY KEY,
  bh_id         INTEGER NOT NULL REFERENCES borehole(id),
  test_type     TEXT NOT NULL,
  test_depth    REAL NOT NULL,
  spt_blow      INTEGER,
  test_date     TEXT
);

CREATE TABLE water_level (
  id            INTEGER PRIMARY KEY,
  bh_id         INTEGER NOT NULL REFERENCES borehole(id),
  obs_date      TEXT,
  first_depth   REAL,
  stable_depth  REAL,
  obs_method    TEXT
);

建好表后,用一条简单查询即可验证关联是否通畅:按孔号统计各子表记录数,若某孔分层数为 0 或层底深度小于终孔深度,说明录入缺失,应在出图前补录。数据库设计阶段多花半天把表结构、命名和约束定死,后续柱状图自动出图才能稳定复现,而不是每次出图都靠人工核对。