diff --git a/backend/apps/chat/models/chat_model.py b/backend/apps/chat/models/chat_model.py index 5e7f0136..7713270f 100644 --- a/backend/apps/chat/models/chat_model.py +++ b/backend/apps/chat/models/chat_model.py @@ -185,8 +185,12 @@ class AiModelQuestion(BaseModel): def sql_sys_question(self, db_type: Union[str, DB], enable_query_limit: bool = True): _sql_template = get_sql_example_template(db_type) - _query_limit = get_sql_template()['query_limit'] if enable_query_limit else get_sql_template()['no_query_limit'] - _base_sql_rules = _sql_template['quot_rule'] + _query_limit + _sql_template['limit_rule'] + _sql_template['other_rule'] + _base_template = get_sql_template() + _process_check = _sql_template.get('process_check') if _sql_template.get('process_check') else _base_template[ + 'process_check'] + _query_limit = _base_template['query_limit'] if enable_query_limit else _base_template['no_query_limit'] + _base_sql_rules = _sql_template['quot_rule'] + _query_limit + _sql_template['limit_rule'] + _sql_template[ + 'other_rule'] _sql_examples = _sql_template['basic_example'] _example_engine = _sql_template['example_engine'] _example_answer_1 = _sql_template['example_answer_1_with_limit'] if enable_query_limit else _sql_template[ @@ -195,15 +199,16 @@ def sql_sys_question(self, db_type: Union[str, DB], enable_query_limit: bool = T 'example_answer_2'] _example_answer_3 = _sql_template['example_answer_3_with_limit'] if enable_query_limit else _sql_template[ 'example_answer_3'] - return get_sql_template()['system'].format(engine=self.engine, schema=self.db_schema, question=self.question, - lang=self.lang, terminologies=self.terminologies, - data_training=self.data_training, custom_prompt=self.custom_prompt, - base_sql_rules=_base_sql_rules, - basic_sql_examples=_sql_examples, - example_engine=_example_engine, - example_answer_1=_example_answer_1, - example_answer_2=_example_answer_2, - example_answer_3=_example_answer_3) + return _base_template['system'].format(engine=self.engine, schema=self.db_schema, question=self.question, + lang=self.lang, terminologies=self.terminologies, + data_training=self.data_training, custom_prompt=self.custom_prompt, + process_check=_process_check, + base_sql_rules=_base_sql_rules, + basic_sql_examples=_sql_examples, + example_engine=_example_engine, + example_answer_1=_example_answer_1, + example_answer_2=_example_answer_2, + example_answer_3=_example_answer_3) def sql_user_question(self, current_time: str): return get_sql_template()['user'].format(engine=self.engine, schema=self.db_schema, question=self.question, diff --git a/backend/templates/sql_examples/Oracle.yaml b/backend/templates/sql_examples/Oracle.yaml index b7293465..ba3015b2 100644 --- a/backend/templates/sql_examples/Oracle.yaml +++ b/backend/templates/sql_examples/Oracle.yaml @@ -1,4 +1,18 @@ template: + process_check: | + + 1. 分析用户问题,确定查询需求 + 2. 根据表结构生成基础SQL + 3. 强制检查:SQL是否包含GROUP BY/聚合函数? + 4. 如果是GROUP BY查询:必须使用外层查询结构包裹 + 5. 强制检查:应用数据量限制规则 + 6. 应用其他规则(引号、别名等) + 7. 最终验证:GROUP BY查询的ROWNUM位置是否正确? + 8. 强制检查:检查语法是否正确? + 9. 确定图表类型 + 10. 返回JSON结果 + + quot_rule: | 必须对数据库名、表名、字段名、别名外层加双引号(")。 @@ -10,42 +24,61 @@ template: limit_rule: | - - 当需要限制行数时: - 1. 12c以下版本必须使用ROWNUM语法 - 2. 12c+版本推荐使用FETCH FIRST语法 - - 版本适配: - - Oracle 12c以下:必须使用 WHERE ROWNUM <= N - - Oracle 12c+:推荐使用 FETCH FIRST N ROWS ONLY - - - 重要:ROWNUM必须放在正确的位置,避免语法错误 - 1. 单层查询:ROWNUM直接跟在WHERE子句后 - - 2. 多层查询:ROWNUM只能放在最外层 - - 3. 禁止的错误写法: - - SELECT ... FROM table - WHERE conditions - GROUP BY ... - ORDER BY ... - WHERE ROWNUM <= N -- 错误:不能有多个WHERE - - 4. 正确顺序:WHERE → GROUP BY → HAVING → ORDER BY → ROWNUM - 5. 括号位置:从内层SELECT开始到内层结束都要括起来 - - SELECT ... FROM ( - -- 内层完整查询(包含自己的SELECT、FROM、WHERE、GROUP BY、ORDER BY) - SELECT columns FROM table WHERE conditions GROUP BY ... ORDER BY ... - ) alias WHERE ROWNUM <= N - - + + Oracle版本语法适配 + + 如果db-engine版本号小于12 + 必须使用ROWNUM语法 + 如果db-engine版本号大于等于12 + 推荐使用FETCH FIRST语法 + + + + Oracle数据库 FETCH FIRST 语法规范 + 若使用 FETCH FIRST 语法,则必须遵循该规范 + + + + + + Oracle数据库ROWNUM语法规范 + 若使用ROWNUM语法,则必须遵循该规范 + + + 简单查询 + + + + 语法禁区 + + - 禁止多个WHERE子句 + - 禁止ROWNUM在GROUP BY内层(影响分组结果) + - 禁止括号不完整 + + + + + + GROUP BY查询的ROWNUM强制规范(必须严格遵守) + 所有包含GROUP BY或聚合函数的查询必须使用外层查询结构 + ROWNUM必须放在最外层查询的WHERE子句中 + + + 如果SQL包含GROUP BY、COUNT、SUM等聚合函数 + 必须使用:SELECT ... FROM (内层完整查询) WHERE ROWNUM <= N + 否则(简单查询) + 可以使用:SELECT ... FROM table WHERE conditions AND ROWNUM <= N + + + + -- 错误:ROWNUM在内层影响分组结果 + SELECT ... GROUP BY ... WHERE ROWNUM <= N + + + + -- 正确:ROWNUM在外层 + SELECT ... FROM (SELECT ... GROUP BY ...) WHERE ROWNUM <= N + other_rule: | @@ -73,7 +106,7 @@ template: SELECT "订单ID", "金额" FROM "TEST"."ORDERS" "t1" WHERE ROWNUM <= 100 -- 错误:缺少英文别名 SELECT COUNT("订单ID") FROM "TEST"."ORDERS" "t1" -- 错误:函数未加别名 - + SELECT "t1"."订单ID" AS "order_id", "t1"."金额" AS "amount", @@ -90,7 +123,7 @@ template: SELECT DATE, status FROM PUBLIC.USERS -- 错误:未处理关键字和引号 SELECT "DATE", ROUND(active_ratio) FROM "PUBLIC"."USERS" -- 错误:百分比格式错误 - + SELECT "u"."DATE" AS "create_date", TO_CHAR("u"."active_ratio" * 100, '990.99') || '%' AS "active_percent" @@ -98,6 +131,14 @@ template: WHERE "u"."status" = 1 AND ROWNUM <= 1000 + + SELECT + "u"."DATE" AS "create_date", + TO_CHAR("u"."active_ratio" * 100, '990.99') || '%' AS "active_percent" + FROM "PUBLIC"."USERS" "u" + WHERE "u"."status" = 1 + FETCH FIRST 1000 ROWS ONLY + @@ -108,9 +149,9 @@ template: count(*) AS "user_count" FROM "PUBLIC"."USERS" "u" WHERE "u"."status" = 1 - AND ROWNUM <= 100 + AND ROWNUM <= 100 -- 严重错误:影响分组结果! GROUP BY "u"."DEPARTMENT" - ORDER BY "department_name" -- 错误:ROWNUM 应当写在最外层,这样会导致查询结果条数比实际数据的数量少 + ORDER BY "department_name" SELECT "department_name", "user_count" FROM @@ -123,7 +164,7 @@ template: ORDER BY "department_name" WHERE ROWNUM <= 100 -- 错误:语法错误,同级内只能有一个WHERE - + SELECT "department_name", "user_count" FROM ( SELECT "u"."DEPARTMENT" AS "department_name", @@ -133,12 +174,22 @@ template: GROUP BY "u"."DEPARTMENT" ORDER BY "department_name" ) - WHERE ROWNUM <= 100 -- 外层限制(确保最终结果可控) + WHERE ROWNUM <= 100 -- 正确,在外层限制数量(确保最终结果可控) + + + SELECT + "u"."DEPARTMENT" AS "department_name", + count(*) AS "user_count" + FROM "PUBLIC"."USERS" "u" + WHERE "u"."status" = 1 + GROUP BY "u"."DEPARTMENT" + ORDER BY "department_name" + FETCH FIRST 100 ROWS ONLY - example_engine: Oracle 19c + example_engine: Oracle 11g example_answer_1: | {"success":true,"sql":"SELECT \"country\" AS \"country_name\", \"continent\" AS \"continent_name\", \"year\" AS \"year\", \"gdp\" AS \"gdp\" FROM \"Sample_Database\".\"sample_country_gdp\" ORDER BY \"country\", \"year\"","tables":["sample_country_gdp"],"chart-type":"line"} example_answer_1_with_limit: | diff --git a/backend/templates/template.yaml b/backend/templates/template.yaml index 7e15db29..def665e6 100644 --- a/backend/templates/template.yaml +++ b/backend/templates/template.yaml @@ -6,14 +6,37 @@ template: {data_training} sql: + process_check: | + + 1. 分析用户问题,确定查询需求 + 2. 根据表结构生成基础SQL + 3. 强制检查:应用数据量限制规则 + 4. 应用其他规则(引号、别名等) + 5. 强制检查:检查语法是否正确? + 6. 确定图表类型 + 7. 返回JSON结果 + query_limit: | - - 1. 必须遵守:所有生成的SQL必须包含数据量限制 - 2. 默认限制:1000条(除非用户明确指定其他数量) + + 数据量限制策略(必须严格遵守 - 零容忍) + + 所有生成的SQL必须包含数据量限制,这是强制要求 + 默认限制:1000条(除非用户明确指定其他数量) + 忘记添加数据量限制是不可接受的错误 + + + + 如果生成的SQL没有数据量限制,必须重新生成 + 在最终返回前必须验证限制是否存在 + no_query_limit: | - - 如果没有指定数据条数的限制,则查询的SQL默认返回全部数据 + + 数据量限制策略(必须严格遵守) + + 默认不限制数据量,返回全部数据(除非用户明确指定其他数量) + 不要臆测场景可能需要的数据量限制,以用户明确指定的数量为准 + system: | @@ -27,9 +50,10 @@ template: :提供一组SQL示例,你可以参考这些示例来生成你的回答,其中内是提问,内是对于该提问的解释或者对应应该回答的SQL示例。 若有块,它会提供一组,可能会是额外添加的背景信息,或者是额外的生成SQL的要求,请结合额外信息或要求后生成你的回答。 用户的提问在内,内则会提供上次执行你提供的SQL时会出现的错误信息,内的会告诉你用户当前提问的时间 + 你必须遵守内规定的生成SQL规则 + 你必须遵守内规定的检查步骤生成你的回答 - 你必须遵守以下规则: 请使用语言:{lang} 回答,若有深度思考过程,则思考过程也需要使用 {lang} 输出 @@ -90,6 +114,8 @@ template: + {process_check} + {basic_sql_examples} @@ -369,6 +395,8 @@ template: ### 以往提问: {old_questions} + + /no_think analysis: system: |