嵌套 select 中的 Oracle 11g order by 和 rownum 不适用于内部联接
Oracle 11g order by and rownum in nested select does not work with inner join
我想要 return 两列,其中第二列是通过外国 ID 链接的用户名。我担心在州内可能存在如此多条不同的记录,所以我想按州排序(自定义优先级)并找到第一个。
这是编辑前的工作查询。
select entry.ID,
(select username.USERNAME from USER_CREDENTIALS username
inner join CREDENTIALS cred on username.ID = cred.ID
where cred.SUBJECT_ID=entry.SUBJECTID
)
from ENTRY entry
where entry.ID in (1, 2, 5);
我的最终目标是确保内部 select return 最多有一条记录(null
中没有记录结果很好)。所以在这里我使用 rownum
和 ORDER BY
状态*自定义优先级)关于 rownum
和 ORDER By
在描述 here 的同一查询中的问题(来源sqlandplsql.com)。我最终得到了这样的结果:
select entry.ID,
(select * from
(select username.USERNAME from USER_CREDENTIALS username
inner join CREDENTIALS cred on username.ID = cred.ID
where cred.SUBJECT_ID=entry.SUBJECTID
order by case when cred.STATE = 'A' then 1 else 2 end
)
where rownum=1
)
from ENTRY entry
where entry.ID in (1, 2, 5);
抛出异常:
[42000][904] ORA-00904: "ENTRY"."SUBJECTID": invalid identifier
我怀疑只有这样的属性只对第一级嵌套可见select。但是,我需要通过另一个不符合 "ENTRY"."SUBJECTID"
可见性的 select 使用 rownum
-ORDER BY
组合。
如何重写队列以消除错误并使其 rownum
-ORDER BY
安全?
Oracle(通常)不允许在多于一层的嵌套中使用关联子句。相反,您可以使用 keep
:
select e.ID,
(select max(uc.USERNAME) keep (dense_rank first order by (case when c.STATE = 'A' then 1 else 2 end))
from USER_CREDENTIALS uc join
CREDENTIALS c
on uc.ID = c.ID
where c.SUBJECTID = e.SUBJECTID
)
from ENTRY e
where e.ID in (1, 2, 5);
keep
是 Oracle 用于“第一个”聚合函数的奇特语法。
您不能引用已删除多个子查询的标识符。在您的情况下,您在嵌套两层的子查询中引用 ENTRY.SUBJECTID
并且看不到标识符。相反,您需要将过滤器移动到外部子查询:
选项 1
select e.ID,
( select USERNAME
from (
select c.SUBJECT_ID,
u.USERNAME
from USER_CREDENTIALS u
inner join CREDENTIALS c
on ( u.ID = c.ID )
order by case when STATE = 'A' then 1 else 2 end
) t
WHERE t.SUBJECT_ID = e.SUBJECTID
AND rownum=1
) AS username
from ENTRY e
where e.ID in (1, 2, 5);
选项 2
或者使用ROW_NUMBER
解析函数:
select e.ID,
( select USERNAME
from (
select c.SUBJECT_ID,
u.USERNAME,
ROW_NUMBER() OVER (
PARTITION BY c.SUBJECT_ID
ORDER BY case when STATE = 'A' then 1 else 2 end
) As rn
from USER_CREDENTIALS u
inner join CREDENTIALS c
on ( u.ID = c.ID )
) t
WHERE t.SUBJECT_ID = e.SUBJECTID
AND rn=1
) AS username
from ENTRY e
where e.ID in (1, 2, 5);
其中,对于示例数据:
CREATE TABLE entry ( id, subjectid ) AS
SELECT LEVEL, LEVEL FROM DUAL CONNECT BY LEVEL <= 5;
CREATE TABLE credentials ( id, subject_id ) AS
SELECT LEVEL, LEVEL FROM DUAL CONNECT BY LEVEL <= 5;
CREATE TABLE user_credentials ( id, username, state ) AS
SELECT LEVEL, 'NameA' || LEVEL, 'A' FROM DUAL CONNECT BY LEVEL <= 4 UNION ALL
SELECT LEVEL, 'NameB' || LEVEL, 'B' FROM DUAL CONNECT BY LEVEL <= 5;
双输出:
ID | USERNAME
-: | :-------
1 | NameA1
2 | NameA2
5 | NameB5
db<>fiddle here
如果您使用的是 Oracle 12,则可以使用 FETCH FIRST ROW ONLY
语法:
select e.ID,
( select u.USERNAME
from USER_CREDENTIALS u
inner join CREDENTIALS c
on ( u.ID = c.ID )
WHERE t.SUBJECT_ID=e.SUBJECTID
order by case when STATE = 'A' then 1 else 2 end
FETCH FIRST ROW ONLY
)
from ENTRY e
where e.ID in (1, 2, 5);
(与ROW_NUMBER
解析解相同EXPLAIN PLAN
)
db<>fiddle here
我想要 return 两列,其中第二列是通过外国 ID 链接的用户名。我担心在州内可能存在如此多条不同的记录,所以我想按州排序(自定义优先级)并找到第一个。
这是编辑前的工作查询。
select entry.ID,
(select username.USERNAME from USER_CREDENTIALS username
inner join CREDENTIALS cred on username.ID = cred.ID
where cred.SUBJECT_ID=entry.SUBJECTID
)
from ENTRY entry
where entry.ID in (1, 2, 5);
我的最终目标是确保内部 select return 最多有一条记录(null
中没有记录结果很好)。所以在这里我使用 rownum
和 ORDER BY
状态*自定义优先级)关于 rownum
和 ORDER By
在描述 here 的同一查询中的问题(来源sqlandplsql.com)。我最终得到了这样的结果:
select entry.ID,
(select * from
(select username.USERNAME from USER_CREDENTIALS username
inner join CREDENTIALS cred on username.ID = cred.ID
where cred.SUBJECT_ID=entry.SUBJECTID
order by case when cred.STATE = 'A' then 1 else 2 end
)
where rownum=1
)
from ENTRY entry
where entry.ID in (1, 2, 5);
抛出异常:
[42000][904] ORA-00904: "ENTRY"."SUBJECTID": invalid identifier
我怀疑只有这样的属性只对第一级嵌套可见select。但是,我需要通过另一个不符合 "ENTRY"."SUBJECTID"
可见性的 select 使用 rownum
-ORDER BY
组合。
如何重写队列以消除错误并使其 rownum
-ORDER BY
安全?
Oracle(通常)不允许在多于一层的嵌套中使用关联子句。相反,您可以使用 keep
:
select e.ID,
(select max(uc.USERNAME) keep (dense_rank first order by (case when c.STATE = 'A' then 1 else 2 end))
from USER_CREDENTIALS uc join
CREDENTIALS c
on uc.ID = c.ID
where c.SUBJECTID = e.SUBJECTID
)
from ENTRY e
where e.ID in (1, 2, 5);
keep
是 Oracle 用于“第一个”聚合函数的奇特语法。
您不能引用已删除多个子查询的标识符。在您的情况下,您在嵌套两层的子查询中引用 ENTRY.SUBJECTID
并且看不到标识符。相反,您需要将过滤器移动到外部子查询:
选项 1
select e.ID,
( select USERNAME
from (
select c.SUBJECT_ID,
u.USERNAME
from USER_CREDENTIALS u
inner join CREDENTIALS c
on ( u.ID = c.ID )
order by case when STATE = 'A' then 1 else 2 end
) t
WHERE t.SUBJECT_ID = e.SUBJECTID
AND rownum=1
) AS username
from ENTRY e
where e.ID in (1, 2, 5);
选项 2
或者使用ROW_NUMBER
解析函数:
select e.ID,
( select USERNAME
from (
select c.SUBJECT_ID,
u.USERNAME,
ROW_NUMBER() OVER (
PARTITION BY c.SUBJECT_ID
ORDER BY case when STATE = 'A' then 1 else 2 end
) As rn
from USER_CREDENTIALS u
inner join CREDENTIALS c
on ( u.ID = c.ID )
) t
WHERE t.SUBJECT_ID = e.SUBJECTID
AND rn=1
) AS username
from ENTRY e
where e.ID in (1, 2, 5);
其中,对于示例数据:
CREATE TABLE entry ( id, subjectid ) AS
SELECT LEVEL, LEVEL FROM DUAL CONNECT BY LEVEL <= 5;
CREATE TABLE credentials ( id, subject_id ) AS
SELECT LEVEL, LEVEL FROM DUAL CONNECT BY LEVEL <= 5;
CREATE TABLE user_credentials ( id, username, state ) AS
SELECT LEVEL, 'NameA' || LEVEL, 'A' FROM DUAL CONNECT BY LEVEL <= 4 UNION ALL
SELECT LEVEL, 'NameB' || LEVEL, 'B' FROM DUAL CONNECT BY LEVEL <= 5;
双输出:
ID | USERNAME -: | :------- 1 | NameA1 2 | NameA2 5 | NameB5
db<>fiddle here
如果您使用的是 Oracle 12,则可以使用 FETCH FIRST ROW ONLY
语法:
select e.ID,
( select u.USERNAME
from USER_CREDENTIALS u
inner join CREDENTIALS c
on ( u.ID = c.ID )
WHERE t.SUBJECT_ID=e.SUBJECTID
order by case when STATE = 'A' then 1 else 2 end
FETCH FIRST ROW ONLY
)
from ENTRY e
where e.ID in (1, 2, 5);
(与ROW_NUMBER
解析解相同EXPLAIN PLAN
)
db<>fiddle here