【发布时间】:2019-02-22 15:11:58
【问题描述】:
我有以下 SQL 查询,我将其翻译为 HQL:
SELECT f.date,
f.name,
SUM(f.seats)
FROM Foo f
WHERE EXISTS ( SELECT 1
FROM Foo fh
WHERE f.start + f.end IN ( SELECT fl.start + fl.end
FROM Foo fl
WHERE fl.date BETWEEN dateadd(yy,-1,fh.date)
AND fh.date
AND fl.name = '<name>')
AND f.date = fh.date
AND fh.date >= '2016-01-01'
AND fh.name = '<name>' )
AND f.date >= '2016-01-01'
GROUP BY f.date,
f.name
ORDER BY f.date ASC,
SUM(f.seats) DESC
在我的应用程序中,这个查询导致标题中的错误:
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Could not allocate a new page for database 'TEMPDB' because of insufficient disk space in filegroup 'DEFAULT'. Create the necessary space by dropping objects in the filegroup, adding additional files to the filegroup, or setting autogrowth on for existing files in the filegroup.
at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(SQLServerException.java:216)
at com.microsoft.sqlserver.jdbc.SQLServerResultSet$FetchBuffer.nextRow(SQLServerResultSet.java:4853)
at com.microsoft.sqlserver.jdbc.SQLServerResultSet.fetchBufferNext(SQLServerResultSet.java:1781)
at com.microsoft.sqlserver.jdbc.SQLServerResultSet.next(SQLServerResultSet.java:1034)
at org.apache.commons.dbcp2.DelegatingResultSet.next(DelegatingResultSet.java:191)
at org.apache.commons.dbcp2.DelegatingResultSet.next(DelegatingResultSet.java:191)
at org.hibernate.loader.Loader.processResultSet(Loader.java:986)
at org.hibernate.loader.Loader.doQuery(Loader.java:948)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:340)
at org.hibernate.loader.Loader.doList(Loader.java:2689)
这显然是由于查询的效率低下以及执行的次数以及处理的行数造成的。
让我们解释一下查询的作用。下面的例子:
我有关于优步司机的数据。每行是司机的一个驱动器,包含日期(月份)、司机姓名、司机可用的座位、开始位置和结束位置。
E. g.:
Date Name Seats Start End
-------------------------------------------
7/1/2019 John 45 A B
数据按月汇总。所以John 在A 和B 之间有9 个驱动器,每次他有5 个座位可用。当然,也有其他人驾驶同一条路线,因此与John竞争。
Date Name Seats Start End
-------------------------------------------
7/1/2019 John 45 A B
7/1/2019 Doe 25 A A
7/1/2019 Alice 35 A C
7/1/2019 John 30 A A
7/1/2019 Doe 25 A C
7/1/2019 Alice 10 A B
7/1/2019 Doe 5 A B
7/1/2019 Alice 15 A A
所以对于7/1/2019Johns“网络”(所有路线)进行了这场比赛:
Date Name Seats Route
---------------------------------
7/1/2019 John 30 A-A
7/1/2019 Doe 25 A-A
7/1/2019 Alice 15 A-A
7/1/2019 John 45 A-B
7/1/2019 Doe 5 A-B
7/1/2019 Alice 10 A-B
如您所见,在此结果中,路线A-C 没有列出,因为John 根本没有驾驶它。如果我们将示例数据扩展一个新的月份8/1/2019:
Date Name Seats Start End
-------------------------------------------
8/1/2019 John 65 A C
8/1/2019 Doe 25 A A
8/1/2019 Alice 35 A A
8/1/2019 Doe 25 A B
8/1/2019 Alice 10 A B
8/1/2019 Doe 5 A C
8/1/2019 Alice 15 A C
我们可以看到John 这个月只开车A-C。由于network 应该在过去 1 年的时间跨度内(2018 年 8 月 1 日至 2019 年 8 月 1 日)构建,Johns 网络现在是所有三个路由(A-A、A-B、 A-C),但仅用于计算截至 8/1/2019 的竞争对手。对于7/1/2019、Johns 网络保持A-A、A-B。所以8/1/2019 的结果是这样的:
Date Name Seats Route
---------------------------------
8/1/2019 John 0 A-A
8/1/2019 Doe 25 A-A
8/1/2019 Alice 35 A-A
8/1/2019 John 0 A-B
8/1/2019 Doe 25 A-B
8/1/2019 Alice 10 A-B
8/1/2019 John 65 A-C
8/1/2019 Doe 5 A-C
8/1/2019 Alice 10 A-C
John 只开过A-C,这就是为什么他在其他路线上被计为 0 座的原因。
由于结果是对座位求和而忽略了路线,所以查询的实际输出如下:
7/1/2019 John 75 <-- 30+45
7/1/2019 Doe 30 <-- 25+5
7/1/2019 Alice 25 <-- 10+15
8/1/2019 John 65 <-- 65+0+0
8/1/2019 Doe 55 <-- 25+25+5
8/1/2019 Alice 55 <-- 35+10+10
在此结果中,我们仅将 A-A 和 A-B 作为 7/1/2019 的路线作为 Johns 竞争对手的路线,因为在该日期之前没有数据。对于8/1/2019Johns 网络是A-A、A-B 和A-C,尽管他只在8/1/2019 中驾驶A-C(A-A 和A-B 在@98765436767 中)。
我希望我提供的数据是可以理解的。如果您需要更多说明,请询问,我会尽力解释更多。
我需要如何更改查询以显着提高性能?
到目前为止,我还没有使用过JOINs,因为我必须加入子查询,而这在 HQL 中是不允许的。
如果您需要更多信息/说明,请随时提问!
编辑:
我知道我也可以在codereview.stackexchange.com 上发帖,但我选择反对它,因为查询本身有效,如果只针对 1 个名称执行并且只针对更多名称失败。我对codereview.stackexchange.com的理解是,应该只有性能提升问题
【问题讨论】:
-
我建议您修改示例,以便座位加起来,以免混淆这个问题的读者
-
您是否直接在某些 SQL 客户端中尝试了生成的 SQL 查询并对其进行了解释?如果它会返回大量可以解释内存问题的数据,但如果不是,数据库仍然可以创建一个巨大的临时表(该错误似乎表明了这一点)。所以我会先尝试分析和修复SQL,然后尝试调整HQL查询,让Hibernate生成我想要的SQL。
-
...事实上,我确信使用窗口函数解决这个问题可能非常“容易”,但是我不想修复您现有的查询,而是希望通过精心设计的示例并从那里开始。很确定你可以忘记 HQL(正如我在上一个问题中已经建议的那样):)
-
@Thomas 如果我只为 1 个名称运行该查询,它返回的行数少于 3k。所以这不是结果数量的问题,而是如果多次运行查询如何被数据库处理(创建巨大的临时表)。我确实发布了 SQL 查询而不是 HQL 查询,因为它比 HQL 更容易修复 SQL
-
@LukasEder 我已经更新了示例,它们现在应该加起来了。正如您在上一个问题中向我解释的那样,我认为我能够从查询中删除所有 carthesian 产品。我怀疑
start + end的计算列能否解决手头的内存问题。如果您需要更多信息或不清楚的地方,请随时提出,我会尽力提供可以理解的解释
标签: java sql sql-server hql