【发布时间】:2018-03-02 22:30:10
【问题描述】:
使用 EF6、ODP.NET 12.2 和 Oracle 12.2 服务器,我有下表:
id raw(16)
number number(19)
name varchar2(100 char) not null
description varchar2(100 char) not null
(名称)和(名称、描述)的索引
以下 LINQ 查询:
db.SAMPLES.OrderBy(p => p.NAME).ThenBy(p => p.DESCRIPTION).Skip(0).Take(10).ToList();
产生这个 SQL:
SELECT *
FROM (
SELECT
"Extent1"."ID" AS "ID",
"Extent1"."NUMBER" AS "NUMBER",
"Extent1"."NAME" AS "NAME"
FROM ( SELECT "Extent1"."ID" AS "ID", "Extent1"."NUMBER" AS "NUMBER", "Extent1"."NAME" AS "NAME", row_number() OVER (ORDER BY "Extent1"."NAME" ASC) AS "row_number"
FROM "ZENKI"."SAMPLEs" "Extent1"
) "Extent1"
WHERE ("Extent1"."row_number" > 0)
ORDER BY "Extent1"."NAME" ASC
)
WHERE (ROWNUM <= (10) )
查询运行良好,但性能不佳。显然,由于 (ORDER BY "Extent1"."NAME" ASC) 的(额外??)顺序,Oracle 没有使用索引。
查询的执行计划是:
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10 | 2250 | 2 | 00:00:01 |
| * 1 | COUNT STOPKEY | | | | | |
| 2 | VIEW | | 10 | 2250 | 2 | 00:00:01 |
| * 3 | SORT ORDER BY STOPKEY | | 10 | 2380 | 2 | 00:00:01 |
| * 4 | VIEW | | 10 | 2380 | 1 | 00:00:01 |
| 5 | WINDOW NOSORT | | 10 | 1300 | 1 | 00:00:01 |
| 6 | TABLE ACCESS BY INDEX ROWID | SAMPLEs | 245376 | 31898880 | 1 | 00:00:01 |
| 7 | INDEX FULL SCAN | INDEX_SP1 | 10 | | 1 | 00:00:01 |
----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
------------------------------------------
* 1 - filter(ROWNUM<=10)
* 3 - filter(ROWNUM<=10)
* 4 - filter("Extent1"."row_number">0)
如果我删除最后一个订单,查询会很快并产生相同的结果。而执行计划变成:
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10 | 2380 | 1 | 00:00:01 |
| * 1 | COUNT STOPKEY | | | | | |
| * 2 | VIEW | | 10 | 2380 | 1 | 00:00:01 |
| 3 | WINDOW NOSORT | | 10 | 1300 | 1 | 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID | SAMPLEs | 245376 | 31898880 | 1 | 00:00:01 |
| 5 | INDEX FULL SCAN | INDEX_SP1 | 10 | | 1 | 00:00:01 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
------------------------------------------
* 1 - filter(ROWNUM<=10)
* 2 - filter("Extent1"."row_number">0)
经过大量谷歌搜索后,我在 ODP.NET 论坛中询问并得到了这个:
https://community.oracle.com/thread/4126265
似乎 odp.net 无法控制(或限制)生成的 SQL。我认为提供程序从表达式树生成 SQL,那么这怎么可能呢?有其他人对这种类型的查询有这个问题吗?
更新:
LINQ 论坛中的帖子:
更新 1:
使用 Number 数据类型而不是 varchar 作为 order by 会产生不同的解释计划并且非常快,所以我开始认为这与数据类型有任何关系。
【问题讨论】:
-
由于IQueryable没有AsUnordered,你可以尝试在take后添加.Distinct()。
-
相同的性能结果
-
正如您在 ODP.NET 论坛中看到的那样,这是使用 ORM 的痛点之一......潜在的低效 SQL。像这样的问题总是会不断出现,可能是由于 EF 错误或 ODP.NET 错误。 APPLY 关键字是 EF 历史上最严重的违规者。多年来,只有当表达式树指示需要“APPLY”时,SQL Server 才能工作。不仅是 Oracle,没有其他数据库可以处理这种情况。最后 MS 所需的数据库添加 APPLY 关键字或抛出错误。
-
@ChristianShay 我不能理解你的意思,真正困扰我的是这个查询似乎很常见。没有人尝试使用 EF 和 Oracle 使用分页查询?
-
这可能是 ODP.NET 中的错误,也可能不是。问题是,错误得到修复,然后表达式树在未来的 EF 版本中发生变化,并暴露出另一个不同的错误或效率低下。我只是指出 ORM 的一个弱点。
标签: oracle performance linq odp.net