Skip to content

Commit 054bbae

Browse files
committed
fix(chat): constrain SQL row-limit placement to outermost query in prompt templates (#1202)
The row-limit policy in query_limit/no_query_limit did not specify WHERE the limit should be applied, so LLMs could put LIMIT/ROWNUM/TOP inside subqueries, truncating the data feeding aggregation (limiting accessed rows instead of the final result set). - query_limit/no_query_limit: limit must be applied only to the outermost query; never inside subqueries, derived tables or CTEs; add conditional per-group top-N note (window functions where supported) - process_check (template.yaml + Oracle override): step 4 now verifies limit placement - sql_examples: add nested-aggregation bad/good examples for all 11 engines showing the limit on the outer query only - Oracle: fix invalid GROUP BY...WHERE ROWNUM example and wrong ROWNUM-before-ORDER BY few-shot answers (wrap with outer query)
1 parent 21961e2 commit 054bbae

13 files changed

Lines changed: 263 additions & 12 deletions

‎backend/templates/sql_examples/AWS_Redshift.yaml‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,27 @@ template:
7171
FETCH FIRST 1000 ROWS ONLY
7272
</output-good>
7373
</example>
74+
75+
<example>
76+
<input>统计订单表 TEST.ORDERS 中2024年每个客户的消费总金额,返回金额最高的前100名客户</input>
77+
<output-bad>
78+
SELECT "t"."customer_id" AS "customer_id", SUM("t"."amount") AS "total_amount"
79+
FROM (SELECT "customer_id", "amount" FROM "TEST"."ORDERS" WHERE "create_time" >= '2024-01-01' LIMIT 100) "t"
80+
GROUP BY "t"."customer_id"
81+
-- 严重错误:LIMIT加在了内层子查询中,聚合只基于被截断的100行数据计算,结果错误!
82+
</output-bad>
83+
<output-good>
84+
SELECT
85+
"o"."customer_id" AS "customer_id",
86+
SUM("o"."amount") AS "total_amount"
87+
FROM "TEST"."ORDERS" "o"
88+
WHERE "o"."create_time" >= '2024-01-01'
89+
GROUP BY "o"."customer_id"
90+
ORDER BY "total_amount" DESC
91+
LIMIT 100
92+
-- 正确:聚合基于完整数据计算,LIMIT只限制最终返回结果的行数,且只出现在最外层查询
93+
</output-good>
94+
</example>
7495
</basic-examples>
7596
7697
example_engine: AWS Redshift 1.0

‎backend/templates/sql_examples/ClickHouse.yaml‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,27 @@ template:
7474
LIMIT 5
7575
</output-good>
7676
</example>
77+
78+
<example>
79+
<input>统计订单表 default.orders 中2024年每个客户的消费总金额,返回金额最高的前100名客户</input>
80+
<output-bad>
81+
SELECT "t"."customer_id" AS "customer_id", SUM("t"."amount") AS "total_amount"
82+
FROM (SELECT "customer_id", "amount" FROM "default"."orders" WHERE "create_time" >= '2024-01-01' LIMIT 100) "t"
83+
GROUP BY "t"."customer_id"
84+
-- 严重错误:LIMIT加在了内层子查询中,聚合只基于被截断的100行数据计算,结果错误!
85+
</output-bad>
86+
<output-good>
87+
SELECT
88+
"o"."customer_id" AS "customer_id",
89+
SUM("o"."amount") AS "total_amount"
90+
FROM "default"."orders" "o"
91+
WHERE "o"."create_time" >= '2024-01-01'
92+
GROUP BY "o"."customer_id"
93+
ORDER BY "total_amount" DESC
94+
LIMIT 100
95+
-- 正确:聚合基于完整数据计算,LIMIT只限制最终返回结果的行数,且只出现在最外层查询
96+
</output-good>
97+
</example>
7798
</basic-examples>
7899
79100
example_engine: ClickHouse 23.3

‎backend/templates/sql_examples/DM.yaml‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,27 @@ template:
7272
LIMIT 0, 1000 -- 达梦兼容写法
7373
</output-good>
7474
</example>
75+
76+
<example>
77+
<input>统计订单表 TEST.ORDERS 中2024年每个客户的消费总金额,返回金额最高的前100名客户</input>
78+
<output-bad>
79+
SELECT "t"."customer_id" AS "customer_id", SUM("t"."amount") AS "total_amount"
80+
FROM (SELECT "customer_id", "amount" FROM "TEST"."ORDERS" WHERE "create_time" >= '2024-01-01' LIMIT 100) "t"
81+
GROUP BY "t"."customer_id"
82+
-- 严重错误:LIMIT加在了内层子查询中,聚合只基于被截断的100行数据计算,结果错误!
83+
</output-bad>
84+
<output-good>
85+
SELECT
86+
"o"."customer_id" AS "customer_id",
87+
SUM("o"."amount") AS "total_amount"
88+
FROM "TEST"."ORDERS" "o"
89+
WHERE "o"."create_time" >= '2024-01-01'
90+
GROUP BY "o"."customer_id"
91+
ORDER BY "total_amount" DESC
92+
LIMIT 100
93+
-- 正确:聚合基于完整数据计算,LIMIT只限制最终返回结果的行数,且只出现在最外层查询
94+
</output-good>
95+
</example>
7596
</basic-examples>
7697
7798
example_engine: DM Database 8

‎backend/templates/sql_examples/Doris.yaml‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,27 @@ template:
7474
LIMIT 1000
7575
</output-good>
7676
</example>
77+
78+
<example>
79+
<input>统计订单表 test.orders 中2024年每个客户的消费总金额,返回金额最高的前100名客户</input>
80+
<output-bad>
81+
SELECT `t`.`customer_id` AS `customer_id`, SUM(`t`.`amount`) AS `total_amount`
82+
FROM (SELECT `customer_id`, `amount` FROM `test`.`orders` WHERE `create_time` >= '2024-01-01' LIMIT 100) `t`
83+
GROUP BY `t`.`customer_id`
84+
-- 严重错误:LIMIT加在了内层子查询中,聚合只基于被截断的100行数据计算,结果错误!
85+
</output-bad>
86+
<output-good>
87+
SELECT
88+
`o`.`customer_id` AS `customer_id`,
89+
SUM(`o`.`amount`) AS `total_amount`
90+
FROM `test`.`orders` `o`
91+
WHERE `o`.`create_time` >= '2024-01-01'
92+
GROUP BY `o`.`customer_id`
93+
ORDER BY `total_amount` DESC
94+
LIMIT 100
95+
-- 正确:聚合基于完整数据计算,LIMIT只限制最终返回结果的行数,且只出现在最外层查询
96+
</output-good>
97+
</example>
7798
</basic-examples>
7899
79100
example_engine: Apache Doris 2.0

‎backend/templates/sql_examples/Elasticsearch.yaml‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,27 @@ template:
8888
LIMIT 5
8989
</output-good>
9090
</example>
91+
92+
<example>
93+
<input>统计 logs-* 索引中最近一天各个客户端IP的日志数量,返回数量最多的前100个IP</input>
94+
<output-bad>
95+
SELECT "t"."host.ip" AS "client_ip", COUNT("t"."message") AS "log_count"
96+
FROM (SELECT "host.ip", "message" FROM "logs-*" LIMIT 100) "t"
97+
GROUP BY "t"."host.ip"
98+
-- 严重错误:内层LIMIT导致聚合只基于被截断的100行数据计算,结果错误!Elasticsearch SQL也不支持FROM子查询
99+
</output-bad>
100+
<output-good>
101+
SELECT
102+
"l"."host.ip" AS "client_ip",
103+
COUNT("l"."message") AS "log_count"
104+
FROM "logs-*" "l"
105+
WHERE CAST("l"."@timestamp" AS TIMESTAMP) > DATE_SUB(NOW(), INTERVAL 1 DAY)
106+
GROUP BY "l"."host.ip"
107+
ORDER BY "log_count" DESC
108+
LIMIT 100
109+
-- 正确:聚合基于完整数据计算,LIMIT只限制最终返回结果的行数,且只出现在整个查询的最外层
110+
</output-good>
111+
</example>
91112
</basic-examples>
92113
93114
example_engine: Elasticsearch SQL 8.9

‎backend/templates/sql_examples/Hive.yaml‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,27 @@ template:
7171
WHERE `u`.`status` = 1
7272
</output-good>
7373
</example>
74+
75+
<example>
76+
<input>统计订单表 ods.orders 中2024年每个客户的消费总金额,返回金额最高的前100名客户</input>
77+
<output-bad>
78+
SELECT `t`.`customer_id` AS `customer_id`, SUM(`t`.`amount`) AS `total_amount`
79+
FROM (SELECT `customer_id`, `amount` FROM `ods`.`orders` WHERE YEAR(`create_time`) = 2024 LIMIT 100) `t`
80+
GROUP BY `t`.`customer_id`
81+
-- 严重错误:LIMIT加在了内层子查询中,聚合只基于被截断的100行数据计算,结果错误!
82+
</output-bad>
83+
<output-good>
84+
SELECT
85+
`o`.`customer_id` AS `customer_id`,
86+
SUM(`o`.`amount`) AS `total_amount`
87+
FROM `ods`.`orders` `o`
88+
WHERE YEAR(`o`.`create_time`) = 2024
89+
GROUP BY `o`.`customer_id`
90+
ORDER BY `total_amount` DESC
91+
LIMIT 100
92+
-- 正确:聚合基于完整数据计算,LIMIT只限制最终返回结果的行数,且只出现在最外层查询
93+
</output-good>
94+
</example>
7495
</basic-examples>
7596
7697
example_engine: Apache Hive 2.X

‎backend/templates/sql_examples/Kingbase.yaml‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,27 @@ template:
6767
WHERE "u"."status" = 1
6868
</output-good>
6969
</example>
70+
71+
<example>
72+
<input>统计订单表 TEST.ORDERS 中2024年每个客户的消费总金额,返回金额最高的前100名客户</input>
73+
<output-bad>
74+
SELECT "t"."customer_id" AS "customer_id", SUM("t"."amount") AS "total_amount"
75+
FROM (SELECT "customer_id", "amount" FROM "TEST"."ORDERS" WHERE "create_time" >= '2024-01-01' LIMIT 100) "t"
76+
GROUP BY "t"."customer_id"
77+
-- 严重错误:LIMIT加在了内层子查询中,聚合只基于被截断的100行数据计算,结果错误!
78+
</output-bad>
79+
<output-good>
80+
SELECT
81+
"o"."customer_id" AS "customer_id",
82+
SUM("o"."amount") AS "total_amount"
83+
FROM "TEST"."ORDERS" "o"
84+
WHERE "o"."create_time" >= '2024-01-01'
85+
GROUP BY "o"."customer_id"
86+
ORDER BY "total_amount" DESC
87+
LIMIT 100
88+
-- 正确:聚合基于完整数据计算,LIMIT只限制最终返回结果的行数,且只出现在最外层查询
89+
</output-good>
90+
</example>
7091
</basic-examples>
7192
7293
example_engine: Kingbase V8

‎backend/templates/sql_examples/Microsoft_SQL_Server.yaml‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,26 @@ template:
7272
OFFSET 0 ROWS FETCH NEXT 1000 ROWS ONLY
7373
</output-good>
7474
</example>
75+
76+
<example>
77+
<input>统计订单表 Sales.Orders 中2024年每个客户的消费总金额,返回金额最高的前100名客户</input>
78+
<output-bad>
79+
SELECT [t].[customer_id] AS [customer_id], SUM([t].[amount]) AS [total_amount]
80+
FROM (SELECT TOP 100 [customer_id], [amount] FROM [Sales].[Orders] WHERE [create_time] >= '2024-01-01') [t]
81+
GROUP BY [t].[customer_id]
82+
-- 严重错误:TOP加在了内层子查询中,聚合只基于被截断的100行数据计算,结果错误!
83+
</output-bad>
84+
<output-good>
85+
SELECT TOP 100
86+
[o].[customer_id] AS [customer_id],
87+
SUM([o].[amount]) AS [total_amount]
88+
FROM [Sales].[Orders] [o]
89+
WHERE [o].[create_time] >= '2024-01-01'
90+
GROUP BY [o].[customer_id]
91+
ORDER BY [total_amount] DESC
92+
-- 正确:TOP只加在最外层查询上,聚合基于完整数据计算,只限制最终返回结果的行数
93+
</output-good>
94+
</example>
7595
</basic-examples>
7696
7797
example_engine: Microsoft SQL Server 2022

‎backend/templates/sql_examples/MySQL.yaml‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,35 @@ template:
6363
SELECT `desc`, ROUND(active_ratio*100) FROM `public`.`users` -- 错误:百分比格式错误
6464
</output-bad>
6565
<output-good>
66-
SELECT
66+
SELECT
6767
`u`.`desc` AS `description`,
6868
CONCAT(ROUND(`u`.`active_ratio` * 100, 2), '%') AS `active_percent`
6969
FROM `public`.`users` `u`
7070
WHERE `u`.`status` = 1
7171
LIMIT 1000
7272
</output-good>
7373
</example>
74+
75+
<example>
76+
<input>统计订单表 test.orders 中2024年每个客户的消费总金额,返回金额最高的前100名客户</input>
77+
<output-bad>
78+
SELECT `t`.`customer_id` AS `customer_id`, SUM(`t`.`amount`) AS `total_amount`
79+
FROM (SELECT `customer_id`, `amount` FROM `test`.`orders` WHERE `create_time` >= '2024-01-01' LIMIT 100) `t`
80+
GROUP BY `t`.`customer_id`
81+
-- 严重错误:LIMIT加在了内层子查询中,聚合只基于被截断的100行数据计算,结果错误!
82+
</output-bad>
83+
<output-good>
84+
SELECT
85+
`o`.`customer_id` AS `customer_id`,
86+
SUM(`o`.`amount`) AS `total_amount`
87+
FROM `test`.`orders` `o`
88+
WHERE `o`.`create_time` >= '2024-01-01'
89+
GROUP BY `o`.`customer_id`
90+
ORDER BY `total_amount` DESC
91+
LIMIT 100
92+
-- 正确:聚合基于完整数据计算,LIMIT只限制最终返回结果的行数,且只出现在最外层查询
93+
</output-good>
94+
</example>
7495
</basic-examples>
7596
7697
example_engine: MySQL 8.0

‎backend/templates/sql_examples/Oracle.yaml‎

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ template:
44
<step>1. 分析用户问题,确定查询需求</step>
55
<step>2. 根据表结构生成基础SQL</step>
66
<step>3. <strong>强制检查:逐字验证SQL中使用的表名和字段名是否在<m-schema>中定义,且字符必须完全匹配(包括繁简体、大小写、特殊字符),不得有任何转换。如发现任何不一致,必须重新生成SQL。</strong></step>
7-
<step>4. <strong>强制检查:应用数据量限制规则(默认限制或用户指定数量)</strong></step>
7+
<step>4. <strong>强制检查:应用数据量限制规则(默认限制或用户指定数量),限制必须位于最外层查询,不得出现在子查询、派生表、CTE内部</strong></step>
88
<step>5. 应用其他规则(引号、别名、格式化等)</step>
99
<step>6. <strong>最终验证:GROUP BY查询的ROWNUM位置是否正确?</strong></step>
1010
<step>7. <strong>强制检查:验证SQL语法是否符合<db-engine>规范</strong></step>
@@ -110,14 +110,26 @@ template:
110110
SELECT COUNT("订单ID") FROM "TEST"."ORDERS" "t1" -- 错误:函数未加别名
111111
</output-bad>
112112
<output-good version="12c-below">
113-
SELECT
113+
SELECT "order_id", "amount", "total_orders", "discount_percent" FROM (
114+
SELECT
115+
"t1"."订单ID" AS "order_id",
116+
"t1"."金额" AS "amount",
117+
COUNT("t1"."订单ID") AS "total_orders",
118+
TO_CHAR("t1"."折扣率" * 100, '990.99') || '%' AS "discount_percent"
119+
FROM "TEST"."ORDERS" "t1"
120+
GROUP BY "t1"."订单ID", "t1"."金额", "t1"."折扣率"
121+
)
122+
WHERE ROWNUM <= 100
123+
</output-good>
124+
<output-good version="12c+">
125+
SELECT
114126
"t1"."订单ID" AS "order_id",
115127
"t1"."金额" AS "amount",
116128
COUNT("t1"."订单ID") AS "total_orders",
117129
TO_CHAR("t1"."折扣率" * 100, '990.99') || '%' AS "discount_percent"
118130
FROM "TEST"."ORDERS" "t1"
119131
GROUP BY "t1"."订单ID", "t1"."金额", "t1"."折扣率"
120-
WHERE ROWNUM <= 100
132+
FETCH FIRST 100 ROWS ONLY
121133
</output-good>
122134
</example>
123135
@@ -197,11 +209,11 @@ template:
197209
example_answer_1: |
198210
{"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"}
199211
example_answer_1_with_limit: |
200-
{"success":true,"sql":"SELECT \"country\" AS \"country_name\", \"continent\" AS \"continent_name\", \"year\" AS \"year\", \"gdp\" AS \"gdp\" FROM \"Sample_Database\".\"sample_country_gdp\" WHERE ROWNUM <= 1000 ORDER BY \"country\", \"year\"","tables":["sample_country_gdp"],"chart-type":"line"}
212+
{"success":true,"sql":"SELECT \"country_name\", \"continent_name\", \"year\", \"gdp\" FROM (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\") WHERE ROWNUM <= 1000","tables":["sample_country_gdp"],"chart-type":"line"}
201213
example_answer_2: |
202214
{"success":true,"sql":"SELECT \"country\" AS \"country_name\", \"gdp\" AS \"gdp\" FROM \"Sample_Database\".\"sample_country_gdp\" WHERE \"year\" = '2024' ORDER BY \"gdp\" DESC","tables":["sample_country_gdp"],"chart-type":"pie"}
203215
example_answer_2_with_limit: |
204-
{"success":true,"sql":"SELECT \"country\" AS \"country_name\", \"gdp\" AS \"gdp\" FROM \"Sample_Database\".\"sample_country_gdp\" WHERE \"year\" = '2024' AND ROWNUM <= 1000 ORDER BY \"gdp\" DESC","tables":["sample_country_gdp"],"chart-type":"pie"}
216+
{"success":true,"sql":"SELECT \"country_name\", \"gdp\" FROM (SELECT \"country\" AS \"country_name\", \"gdp\" AS \"gdp\" FROM \"Sample_Database\".\"sample_country_gdp\" WHERE \"year\" = '2024' ORDER BY \"gdp\" DESC) WHERE ROWNUM <= 1000","tables":["sample_country_gdp"],"chart-type":"pie"}
205217
example_answer_3: |
206218
{"success":true,"sql":"SELECT \"country\" AS \"country_name\", \"gdp\" AS \"gdp\" FROM \"Sample_Database\".\"sample_country_gdp\" WHERE \"year\" = '2025' AND \"country\" = '中国'","tables":["sample_country_gdp"],"chart-type":"table"}
207219
example_answer_3_with_limit: |

0 commit comments

Comments
 (0)