|
|
@@ -0,0 +1,442 @@
|
|
|
+"""
|
|
|
+SQL 查询构建器
|
|
|
+
|
|
|
+提供链式 API 构建 SQL 查询语句。
|
|
|
+"""
|
|
|
+
|
|
|
+from typing import Any, Dict, List, Optional, Union
|
|
|
+from src.common.logging_config import get_logger
|
|
|
+
|
|
|
+logger = get_logger(__name__)
|
|
|
+
|
|
|
+
|
|
|
+class QueryBuilder:
|
|
|
+ """
|
|
|
+ SQL 查询构建器
|
|
|
+
|
|
|
+ 提供链式 API 构建 SQL 查询语句,支持:
|
|
|
+ - WHERE 条件构建
|
|
|
+ - ORDER BY 排序
|
|
|
+ - LIMIT/OFFSET 分页
|
|
|
+ - JOIN 关联查询
|
|
|
+ """
|
|
|
+
|
|
|
+ def __init__(self, table_name: str):
|
|
|
+ """
|
|
|
+ 初始化查询构建器
|
|
|
+
|
|
|
+ Args:
|
|
|
+ table_name: 表名
|
|
|
+ """
|
|
|
+ self.table_name = table_name
|
|
|
+ self._select_fields = ["*"]
|
|
|
+ self._where_conditions = []
|
|
|
+ self._where_params = []
|
|
|
+ self._order_by = None
|
|
|
+ self._limit_value = None
|
|
|
+ self._offset_value = None
|
|
|
+ self._joins = []
|
|
|
+ self._group_by = None
|
|
|
+ self._having = None
|
|
|
+
|
|
|
+ def select(self, fields: Union[str, List[str]]) -> 'QueryBuilder':
|
|
|
+ """
|
|
|
+ 指定查询字段
|
|
|
+
|
|
|
+ Args:
|
|
|
+ fields: 字段名或字段列表,如 "id, name" 或 ["id", "name"]
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ QueryBuilder 实例(支持链式调用)
|
|
|
+ """
|
|
|
+ if isinstance(fields, str):
|
|
|
+ self._select_fields = [f.strip() for f in fields.split(",")]
|
|
|
+ else:
|
|
|
+ self._select_fields = fields
|
|
|
+ return self
|
|
|
+
|
|
|
+ def where(self, field: str, operator: str, value: Any) -> 'QueryBuilder':
|
|
|
+ """
|
|
|
+ 添加 WHERE 条件
|
|
|
+
|
|
|
+ Args:
|
|
|
+ field: 字段名
|
|
|
+ operator: 操作符,如 "=", ">", "<", "LIKE", "IN", "BETWEEN" 等
|
|
|
+ value: 条件值
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ QueryBuilder 实例(支持链式调用)
|
|
|
+
|
|
|
+ Examples:
|
|
|
+ >>> builder.where("id", "=", 1)
|
|
|
+ >>> builder.where("age", ">", 18)
|
|
|
+ >>> builder.where("name", "LIKE", "%test%")
|
|
|
+ >>> builder.where("status", "IN", [1, 2, 3])
|
|
|
+ """
|
|
|
+ if operator.upper() == "IN":
|
|
|
+ if not isinstance(value, (list, tuple)):
|
|
|
+ raise ValueError("IN 操作符的值必须是列表或元组")
|
|
|
+ placeholders = ",".join(["%s"] * len(value))
|
|
|
+ self._where_conditions.append(f"{field} IN ({placeholders})")
|
|
|
+ self._where_params.extend(value)
|
|
|
+ elif operator.upper() == "BETWEEN":
|
|
|
+ if not isinstance(value, (list, tuple)) or len(value) != 2:
|
|
|
+ raise ValueError("BETWEEN 操作符的值必须是包含两个元素的列表或元组")
|
|
|
+ self._where_conditions.append(f"{field} BETWEEN %s AND %s")
|
|
|
+ self._where_params.extend(value)
|
|
|
+ elif operator.upper() == "IS NULL":
|
|
|
+ self._where_conditions.append(f"{field} IS NULL")
|
|
|
+ elif operator.upper() == "IS NOT NULL":
|
|
|
+ self._where_conditions.append(f"{field} IS NOT NULL")
|
|
|
+ else:
|
|
|
+ self._where_conditions.append(f"{field} {operator} %s")
|
|
|
+ self._where_params.append(value)
|
|
|
+ return self
|
|
|
+
|
|
|
+ def where_eq(self, field: str, value: Any) -> 'QueryBuilder':
|
|
|
+ """等于条件(便捷方法)"""
|
|
|
+ return self.where(field, "=", value)
|
|
|
+
|
|
|
+ def where_ne(self, field: str, value: Any) -> 'QueryBuilder':
|
|
|
+ """不等于条件(便捷方法)"""
|
|
|
+ return self.where(field, "!=", value)
|
|
|
+
|
|
|
+ def where_gt(self, field: str, value: Any) -> 'QueryBuilder':
|
|
|
+ """大于条件(便捷方法)"""
|
|
|
+ return self.where(field, ">", value)
|
|
|
+
|
|
|
+ def where_gte(self, field: str, value: Any) -> 'QueryBuilder':
|
|
|
+ """大于等于条件(便捷方法)"""
|
|
|
+ return self.where(field, ">=", value)
|
|
|
+
|
|
|
+ def where_lt(self, field: str, value: Any) -> 'QueryBuilder':
|
|
|
+ """小于条件(便捷方法)"""
|
|
|
+ return self.where(field, "<", value)
|
|
|
+
|
|
|
+ def where_lte(self, field: str, value: Any) -> 'QueryBuilder':
|
|
|
+ """小于等于条件(便捷方法)"""
|
|
|
+ return self.where(field, "<=", value)
|
|
|
+
|
|
|
+ def where_like(self, field: str, value: str) -> 'QueryBuilder':
|
|
|
+ """LIKE 条件(便捷方法)"""
|
|
|
+ return self.where(field, "LIKE", value)
|
|
|
+
|
|
|
+ def where_in(self, field: str, values: List[Any]) -> 'QueryBuilder':
|
|
|
+ """IN 条件(便捷方法)"""
|
|
|
+ return self.where(field, "IN", values)
|
|
|
+
|
|
|
+ def where_between(self, field: str, start: Any, end: Any) -> 'QueryBuilder':
|
|
|
+ """BETWEEN 条件(便捷方法)"""
|
|
|
+ return self.where(field, "BETWEEN", [start, end])
|
|
|
+
|
|
|
+ def where_conditions(self, conditions: Dict[str, Any]) -> 'QueryBuilder':
|
|
|
+ """
|
|
|
+ 批量添加 WHERE 条件
|
|
|
+
|
|
|
+ Args:
|
|
|
+ conditions: 条件字典,如 {"id": 1, "status": "active"}
|
|
|
+ 默认使用 = 操作符
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ QueryBuilder 实例(支持链式调用)
|
|
|
+ """
|
|
|
+ for field, value in conditions.items():
|
|
|
+ self.where_eq(field, value)
|
|
|
+ return self
|
|
|
+
|
|
|
+ def order_by(self, field: str, direction: str = "ASC") -> 'QueryBuilder':
|
|
|
+ """
|
|
|
+ 添加排序
|
|
|
+
|
|
|
+ Args:
|
|
|
+ field: 排序字段
|
|
|
+ direction: 排序方向,ASC 或 DESC
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ QueryBuilder 实例(支持链式调用)
|
|
|
+ """
|
|
|
+ if direction.upper() not in ["ASC", "DESC"]:
|
|
|
+ raise ValueError("排序方向必须是 ASC 或 DESC")
|
|
|
+ self._order_by = f"{field} {direction.upper()}"
|
|
|
+ return self
|
|
|
+
|
|
|
+ def limit(self, count: int) -> 'QueryBuilder':
|
|
|
+ """
|
|
|
+ 限制返回数量
|
|
|
+
|
|
|
+ Args:
|
|
|
+ count: 返回数量
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ QueryBuilder 实例(支持链式调用)
|
|
|
+ """
|
|
|
+ self._limit_value = count
|
|
|
+ return self
|
|
|
+
|
|
|
+ def offset(self, count: int) -> 'QueryBuilder':
|
|
|
+ """
|
|
|
+ 设置偏移量
|
|
|
+
|
|
|
+ Args:
|
|
|
+ count: 偏移量
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ QueryBuilder 实例(支持链式调用)
|
|
|
+ """
|
|
|
+ self._offset_value = count
|
|
|
+ return self
|
|
|
+
|
|
|
+ def join(self, table: str, on: str, join_type: str = "INNER") -> 'QueryBuilder':
|
|
|
+ """
|
|
|
+ 添加 JOIN
|
|
|
+
|
|
|
+ Args:
|
|
|
+ table: 关联表名
|
|
|
+ on: JOIN 条件,如 "table1.id = table2.foreign_id"
|
|
|
+ join_type: JOIN 类型,INNER, LEFT, RIGHT, FULL
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ QueryBuilder 实例(支持链式调用)
|
|
|
+ """
|
|
|
+ join_type = join_type.upper()
|
|
|
+ if join_type not in ["INNER", "LEFT", "RIGHT", "FULL"]:
|
|
|
+ raise ValueError("JOIN 类型必须是 INNER, LEFT, RIGHT 或 FULL")
|
|
|
+ self._joins.append(f"{join_type} JOIN {table} ON {on}")
|
|
|
+ return self
|
|
|
+
|
|
|
+ def group_by(self, field: str) -> 'QueryBuilder':
|
|
|
+ """
|
|
|
+ 添加 GROUP BY
|
|
|
+
|
|
|
+ Args:
|
|
|
+ field: 分组字段
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ QueryBuilder 实例(支持链式调用)
|
|
|
+ """
|
|
|
+ self._group_by = field
|
|
|
+ return self
|
|
|
+
|
|
|
+ def having(self, condition: str) -> 'QueryBuilder':
|
|
|
+ """
|
|
|
+ 添加 HAVING 条件
|
|
|
+
|
|
|
+ Args:
|
|
|
+ condition: HAVING 条件,如 "COUNT(*) > 10"
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ QueryBuilder 实例(支持链式调用)
|
|
|
+ """
|
|
|
+ self._having = condition
|
|
|
+ return self
|
|
|
+
|
|
|
+ def build_select(self) -> tuple[str, List[Any]]:
|
|
|
+ """
|
|
|
+ 构建 SELECT 查询语句
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ (SQL 语句, 参数列表) 元组
|
|
|
+ """
|
|
|
+ # 构建 SELECT 字段
|
|
|
+ select_clause = ", ".join(self._select_fields)
|
|
|
+
|
|
|
+ # 构建 FROM 子句
|
|
|
+ from_clause = f"FROM {self.table_name}"
|
|
|
+
|
|
|
+ # 构建 JOIN 子句
|
|
|
+ join_clause = ""
|
|
|
+ if self._joins:
|
|
|
+ join_clause = " " + " ".join(self._joins)
|
|
|
+
|
|
|
+ # 构建 WHERE 子句
|
|
|
+ where_clause = ""
|
|
|
+ if self._where_conditions:
|
|
|
+ where_clause = " WHERE " + " AND ".join(self._where_conditions)
|
|
|
+
|
|
|
+ # 构建 GROUP BY 子句
|
|
|
+ group_by_clause = ""
|
|
|
+ if self._group_by:
|
|
|
+ group_by_clause = f" GROUP BY {self._group_by}"
|
|
|
+
|
|
|
+ # 构建 HAVING 子句
|
|
|
+ having_clause = ""
|
|
|
+ if self._having:
|
|
|
+ having_clause = f" HAVING {self._having}"
|
|
|
+
|
|
|
+ # 构建 ORDER BY 子句
|
|
|
+ order_by_clause = ""
|
|
|
+ if self._order_by:
|
|
|
+ order_by_clause = f" ORDER BY {self._order_by}"
|
|
|
+
|
|
|
+ # 构建 LIMIT 子句
|
|
|
+ limit_clause = ""
|
|
|
+ if self._limit_value is not None:
|
|
|
+ limit_clause = f" LIMIT {self._limit_value}"
|
|
|
+ if self._offset_value is not None:
|
|
|
+ limit_clause += f" OFFSET {self._offset_value}"
|
|
|
+
|
|
|
+ # 组合 SQL
|
|
|
+ sql = f"SELECT {select_clause} {from_clause}{join_clause}{where_clause}{group_by_clause}{having_clause}{order_by_clause}{limit_clause}"
|
|
|
+
|
|
|
+ return sql.strip(), self._where_params
|
|
|
+
|
|
|
+ def build_count(self) -> tuple[str, List[Any]]:
|
|
|
+ """
|
|
|
+ 构建 COUNT 查询语句
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ (SQL 语句, 参数列表) 元组
|
|
|
+ """
|
|
|
+ # 构建 FROM 子句
|
|
|
+ from_clause = f"FROM {self.table_name}"
|
|
|
+
|
|
|
+ # 构建 JOIN 子句
|
|
|
+ join_clause = ""
|
|
|
+ if self._joins:
|
|
|
+ join_clause = " " + " ".join(self._joins)
|
|
|
+
|
|
|
+ # 构建 WHERE 子句
|
|
|
+ where_clause = ""
|
|
|
+ if self._where_conditions:
|
|
|
+ where_clause = " WHERE " + " AND ".join(self._where_conditions)
|
|
|
+
|
|
|
+ # 构建 GROUP BY 子句
|
|
|
+ group_by_clause = ""
|
|
|
+ if self._group_by:
|
|
|
+ group_by_clause = f" GROUP BY {self._group_by}"
|
|
|
+
|
|
|
+ # 构建 HAVING 子句
|
|
|
+ having_clause = ""
|
|
|
+ if self._having:
|
|
|
+ having_clause = f" HAVING {self._having}"
|
|
|
+
|
|
|
+ sql = f"SELECT COUNT(*) as count {from_clause}{join_clause}{where_clause}{group_by_clause}{having_clause}"
|
|
|
+
|
|
|
+ return sql.strip(), self._where_params
|
|
|
+
|
|
|
+ def build_insert(self, data: Dict[str, Any]) -> tuple[str, List[Any]]:
|
|
|
+ """
|
|
|
+ 构建 INSERT 语句
|
|
|
+
|
|
|
+ Args:
|
|
|
+ data: 要插入的数据字典
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ (SQL 语句, 参数列表) 元组
|
|
|
+ """
|
|
|
+ if not data:
|
|
|
+ raise ValueError("插入数据不能为空")
|
|
|
+
|
|
|
+ fields = list(data.keys())
|
|
|
+ placeholders = ", ".join(["%s"] * len(fields))
|
|
|
+ fields_str = ", ".join(fields)
|
|
|
+
|
|
|
+ sql = f"INSERT INTO {self.table_name} ({fields_str}) VALUES ({placeholders})"
|
|
|
+ params = list(data.values())
|
|
|
+
|
|
|
+ return sql, params
|
|
|
+
|
|
|
+ def build_bulk_insert(self, data_list: List[Dict[str, Any]]) -> tuple[str, List[Any]]:
|
|
|
+ """
|
|
|
+ 构建批量 INSERT 语句
|
|
|
+
|
|
|
+ Args:
|
|
|
+ data_list: 要插入的数据列表
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ (SQL 语句, 参数列表) 元组
|
|
|
+ """
|
|
|
+ if not data_list:
|
|
|
+ raise ValueError("插入数据列表不能为空")
|
|
|
+
|
|
|
+ # 使用第一个字典的键作为字段名
|
|
|
+ fields = list(data_list[0].keys())
|
|
|
+ fields_str = ", ".join(fields)
|
|
|
+
|
|
|
+ # 构建 VALUES 子句
|
|
|
+ values_list = []
|
|
|
+ params = []
|
|
|
+ for data in data_list:
|
|
|
+ if set(data.keys()) != set(fields):
|
|
|
+ raise ValueError("批量插入的数据字典字段必须一致")
|
|
|
+ placeholders = ", ".join(["%s"] * len(fields))
|
|
|
+ values_list.append(f"({placeholders})")
|
|
|
+ params.extend([data[field] for field in fields])
|
|
|
+
|
|
|
+ values_str = ", ".join(values_list)
|
|
|
+ sql = f"INSERT INTO {self.table_name} ({fields_str}) VALUES {values_str}"
|
|
|
+
|
|
|
+ return sql, params
|
|
|
+
|
|
|
+ def build_update(self, data: Dict[str, Any], conditions: Dict[str, Any] = None) -> tuple[str, List[Any]]:
|
|
|
+ """
|
|
|
+ 构建 UPDATE 语句
|
|
|
+
|
|
|
+ Args:
|
|
|
+ data: 要更新的数据字典
|
|
|
+ conditions: 更新条件字典(如果提供,会添加到 WHERE 子句)
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ (SQL 语句, 参数列表) 元组
|
|
|
+ """
|
|
|
+ if not data:
|
|
|
+ raise ValueError("更新数据不能为空")
|
|
|
+
|
|
|
+ # 构建 SET 子句
|
|
|
+ set_clauses = []
|
|
|
+ params = []
|
|
|
+ for field, value in data.items():
|
|
|
+ set_clauses.append(f"{field} = %s")
|
|
|
+ params.append(value)
|
|
|
+
|
|
|
+ set_clause = ", ".join(set_clauses)
|
|
|
+
|
|
|
+ # 构建 WHERE 子句
|
|
|
+ where_clause = ""
|
|
|
+ if conditions:
|
|
|
+ where_conditions = []
|
|
|
+ for field, value in conditions.items():
|
|
|
+ where_conditions.append(f"{field} = %s")
|
|
|
+ params.append(value)
|
|
|
+ where_clause = " WHERE " + " AND ".join(where_conditions)
|
|
|
+ elif self._where_conditions:
|
|
|
+ where_clause = " WHERE " + " AND ".join(self._where_conditions)
|
|
|
+ params.extend(self._where_params)
|
|
|
+
|
|
|
+ if not where_clause:
|
|
|
+ raise ValueError("UPDATE 语句必须包含 WHERE 条件")
|
|
|
+
|
|
|
+ sql = f"UPDATE {self.table_name} SET {set_clause}{where_clause}"
|
|
|
+
|
|
|
+ return sql, params
|
|
|
+
|
|
|
+ def build_delete(self, conditions: Dict[str, Any] = None) -> tuple[str, List[Any]]:
|
|
|
+ """
|
|
|
+ 构建 DELETE 语句
|
|
|
+
|
|
|
+ Args:
|
|
|
+ conditions: 删除条件字典(如果提供,会添加到 WHERE 子句)
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ (SQL 语句, 参数列表) 元组
|
|
|
+ """
|
|
|
+ params = []
|
|
|
+
|
|
|
+ # 构建 WHERE 子句
|
|
|
+ where_clause = ""
|
|
|
+ if conditions:
|
|
|
+ where_conditions = []
|
|
|
+ for field, value in conditions.items():
|
|
|
+ where_conditions.append(f"{field} = %s")
|
|
|
+ params.append(value)
|
|
|
+ where_clause = " WHERE " + " AND ".join(where_conditions)
|
|
|
+ elif self._where_conditions:
|
|
|
+ where_clause = " WHERE " + " AND ".join(self._where_conditions)
|
|
|
+ params.extend(self._where_params)
|
|
|
+
|
|
|
+ if not where_clause:
|
|
|
+ raise ValueError("DELETE 语句必须包含 WHERE 条件")
|
|
|
+
|
|
|
+ sql = f"DELETE FROM {self.table_name}{where_clause}"
|
|
|
+
|
|
|
+ return sql, params
|