row_number() 在某些情况下得到错误的结果
row_number() gets wrong result in certain conditions
我有一个 SQL 查询在 SQL 服务器上完美运行,但它在 Oracle 上失败了,在我看来,它不应该。
这是重现它的例子:
CREATE TABLE TEST
( TEST_ID NUMBER(37,0) NOT NULL,
TEST_NAME VARCHAR2(50 BYTE),
TEST_GROUP VARCHAR2(20 BYTE),
CONSTRAINT TEST_PK PRIMARY KEY (TEST_ID) );
INSERT INTO TEST (TEST_ID, TEST_NAME) VALUES (1, 'TEST 1');
INSERT INTO TEST (TEST_ID, TEST_NAME, TEST_GROUP) VALUES (2, 'TEST 2', 'A');
INSERT INTO TEST (TEST_ID, TEST_NAME, TEST_GROUP) VALUES (3, 'TEST 3', 'B');
INSERT INTO TEST (TEST_ID, TEST_NAME, TEST_GROUP) VALUES (4, 'TEST 4', 'A');
本次查询returns预期信息:
SELECT TEST_GROUP, COUNT(R$), MIN(R$) R$_A, MAX(R$) R$_Z
FROM (
SELECT MAIN.*, ROW_NUMBER() OVER (ORDER BY TEST_GROUP, TEST_ID) R$
FROM ( SELECT TEST_ID, TEST_NAME, TEST_GROUP
FROM TEST
GROUP BY TEST_ID, TEST_NAME, TEST_GROUP
) MAIN
) MAIN
GROUP BY TEST_GROUP
它returns三个TEST_GROUPS计算正确。
TEST_GROUP COUNT(R$) R$_A R$_Z
-------------- --------- ------- -------
A 2 1 2
B 1 3 3
(null) 1 4 4
解释计划:
OPERATION OBJECT_NAME CARDINALITY COST
SELECT STATEMENT 4 3
SORT (GROUP BY NOSORT) 4 3
VIEW 4 3
WINDOW (NOSORT) 4 3
SORT (GROUP BY) 4 3
TABLE ACCESS (FULL) TEST 4 3
Other XML
{info}
info type="db_version"
12.1.0.1
info type="parse_schema"
"BABTEC"
info type="dynamic_sampling"
2
info type="plan_hash"
1486410247
info type="plan_hash_2"
1249517352
{hint}
FULL(@"SEL5DD26A" "TEST"@"SEL")
NO_ACCESS(@"SEL" "MAIN"@"SEL")
OUTLINE(@"SEL")
OUTLINE(@"SEL")
OUTLINE_LEAF(@"SEL")
MERGE(@"SEL")
OUTLINE_LEAF(@"SEL5DD26A")
ALL_ROWS
DB_VERSION('12.1.0.1')
OPTIMIZER_FEATURES_ENABLE('12.1.0.1')
IGNORE_OPTIM_EMBEDDED_HINTS
但是,如果我们更改 ROW_NUMBER
中的排序(通过将默认的 ASC 更改为 DESC),它不会:
SELECT TEST_GROUP, COUNT(R$), MIN(R$) R$_A, MAX(R$) R$_Z
FROM (
SELECT MAIN.*, ROW_NUMBER() OVER (ORDER BY TEST_GROUP **DESC**, TEST_ID) R$
FROM ( SELECT TEST_ID, TEST_NAME, TEST_GROUP
FROM TEST
GROUP BY TEST_ID, TEST_NAME, TEST_GROUP
) MAIN
) MAIN
GROUP BY TEST_GROUP;
只有returns一个组。
TEST_GROUP COUNT(R$) R$_A R$_Z
-------------- --------- ------- -------
A 4 1 4
解释计划:
OPERATION OBJECT_NAME CARDINALITY COST
SELECT STATEMENT 4 3
HASH(GROUP BY) 4 3
VIEW 4 3
WINDOW (NOSORT) 4 3
SORT (GROUP BY) 4 3
TABLE ACCESS (FULL) TEST 4 3
Other XML
{info}
info type="db_version"
12.1.0.1
info type="parse_schema"
"BABTEC"
info type="dynamic_sampling"
2
info type="plan_hash"
1128091058
info type="plan_hash_2"
3776505473
{hint}
FULL(@"SEL5DD26A" "TEST"@"SEL")
NO_ACCESS(@"SEL" "MAIN"@"SEL")
OUTLINE(@"SEL")
OUTLINE(@"SEL")
OUTLINE_LEAF(@"SEL")
MERGE(@"SEL")
OUTLINE_LEAF(@"SEL5DD26A")
ALL_ROWS
DB_VERSION('12.1.0.1')
OPTIMIZER_FEATURES_ENABLE('12.1.0.1')
IGNORE_OPTIM_EMBEDDED_HINTS
请注意,要重现问题,要求最内部的查询具有 GROUP BY
表达式。如果不是,结果就是我们所期望的:
SELECT TEST_GROUP, COUNT(R$), MIN(R$) R$_A, MAX(R$) R$_Z
FROM (
SELECT MAIN.*, ROW_NUMBER() OVER (ORDER BY TEST_GROUP DESC, TEST_ID) R$
FROM ( SELECT TEST_ID, TEST_NAME, TEST_GROUP
FROM TEST ) MAIN
) MAIN
GROUP BY TEST_GROUP;
TEST_GROUP COUNT(R$) R$_A R$_Z
----------------------------------------
(null) 1 1 1
B 1 2 2
A 2 3 4
我们正在使用 Oracle Database 12c 版本 12.1.0.1.0 - 64 位
这个问题有一个解决方法,就是在 GROUP BY 之后添加一个 ORDER BY
子句,但这只在 Oracle 中有效,在 SQLServer 中失败。查询将是:
SELECT TEST_GROUP, COUNT(R$), MIN(R$) R$_A, MAX(R$) R$_Z
FROM (
SELECT MAIN.*, ROW_NUMBER() OVER (ORDER BY TEST_GROUP DESC, TEST_ID) R$
FROM ( SELECT TEST_ID, TEST_NAME, TEST_GROUP
FROM TEST
GROUP BY TEST_ID, TEST_NAME, TEST_GROUP
ORDER BY TEST_GROUP DESC ) MAIN
) MAIN
GROUP BY TEST_GROUP;
TEST_GROUP COUNT(R$) R$_A R$_Z
-------------- --------- ------- -------
(null) 1 1 1
B 1 2 2
A 2 3 4
任何帮助将不胜感激
这好像是bug 18353141。如果设置 NLS_SORT 并且 NLS_COMP 设置为二进制,则它在 11.2.0.4 和 12.1.0.1 中可重现:
alter session set NLS_SORT=spanish;
alter session set NLS_COMP=binary;
-- your second query
T COUNT(R$) R$_A R$_Z
- ---------- ---------- ----------
A 4 1 4
将排序更改为语言修复它:
alter session set NLS_SORT=spanish;
alter session set NLS_COMP=linguistic;
-- your second query
T COUNT(R$) R$_A R$_Z
- ---------- ---------- ----------
1 1 1
B 1 2 2
A 2 3 4
也可以修改query,使解析的order-by和group-by不同;例如这只是连接空值(在 DESC
之前):
alter session set NLS_SORT=spanish;
alter session set NLS_COMP=binary;
SELECT TEST_GROUP, COUNT(R$), MIN(R$) R$_A, MAX(R$) R$_Z
FROM (
SELECT MAIN.*, ROW_NUMBER() OVER (ORDER BY TEST_GROUP||null DESC, TEST_ID) R$
-------------------------------------------------------------^^^^^^
FROM ( SELECT TEST_ID, TEST_NAME, TEST_GROUP
FROM TEST
GROUP BY TEST_ID, TEST_NAME, TEST_GROUP
) MAIN
) MAIN
GROUP BY TEST_GROUP;
T COUNT(R$) R$_A R$_Z
- ---------- ---------- ----------
1 1 1
B 1 2 2
A 2 3 4
但听起来您希望同一个查询在 SQL Server 和 Oracle 中都有效,因此您需要找到一种对两者都有效的修改方法。
它已在 12.1.0.2 补丁集中修复,如果您无法应用补丁集,可以为 12.1.0.1 提供单独的补丁。
我有一个 SQL 查询在 SQL 服务器上完美运行,但它在 Oracle 上失败了,在我看来,它不应该。
这是重现它的例子:
CREATE TABLE TEST
( TEST_ID NUMBER(37,0) NOT NULL,
TEST_NAME VARCHAR2(50 BYTE),
TEST_GROUP VARCHAR2(20 BYTE),
CONSTRAINT TEST_PK PRIMARY KEY (TEST_ID) );
INSERT INTO TEST (TEST_ID, TEST_NAME) VALUES (1, 'TEST 1');
INSERT INTO TEST (TEST_ID, TEST_NAME, TEST_GROUP) VALUES (2, 'TEST 2', 'A');
INSERT INTO TEST (TEST_ID, TEST_NAME, TEST_GROUP) VALUES (3, 'TEST 3', 'B');
INSERT INTO TEST (TEST_ID, TEST_NAME, TEST_GROUP) VALUES (4, 'TEST 4', 'A');
本次查询returns预期信息:
SELECT TEST_GROUP, COUNT(R$), MIN(R$) R$_A, MAX(R$) R$_Z
FROM (
SELECT MAIN.*, ROW_NUMBER() OVER (ORDER BY TEST_GROUP, TEST_ID) R$
FROM ( SELECT TEST_ID, TEST_NAME, TEST_GROUP
FROM TEST
GROUP BY TEST_ID, TEST_NAME, TEST_GROUP
) MAIN
) MAIN
GROUP BY TEST_GROUP
它returns三个TEST_GROUPS计算正确。
TEST_GROUP COUNT(R$) R$_A R$_Z
-------------- --------- ------- -------
A 2 1 2
B 1 3 3
(null) 1 4 4
解释计划:
OPERATION OBJECT_NAME CARDINALITY COST
SELECT STATEMENT 4 3
SORT (GROUP BY NOSORT) 4 3
VIEW 4 3
WINDOW (NOSORT) 4 3
SORT (GROUP BY) 4 3
TABLE ACCESS (FULL) TEST 4 3
Other XML
{info}
info type="db_version"
12.1.0.1
info type="parse_schema"
"BABTEC"
info type="dynamic_sampling"
2
info type="plan_hash"
1486410247
info type="plan_hash_2"
1249517352
{hint}
FULL(@"SEL5DD26A" "TEST"@"SEL")
NO_ACCESS(@"SEL" "MAIN"@"SEL")
OUTLINE(@"SEL")
OUTLINE(@"SEL")
OUTLINE_LEAF(@"SEL")
MERGE(@"SEL")
OUTLINE_LEAF(@"SEL5DD26A")
ALL_ROWS
DB_VERSION('12.1.0.1')
OPTIMIZER_FEATURES_ENABLE('12.1.0.1')
IGNORE_OPTIM_EMBEDDED_HINTS
但是,如果我们更改 ROW_NUMBER
中的排序(通过将默认的 ASC 更改为 DESC),它不会:
SELECT TEST_GROUP, COUNT(R$), MIN(R$) R$_A, MAX(R$) R$_Z
FROM (
SELECT MAIN.*, ROW_NUMBER() OVER (ORDER BY TEST_GROUP **DESC**, TEST_ID) R$
FROM ( SELECT TEST_ID, TEST_NAME, TEST_GROUP
FROM TEST
GROUP BY TEST_ID, TEST_NAME, TEST_GROUP
) MAIN
) MAIN
GROUP BY TEST_GROUP;
只有returns一个组。
TEST_GROUP COUNT(R$) R$_A R$_Z
-------------- --------- ------- -------
A 4 1 4
解释计划:
OPERATION OBJECT_NAME CARDINALITY COST
SELECT STATEMENT 4 3
HASH(GROUP BY) 4 3
VIEW 4 3
WINDOW (NOSORT) 4 3
SORT (GROUP BY) 4 3
TABLE ACCESS (FULL) TEST 4 3
Other XML
{info}
info type="db_version"
12.1.0.1
info type="parse_schema"
"BABTEC"
info type="dynamic_sampling"
2
info type="plan_hash"
1128091058
info type="plan_hash_2"
3776505473
{hint}
FULL(@"SEL5DD26A" "TEST"@"SEL")
NO_ACCESS(@"SEL" "MAIN"@"SEL")
OUTLINE(@"SEL")
OUTLINE(@"SEL")
OUTLINE_LEAF(@"SEL")
MERGE(@"SEL")
OUTLINE_LEAF(@"SEL5DD26A")
ALL_ROWS
DB_VERSION('12.1.0.1')
OPTIMIZER_FEATURES_ENABLE('12.1.0.1')
IGNORE_OPTIM_EMBEDDED_HINTS
请注意,要重现问题,要求最内部的查询具有 GROUP BY
表达式。如果不是,结果就是我们所期望的:
SELECT TEST_GROUP, COUNT(R$), MIN(R$) R$_A, MAX(R$) R$_Z
FROM (
SELECT MAIN.*, ROW_NUMBER() OVER (ORDER BY TEST_GROUP DESC, TEST_ID) R$
FROM ( SELECT TEST_ID, TEST_NAME, TEST_GROUP
FROM TEST ) MAIN
) MAIN
GROUP BY TEST_GROUP;
TEST_GROUP COUNT(R$) R$_A R$_Z
----------------------------------------
(null) 1 1 1
B 1 2 2
A 2 3 4
我们正在使用 Oracle Database 12c 版本 12.1.0.1.0 - 64 位
这个问题有一个解决方法,就是在 GROUP BY 之后添加一个 ORDER BY
子句,但这只在 Oracle 中有效,在 SQLServer 中失败。查询将是:
SELECT TEST_GROUP, COUNT(R$), MIN(R$) R$_A, MAX(R$) R$_Z
FROM (
SELECT MAIN.*, ROW_NUMBER() OVER (ORDER BY TEST_GROUP DESC, TEST_ID) R$
FROM ( SELECT TEST_ID, TEST_NAME, TEST_GROUP
FROM TEST
GROUP BY TEST_ID, TEST_NAME, TEST_GROUP
ORDER BY TEST_GROUP DESC ) MAIN
) MAIN
GROUP BY TEST_GROUP;
TEST_GROUP COUNT(R$) R$_A R$_Z
-------------- --------- ------- -------
(null) 1 1 1
B 1 2 2
A 2 3 4
任何帮助将不胜感激
这好像是bug 18353141。如果设置 NLS_SORT 并且 NLS_COMP 设置为二进制,则它在 11.2.0.4 和 12.1.0.1 中可重现:
alter session set NLS_SORT=spanish;
alter session set NLS_COMP=binary;
-- your second query
T COUNT(R$) R$_A R$_Z
- ---------- ---------- ----------
A 4 1 4
将排序更改为语言修复它:
alter session set NLS_SORT=spanish;
alter session set NLS_COMP=linguistic;
-- your second query
T COUNT(R$) R$_A R$_Z
- ---------- ---------- ----------
1 1 1
B 1 2 2
A 2 3 4
也可以修改query,使解析的order-by和group-by不同;例如这只是连接空值(在 DESC
之前):
alter session set NLS_SORT=spanish;
alter session set NLS_COMP=binary;
SELECT TEST_GROUP, COUNT(R$), MIN(R$) R$_A, MAX(R$) R$_Z
FROM (
SELECT MAIN.*, ROW_NUMBER() OVER (ORDER BY TEST_GROUP||null DESC, TEST_ID) R$
-------------------------------------------------------------^^^^^^
FROM ( SELECT TEST_ID, TEST_NAME, TEST_GROUP
FROM TEST
GROUP BY TEST_ID, TEST_NAME, TEST_GROUP
) MAIN
) MAIN
GROUP BY TEST_GROUP;
T COUNT(R$) R$_A R$_Z
- ---------- ---------- ----------
1 1 1
B 1 2 2
A 2 3 4
但听起来您希望同一个查询在 SQL Server 和 Oracle 中都有效,因此您需要找到一种对两者都有效的修改方法。
它已在 12.1.0.2 补丁集中修复,如果您无法应用补丁集,可以为 12.1.0.1 提供单独的补丁。