129 lines
4.8 KiB
Python
129 lines
4.8 KiB
Python
from datetime import datetime, timezone
|
||
from flask import current_app
|
||
from databricks import sql
|
||
|
||
|
||
class DatabricksQuery:
|
||
def __init__(self):
|
||
"""纯Python连接Databricks SQL,无JVM依赖"""
|
||
try:
|
||
# 读取配置(参数和原代码一致,无需修改)
|
||
self.host = current_app.config["DATABRICKS_HOST"]
|
||
self.token = current_app.config["DATABRICKS_TOKEN"]
|
||
self.cluster_id = current_app.config["CLUSTER_ID"]
|
||
|
||
# 校验参数
|
||
if not all([self.host, self.token, self.cluster_id]):
|
||
raise ValueError(
|
||
"必须配置DATABRICKS_HOST、DATABRICKS_TOKEN、CLUSTER_ID"
|
||
)
|
||
|
||
print("Databricks SQL 连接器初始化成功")
|
||
except Exception as e:
|
||
print(f"初始化失败: {e}")
|
||
raise
|
||
|
||
# 复用原有参数校验逻辑
|
||
@staticmethod
|
||
def _validate_table_name(table_name):
|
||
if not table_name or not isinstance(table_name, str):
|
||
raise ValueError(f"无效的表名: {table_name}")
|
||
if ";" in table_name:
|
||
raise ValueError(f"表名包含非法字符 ';': {table_name}")
|
||
parts = table_name.split(".")
|
||
if len(parts) != 3:
|
||
raise ValueError(f"表名格式应为 catalog.schema.table: {table_name}")
|
||
|
||
@staticmethod
|
||
def _validate_vin(vin):
|
||
if vin is None:
|
||
return True
|
||
if not isinstance(vin, str):
|
||
raise ValueError(f"无效的 VIN: {vin} (必须是17位字母数字)")
|
||
return True
|
||
|
||
def query_table(
|
||
self, table_name, vin=None, start_time=None, end_time=None,limit=None
|
||
):
|
||
"""纯Python查询表数据,无JVM依赖"""
|
||
try:
|
||
# 验证参数
|
||
self._validate_table_name(table_name)
|
||
self._validate_vin(vin)
|
||
|
||
# 构建SQL查询语句(替代原PySpark DataFrame操作)
|
||
select_fields = """
|
||
Incident__Name AS incident_name,
|
||
Incident__description AS incident_description,
|
||
Meaning AS meaning,
|
||
Signal__Name As signal_name,
|
||
incident_time,
|
||
oneid,
|
||
speed,
|
||
int_value,
|
||
idc_tickcount_ms,
|
||
latitude,
|
||
longitude,
|
||
mux_data,
|
||
gps_heading,
|
||
float_value
|
||
"""
|
||
|
||
sql_query = f"SELECT {select_fields} FROM {table_name}"
|
||
where_conditions = []
|
||
|
||
# 拼接过滤条件
|
||
if vin:
|
||
where_conditions.append(f"oneid = '{vin}'")
|
||
if start_time:
|
||
start_dt = self._parse_iso_time(start_time).isoformat()
|
||
where_conditions.append(f"incident_time >= '{start_dt}'")
|
||
if end_time:
|
||
end_dt = self._parse_iso_time(end_time).isoformat()
|
||
where_conditions.append(f"incident_time <= '{end_dt}'")
|
||
|
||
# 拼接WHERE子句
|
||
if where_conditions:
|
||
sql_query += " WHERE " + " AND ".join(where_conditions)
|
||
# 拼接LIMIT
|
||
if limit is not None:
|
||
sql_query += f" LIMIT {limit}"
|
||
|
||
# sql_query="select * from hive_metastore.taf_level_two_plus.accident_report_data WHERE oneid = 'LE4LG4GB6SL256937' limit 1"
|
||
print(f"查询sql:{sql_query}")
|
||
|
||
# 执行SQL(纯Python,无JVM)
|
||
with sql.connect(
|
||
server_hostname=self.host,
|
||
http_path=f"sql/protocolv1/o/3035612432650494/{self.cluster_id}", # 注意路径格式
|
||
access_token=self.token,
|
||
) as connection:
|
||
with connection.cursor() as cursor:
|
||
cursor.execute(sql_query)
|
||
# 获取列名和数据,转换为字典格式(和原代码返回格式一致)
|
||
columns = [desc[0] for desc in cursor.description]
|
||
results = []
|
||
for row in cursor.fetchall():
|
||
results.append(dict(zip(columns, row)))
|
||
return results
|
||
|
||
except Exception as e:
|
||
print(f"查询失败: {str(e)}")
|
||
raise
|
||
|
||
# 复用原有时间解析逻辑
|
||
def _parse_iso_time(self, time_str):
|
||
try:
|
||
if time_str.endswith("Z"):
|
||
time_str = time_str[:-1] + "+00:00"
|
||
dt = datetime.fromisoformat(time_str)
|
||
if dt.tzinfo is None:
|
||
dt = dt.replace(tzinfo=timezone.utc)
|
||
return dt
|
||
except Exception as e:
|
||
raise ValueError(f"无效的时间格式 '{time_str}': {str(e)}")
|
||
|
||
def stop(self):
|
||
# 纯Python连接器无需关闭SparkSession,此处保留方法以兼容原有调用逻辑
|
||
print("Databricks SQL 连接已释放")
|