Tencent / APIJSON

🏆 实时 零代码、全功能、强安全 ORM 库 🚀 后端接口和文档零代码,前端(客户端) 定制返回 JSON 的数据和结构 🏆 Real-Time coding-free, powerful and secure ORM 🚀 providing APIs and Docs without coding by Backend, and the returned JSON of API can be customized by Frontend(Client) users
http://apijson.cn
Other
17.27k stars 2.16k forks source link

[Bug] sqlserver查询聚合函数出错 #648

Open fengdu126 opened 11 months ago

fengdu126 commented 11 months ago

APIJSON Version/APIJSON 版本号

6.2

Database Type & Version/数据库类型及版本号

sqlserver2012

Environment/环境信息

- JDK/基础库:1.8
- OS/系统:win11

APIAuto Screenshots/APIAuto 请求与结果完整截屏

https://github.com/fengdu126/appokgo/blob/master/c80f2d8b563540fc110e9232b1f3b5d.png

Current Behavior/问题描述

 {
        "Moment": {
             "@column":"max(date):date"
        }
}

返回:{"Moment":{"@column":"max(date):date"},"ok":false,"code":500,"msg":"数据库驱动执行异常SQLException,非 Log.DEBUG 模式下不显示详情,避免泄漏真实模式名、表名等隐私信息"}

log提示:order by 子句 列id无效,因为该列没有包含在聚合函数或group by子句中,

但查max(id)是没有问题的。

Expected Behavior/期望结果

另外希望一条语句能查多个聚合函数,比如: {
        "Moment": {
             "@column":"max(date):date,max(id):id"
        }
}

Any additional comments?/其它补充说明?

No response

TommyLemon commented 11 months ago

Oracle, SQL Server, DB2 的分页 OFFSET 必须加 ORDER BY,默认是用 id,如果没有需要重写 getIdKey 返回对应主键名,或者前端传参 "@order":"key" // key / key+ / key-

https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L1612-L1623

image
fengdu126 commented 11 months ago

Oracle, SQL Server, DB2 的分页 OFFSET 必须加 ORDER BY,默认是用 id,如果没有需要重写 getIdKey 返回对应主键名,或者前端传参 "@order":"key" // key / key+ / key-

https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L1612-L1623 image

这个应该不是offset的主键id的问题,就是查询例子里面的Moment表,本来就是有主键id的。

"Moment": { "@column":"max(date):date" }

意思是查询最大日期。 这条json对应的sqlserver语句应该是: select max(date) as date from Moment

照理不会牵涉到order by id 的问题,怎么后台会出现:order by 子句 列id无效,因为该列没有包含在聚合函数或group by子句中 这句报错?

fengdu126 commented 11 months ago

大概知道什么原因了,是限制行数导致查询出错。

建议能否完善下,如果包含聚合函数的查询,但又没有group的时候虽然是查全表但实际上永远只会回一行,这种情况就不要在后面加上 ORDER BY "id" OFFSET 0 ROWS FETCH FIRST 10 ROWS ONLY 来限制了。

TommyLemon commented 11 months ago

Oracle, SQL Server, DB2 的分页 OFFSET 必须加 ORDER BY,默认是用 id,如果没有需要重写 getIdKey 返回对应主键名,或者前端传参 "@order":"key" // key / key+ / key-

https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L1612-L1623 image

那就需要调整下这里的逻辑,判断 RequestMethod.isGetMethod(method, true) 时,才拼接 FETCH 语句

fengdu126 commented 11 months ago

Oracle, SQL Server, DB2 的分页 OFFSET 必须加 ORDER BY,默认是用 id,如果没有需要重写 getIdKey 返回对应主键名,或者前端传参 "@order":"key" // key / key+ / key- https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L1612-L1623 image

那就需要调整下这里的逻辑,判断 RequestMethod.isGetMethod(method, true) 时,才拼接 FETCH 语句 不知道怎么改,大佬能否修复下这个bug

TommyLemon commented 11 months ago

外层 if 加个 method 的判断就行,我暂时还没时间处理 https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L1612-L1623

image

如果你已经知道问题所在、怎么解决,请直接 提交 Pull Request 为社区做贡献,非常感谢。 开发者也是人,也需要工作、休息、恋爱、陪伴家人、走亲会友等,也有心情不好和身体病痛, 往往没有额外的时间精力顾及一些小问题,请理解和支持,开源要大家参与贡献才会更美好~ 少数个人的热情终有被耗尽的一天,只有大家共同建设和繁荣社区,才能让开源可持续发展! https://github.com/Tencent/APIJSON/issues/406 image https://github.com/Tencent/APIJSON/blob/master/CONTRIBUTING.md#%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%80%E5%AE%9A%E8%A6%81%E8%B4%A1%E7%8C%AE%E4%BB%A3%E7%A0%81

fengdu126 commented 11 months ago

OFFSET 0 ROWS FETCH FIRST 10 ROWS ONLY

不是这里改order by的问题,而是要去掉 OFFSET 0 ROWS FETCH FIRST 10 ROWS ONLY才行,因为 select max(date) as date from Moment 当没有group by的时候永远只会返回一行,这种情况不能再拼接limitstring了。

TommyLemon commented 10 months ago

加了 GROUP BY 就很可能不只一行了,在没法自动判断的前提下只能统一加上。

https://github.com/Tencent/APIJSON/blob/master/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L2576-L2591

image

如果想不拼接 FETCH 语句,可以前端在 [] 内传参 count: 1,并且后端改下 getLimitString 代码,

boolean isTSQL = isOracle() || isSQLServer() || isDb2();
if (isTSQL && count == 1) {
  return "";
}

"@column":"max(date):date,max(id):id" 写法不符合 APIJSON 语法,必须用 ; 分号分隔 SQL 函数。 https://github.com/Tencent/APIJSON/blob/master/Document.md

image image

http://apijson.cn/api/?send=true&type=JSON&url=http%3A%2F%2Fapijson.cn%3A8080%2Fget&json={%22User%22:{%22@column%22:%22max(date)%3Adate%3Bmax(id)%3Aid%22}}

image

http://apijson.cn/api/?send=true&type=JSON&url=http%3A%2F%2Fapijson.cn%3A8080%2Fget&json={%22[]%22:{%22Comment%22:{%22@column%22:%22max(date)%3Adate%3Bmax(id)%3Aid%22,%22@order%22:%22date-%2Cid%2B%22}},%22@explain%22:true}

fengdu126 commented 10 months ago

通过判断[]里面的count是否等于某个特定数字虽然是可以解决,但最好还是能不加[]和count,能自动判断是否需要加LimitString

TommyLemon commented 10 months ago

确实这样更好,应该可以像 Oracle 一样通过 ROW_NUM 替代 FETCH 来解决,相关判断 isOracle 的地方改成 (isOracle || isSQLServer)

TommyLemon commented 10 months ago

不需要分页的地方,也可以改用拼接 TOP 语句来简单解决

fengdu126 commented 10 months ago

不需要分页的地方,也可以改用拼接 TOP 语句来简单解决

这个可以有,一些场合是不需要分页的,能否跟@column一样加上@top关键词,表示只select前面n条记录,有了top就不用强制加LimitString和order by了

TommyLemon commented 10 months ago

不需要加 @top 关键词,只要前端在 [] 内传参 count 并且 page 不是正数,或者只是查单个对象(count = 1),都直接对应转成 getSQL 方法内的 SELECT TOP $count,至于 getLimitString 就直接返回空字符串。

fengdu126 commented 10 months ago

不需要加 @top 关键词,只要前端在 [] 内传参 count 并且 page 不是正数,或者只是查单个对象(count = 1),都直接对应转成 getSQL 方法内的 SELECT TOP $count,至于 getLimitString 就直接返回空字符串。

试了不行,count和page的值都必须大于等于0,提示page值不合法,必须是0-100

TommyLemon commented 10 months ago

怎么会?只有 page == 0 时采用 TOP