414 lines
10 KiB
Markdown
414 lines
10 KiB
Markdown
# `MsSqlor` 技术文档
|
||
|
||
> **模块路径**: `.mssqlor.py`
|
||
> **语言**: Python
|
||
> **数据库支持**: Microsoft SQL Server (通过 `pymssql`)
|
||
> **继承自**: `SQLor`
|
||
|
||
---
|
||
|
||
## 概述
|
||
|
||
`MsSqlor` 是一个专为 **Microsoft SQL Server** 数据库设计的 ORM(对象关系映射)辅助类,继承自通用数据库操作基类 `SQLor`。它封装了针对 SQL Server 的 DDL 模板、数据类型映射、分页查询、元数据提取等核心功能,适用于自动化建模、数据库逆向工程和动态 SQL 构建场景。
|
||
|
||
该类主要用于:
|
||
- 数据库连接识别
|
||
- 类型系统映射(数据库 ↔ 应用模型)
|
||
- 自动生成标准 SQL 语句(如查询、分页、表/字段/主键/外键/索引信息获取)
|
||
|
||
---
|
||
|
||
## 依赖说明
|
||
|
||
```python
|
||
from .sor import SQLor
|
||
from .ddl_template_sqlserver import sqlserver_ddl_tmpl
|
||
```
|
||
|
||
- `SQLor`: 所有数据库适配器的抽象基类。
|
||
- `sqlserver_ddl_tmpl`: 预定义的 SQL Server DDL 模板,用于生成建表语句。
|
||
|
||
---
|
||
|
||
## 类定义
|
||
|
||
```python
|
||
class MsSqlor(SQLor):
|
||
...
|
||
```
|
||
|
||
---
|
||
|
||
## 属性与常量
|
||
|
||
### 1. `ddl_template`
|
||
|
||
```python
|
||
ddl_template = sqlserver_ddl_tmpl
|
||
```
|
||
|
||
- **用途**: 定义创建表时使用的 DDL 模板。
|
||
- **值来源**: 引用外部模块 `ddl_template_sqlserver.sqlserver_ddl_tmpl`
|
||
- **典型内容示例**:
|
||
```sql
|
||
CREATE TABLE [table_name] (
|
||
[col_name] [data_type](...) NULL,
|
||
...
|
||
)
|
||
```
|
||
|
||
---
|
||
|
||
### 2. `db2modelTypeMapping`
|
||
|
||
将 SQL Server 数据库类型映射到应用层模型字段类型。
|
||
|
||
| 数据库类型 | 模型类型 |
|
||
|-----------|----------|
|
||
| `bit`, `tinyint`, `smallint` | `'short'` |
|
||
| `bigint`, `int` | `'long'` |
|
||
| `decimal`, `numeric`, `money`, `smallmoney`, `real`, `float` | `'float'` |
|
||
| `date`, `datetime` | `'date'` |
|
||
| `timestamp`, `uniqueidentifier` | `'timestamp'` |
|
||
| `char` | `'char'` |
|
||
| `varchar`, `nvarchar`, `nchar`, `binary`, `varbinary` | `'str'` |
|
||
| `text`, `ntext` | `'text'` |
|
||
| `image` | `'file'` |
|
||
|
||
> ⚠️ 注意:`uniqueidentifier` 映射为 `'timestamp'` 可能存在语义偏差,建议根据业务需求调整。
|
||
|
||
---
|
||
|
||
### 3. `model2dbTypemapping`
|
||
|
||
将应用模型字段类型反向映射回 SQL Server 数据类型。
|
||
|
||
| 模型类型 | 数据库类型 |
|
||
|---------|------------|
|
||
| `'date'` | `datetime` |
|
||
| `'time'` | `date` *(注意:可能应为 `time` 或 `datetime`)* |
|
||
| `'timestamp'` | `timestamp` |
|
||
| `'str'` | `nvarchar` |
|
||
| `'char'` | `char` |
|
||
| `'short'` | `int` |
|
||
| `'long'` | `numeric` |
|
||
| `'float'` | `numeric` |
|
||
| `'text'` | `ntext` |
|
||
| `'file'` | `image` |
|
||
|
||
> 🔍 提示:`'long'` 和 `'float'` 均映射为 `numeric`,未指定精度,实际使用中需结合上下文补充。
|
||
|
||
---
|
||
|
||
## 类方法
|
||
|
||
### `isMe(cls, name) -> bool`
|
||
|
||
判断当前驱动是否匹配。
|
||
|
||
#### 参数
|
||
- `name` (`str`): 数据库驱动名称,例如 `'pymssql'`
|
||
|
||
#### 返回值
|
||
- `True` 当且仅当 `name == 'pymssql'`
|
||
- 否则返回 `False`
|
||
|
||
#### 示例
|
||
```python
|
||
if MsSqlor.isMe(driver_name):
|
||
db_adapter = MsSqlor()
|
||
```
|
||
|
||
> ✅ 支持多数据库环境下自动识别 SQL Server 连接。
|
||
|
||
---
|
||
|
||
## 实例方法
|
||
|
||
### `grammar(self) -> dict`
|
||
|
||
返回支持的 SQL 语法结构。目前仅注册 `select` 语句模板。
|
||
|
||
#### 返回值
|
||
```python
|
||
{
|
||
'select': select_stmt # 假设全局变量或导入的 SELECT 解析器
|
||
}
|
||
```
|
||
|
||
> ⚠️ 警告:`select_stmt` 未在代码中定义,可能是遗漏或外部引用,请确保其存在。
|
||
|
||
---
|
||
|
||
### `placeHolder(self, varname, pos=None) -> str`
|
||
|
||
生成参数占位符,用于预编译 SQL。
|
||
|
||
#### 参数
|
||
- `varname` (`str`): 参数名
|
||
- `pos` (`int`, optional): 位置索引(本实现未使用)
|
||
|
||
#### 行为逻辑
|
||
- 若参数名为 `__mainsql__`,返回空字符串(通常用于嵌入原始 SQL)
|
||
- 其他情况统一返回 `%s` —— 符合 `pymssql` 参数化语法
|
||
|
||
#### 示例
|
||
```python
|
||
cursor.execute(sql % (), data)
|
||
```
|
||
|
||
> ✅ 安全地防止 SQL 注入。
|
||
|
||
---
|
||
|
||
### `dataConvert(self, dataList) -> tuple`
|
||
|
||
将输入数据标准化为可执行的元组格式。
|
||
|
||
#### 输入类型处理
|
||
- **字典类型**:取 `.values()` 并转为列表 → 元组
|
||
- **列表 of 字典**:提取每个元素的 `'value'` 字段 → 元组
|
||
|
||
#### 示例
|
||
```python
|
||
# 输入1: {'a': 1, 'b': 2} → (1, 2)
|
||
# 输入2: [{'value': 1}, {'value': 2}] → (1, 2)
|
||
```
|
||
|
||
#### 返回值
|
||
- 标准化的 `tuple`,可用于 `cursor.execute(..., params)`
|
||
|
||
---
|
||
|
||
### `pagingSQLmodel(self) -> str`
|
||
|
||
返回适用于 SQL Server 的分页查询模板(基于 `ROW_NUMBER()` 窗口函数)。
|
||
|
||
#### 模板结构
|
||
```sql
|
||
SELECT *
|
||
FROM (
|
||
SELECT ROW_NUMBER() OVER(ORDER BY $[sort]$) AS _row_id, page_s.*
|
||
FROM (%s) page_s
|
||
) A
|
||
WHERE _row_id >= $[from_line]$ AND _row_id < $[end_line]$
|
||
```
|
||
|
||
#### 占位符说明
|
||
| 占位符 | 含义 |
|
||
|----------------|------------------------|
|
||
| `%s` | 子查询(原 SQL) |
|
||
| `$[sort]$` | 排序字段 |
|
||
| `$[from_line]$`| 起始行号(含) |
|
||
| `$[end_line]$` | 结束行号(不含) |
|
||
|
||
#### 使用方式
|
||
此模板需配合字符串替换工具填充实际值,常用于构建分页接口。
|
||
|
||
> ✅ 兼容 SQL Server 2005+ 版本。
|
||
|
||
---
|
||
|
||
### `tablesSQL(self) -> str`
|
||
|
||
获取当前数据库所有用户表及其标题(描述)。
|
||
|
||
#### 查询语句
|
||
```sql
|
||
SELECT
|
||
LOWER(d.name) AS name,
|
||
LOWER(CAST(ISNULL(f.VALUE, d.name) AS NVARCHAR)) AS title
|
||
FROM sysobjects d
|
||
LEFT JOIN sys.extended_properties f ON d.id = f.major_id AND f.minor_id = 0
|
||
WHERE d.xtype = 'U'
|
||
```
|
||
|
||
#### 字段说明
|
||
- `name`: 表名(小写)
|
||
- `title`: 表备注 / 描述(若无则用表名代替)
|
||
|
||
> 📌 `xtype = 'U'` 表示用户表。
|
||
|
||
---
|
||
|
||
### `fieldsSQL(self, tablename=None) -> str`
|
||
|
||
获取指定表或全部表的字段元数据。
|
||
|
||
#### 查询语句摘要
|
||
```sql
|
||
SELECT
|
||
name = LOWER(a.name),
|
||
type = b.name,
|
||
length = COLUMNPROPERTY(a.id, a.name, 'PRECISION'),
|
||
dec = ISNULL(COLUMNPROPERTY(a.id, a.name, 'Scale'), NULL),
|
||
nullable = CASE WHEN a.isnullable = 1 THEN 'yes' ELSE 'no' END,
|
||
title = LOWER(CAST(ISNULL(g.[value], a.name) AS NVARCHAR)),
|
||
table_name = LOWER(d.name)
|
||
FROM syscolumns a
|
||
...
|
||
WHERE schema_name(schema_id) = 'dbo'
|
||
```
|
||
|
||
#### 条件控制
|
||
- 若传入 `tablename`,则添加 `WHERE LOWER(d.name) = 'xxx'`
|
||
- 最终按 `a.id, a.colorder` 排序(保证列顺序)
|
||
|
||
#### 返回字段
|
||
| 字段 | 说明 |
|
||
|-------------|--------------------------|
|
||
| `name` | 列名(小写) |
|
||
| `type` | 数据类型 |
|
||
| `length` | 精度(字符长度或数字总位数) |
|
||
| `dec` | 小数位数 |
|
||
| `nullable` | 是否可为空(yes/no) |
|
||
| `title` | 列说明(扩展属性) |
|
||
| `table_name`| 所属表名(小写) |
|
||
|
||
> ✅ 支持从 `sys.extended_properties` 获取注释。
|
||
|
||
---
|
||
|
||
### `fkSQL(self, tablename=None) -> str`
|
||
|
||
获取外键关系(引用其他表的主键作为本表外键)。
|
||
|
||
#### 查询逻辑
|
||
查找以某表为主表(被引用)的所有外键关联。
|
||
|
||
```sql
|
||
SELECT
|
||
MainCol.name AS field, -- 主表列名(被引用)
|
||
oSub.name AS fk_table, -- 子表名称(引用方)
|
||
SubCol.name AS fk_field -- 子表列名(外键列)
|
||
FROM sys.foreign_keys fk
|
||
JOIN ... -- 多表连接定位主子表及列
|
||
```
|
||
|
||
#### 条件
|
||
- 可选过滤:`lower(oMain.name) = 'xxx'`,即只查某主表被哪些表引用
|
||
|
||
#### 示例输出
|
||
| field | fk_table | fk_field |
|
||
|-------|----------|----------|
|
||
| id | orders | user_id |
|
||
|
||
> 💡 适用于构建实体关系图(ERD)。
|
||
|
||
---
|
||
|
||
### `pkSQL(self, tablename=None) -> str`
|
||
|
||
获取主键字段信息。
|
||
|
||
#### 查询语句
|
||
```sql
|
||
SELECT
|
||
LOWER(a.table_name) AS table_name,
|
||
LOWER(b.column_name) AS field_name
|
||
FROM information_schema.table_constraints a
|
||
INNER JOIN information_schema.constraint_column_usage b
|
||
ON a.constraint_name = b.constraint_name
|
||
WHERE a.constraint_type = 'PRIMARY KEY'
|
||
```
|
||
|
||
#### 可选过滤
|
||
- 若提供 `tablename`,追加条件:`AND LOWER(a.table_name) = 'xxx'`
|
||
|
||
#### 输出
|
||
每条记录表示一个主键列。
|
||
|
||
---
|
||
|
||
### `indexesSQL(self, tablename=None) -> str`
|
||
|
||
获取索引信息(包括唯一性与包含的列)。
|
||
|
||
#### 查询语句
|
||
```sql
|
||
SELECT
|
||
index_name = LOWER(IDX.Name),
|
||
index_type = IDX.is_unique,
|
||
column_name = LOWER(C.Name)
|
||
FROM sys.indexes IDX
|
||
INNER JOIN sys.index_columns IDXC ...
|
||
INNER JOIN sys.columns C ...
|
||
WHERE O.type = 'U' AND O.is_ms_shipped = 0
|
||
```
|
||
|
||
#### 条件
|
||
- 排除系统对象(`is_ms_shipped=0`)
|
||
- 可选按表名过滤:`LOWER(O.name) = 'xxx'`
|
||
|
||
#### 输出字段
|
||
| 字段 | 说明 |
|
||
|--------------|------------------------------|
|
||
| `index_name` | 索引名(小写) |
|
||
| `index_type` | 是否唯一(1=唯一;0=非唯一) |
|
||
| `column_name`| 索引包含的列名 |
|
||
|
||
> ✅ 支持复合索引拆解为多行展示。
|
||
|
||
---
|
||
|
||
## 使用示例
|
||
|
||
### 获取所有表
|
||
```python
|
||
db = MsSqlor(connection)
|
||
sql = db.tablesSQL()
|
||
cursor.execute(sql)
|
||
tables = cursor.fetchall()
|
||
for t in tables:
|
||
print(t['name'], t['title'])
|
||
```
|
||
|
||
### 获取某表字段
|
||
```python
|
||
sql = db.fieldsSQL('users')
|
||
cursor.execute(sql)
|
||
fields = cursor.fetchall()
|
||
for f in fields:
|
||
print(f['name'], f['type'], f['nullable'])
|
||
```
|
||
|
||
### 分页查询构造
|
||
```python
|
||
base_sql = "SELECT id, name FROM users"
|
||
pager = db.pagingSQLmodel() % base_sql
|
||
pager = pager.replace('$[sort]$', 'id') \
|
||
.replace('$[from_line]$', '1') \
|
||
.replace('$[end_line]$', '11')
|
||
cursor.execute(pager)
|
||
```
|
||
|
||
---
|
||
|
||
## 已知限制与建议
|
||
|
||
| 问题 | 建议 |
|
||
|------|------|
|
||
| `select_stmt` 未定义 | 确保已在作用域内定义或导入 |
|
||
| `time` 模型映射为 `date` | 应改为 `time` 或 `datetime` |
|
||
| `uniqueidentifier` 映射为 `timestamp` | 语义错误,建议新增 `'guid'` 类型 |
|
||
| 不支持模式(schema)切换 | 当前固定 `schema_name='dbo'`,如需扩展建议增加参数 |
|
||
| 缺少 `create`, `insert` 等语法支持 | 可在 `grammar()` 中逐步扩展 |
|
||
|
||
---
|
||
|
||
## 总结
|
||
|
||
`MsSqlor` 是一个功能完整的 SQL Server 数据库适配器,具备以下优势:
|
||
|
||
✅ 自动识别驱动
|
||
✅ 完善的类型双向映射
|
||
✅ 强大的元数据查询能力(表、列、主键、外键、索引)
|
||
✅ 标准化的分页模板
|
||
✅ 支持参数化查询与安全执行
|
||
|
||
适合集成于 ORM 框架、数据库管理工具或低代码平台中,实现对 SQL Server 的自动化操作。
|
||
|
||
---
|
||
|
||
> 📎 文档版本:v1.0
|
||
> 📅 更新日期:2025年4月5日 |