"CROSS APPLY"和"OUTER APPLY"的行为不一致

And*_*son 7 sql oracle join oracle12c

我在甲骨文-12C相似,具有典型的论坛的模式accounts,posts,comments.我正在编写一个查询来获取...

  1. 一个用户
  2. 所有用户的帖子
  3. 对这些职位的评论
  4. 和每个评论的作者.

查询如下所示:

select "accounts".*, "p".*, "c".*, "author".*
from "accounts"
cross apply (
    select * from "posts"
    where "posts"."author_id" = "accounts"."id"
) "p"
cross apply (
    select * from "comments"
    where "comments"."post_id" = "p"."id"
) "c"
left join "accounts" "author" on "author"."id" = "c"."author_id"
where "accounts"."id" = 1
Run Code Online (Sandbox Code Playgroud)

此查询按预期工作.我正在使用CROSS APPLY而不是典型的JOIN,因为我将在稍后添加OFFSETFETCH分页.但是,问题是CROSS APPLY省略了没有评论的帖子,这是我不想要的.我想在结果中保留帖子,即使他们没有评论.

所以我尝试CROSS APPLY改为OUTER APPLY.

select "accounts".*, "p".*, "c".*, "author".*
from "accounts"
outer apply (
    select * from "posts"
    where "posts"."author_id" = "accounts"."id"
) "p"
outer apply (
    select * from "comments"
    where "comments"."post_id" = "p"."id"
) "c"
left join "accounts" "author" on "author"."id" = "c"."author_id"
where "accounts"."id" = 1
Run Code Online (Sandbox Code Playgroud)

但是现在我收到了这个错误:

ORA-00904: "p"."id": invalid identifier
00904. 00000 -  "%s: invalid identifier"
*Cause:    
*Action:
Error at Line: 9 Column: 34
Run Code Online (Sandbox Code Playgroud)

出于某种原因,我的第二次OUTER APPLY加入是抱怨我引用"p"."id"第一个结果.但是当我使用时它很好CROSS APPLY.

到底是怎么回事?为什么这些之间的行为存在差异?

编辑:OUTER APPLY在这个基本示例中似乎没有必要.这已经从一个更复杂的场景中,我必须坚持,蒸OUTER APPLY确有必要,但方案的细节无关的实际问题,我要问,这是关于行为之间的这种看似无证差异CROSSOUTER APPLY.

编辑:

Oracle版本: Database 12c标准版12.1.0.2.0版 - 64位生产

客户端: Oracle SQL Developer版本4.2.0.16.356

服务器:输出uname -a-Linux ubuntu-1gb-sfo2-01 4.4.0-64-generic #85-Ubuntu SMP Mon Feb 20 11:50:30 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

DDL: 链接

Mat*_*eak 10

对于CodeFuller的答案,我只想补充一点(A)有一个可用于此bug的补丁和(B)有一个可在12.1.0.2中运行的解决方法,但我不知道它是否能满足您的需求.

解决方法是基本嵌套连接,如下所示:

SELECT accounts.*,
       p.*,
       author.*
FROM   accounts
       CROSS APPLY (SELECT posts.id,
                           posts.author_id,
                           posts.text,
                           comments.comment_author_id,
                           comments.comment_text
                    FROM   posts
                           OUTER APPLY (SELECT comments.author_id comment_author_id,
                                               comments.text comment_text
                                        FROM   comments
                                        WHERE  comments.post_id = posts.id) comments
                    WHERE  posts.author_id = accounts.id) p
       LEFT JOIN accounts author
         ON author.id = p.comment_author_id
WHERE  accounts.id = 1;


ID   NAME      ID_1 AUTHOR_ID TEXT                                              COMMENT_AUTHOR_ID COMMENT_TEXT                            ID_2  NAME_1                               
---- --------- ---- --------- ------------------------------------------------- ----------------- --------------------------------------- ----- ------------------- 
   1 Fred         1         1 Fred wrote this and it has comments                               3 This is Helen's comment on Fred's post      3 Helen                                
   1 Fred         2         1 Fred wrote this and it does not have any comments
-------- End of Data --------
2 row(s) fetched
Run Code Online (Sandbox Code Playgroud)

参考:表DDL的变通方法

CREATE TABLE accounts
(
  id     NUMBER PRIMARY KEY,
  name   VARCHAR2 (30)
);

CREATE TABLE posts
(
  id          NUMBER PRIMARY KEY,
  author_id   NUMBER,
  text        VARCHAR2 (240)
);

CREATE TABLE comments
(
  id          NUMBER PRIMARY KEY,
  post_id     NUMBER,
  author_id   NUMBER,
  text        VARCHAR2 (240)
);

INSERT INTO accounts (id, name)
VALUES (1, 'Fred');

INSERT INTO accounts (id, name)
VALUES (2, 'Mary');

INSERT INTO accounts (id, name)
VALUES (3, 'Helen');

INSERT INTO accounts (id, name)
VALUES (4, 'Iqbal');

INSERT INTO posts (id, author_id, text)
VALUES (1, 1, 'Fred wrote this and it has comments');

INSERT INTO posts (id, author_id, text)
VALUES (2, 1, 'Fred wrote this and it does not have any comments');

INSERT INTO posts (id, author_id, text)
VALUES (3, 4, 'Iqbal wrote this and it does not have any comments');

INSERT INTO comments (id,
                      post_id,
                      author_id,
                      text)
VALUES (1,
        1,
        3,
        'This is Helen''s comment on Fred''s post');
Run Code Online (Sandbox Code Playgroud)


Cod*_*ler 6

您的查询没有任何问题.你所面临的bug二千一百五十四万七千一百三十零分之二千〇三十五万六千七百三十三,在12.1.0.2中引入的,如所描述这里.为了克服它,请使用12.1.0.2之前的版本或应用最新更新(修复的链接线程声明在12.1.0.2.160419补丁集更新中可用).

这个答案主要是由Matthew McPeakMartin Smith发现的.我刚刚如下所述进行了首次尝试,发现该问题在Oracle 12.1.0.1上无法重现.

第一次回答尝试:

我用你的架构创建了测试数据库,这两个查询对我来说都很好.第一个不返回没有评论的帖子,第二个返回所有帐户帖子,没有任何ORA-00904错误.我在Oracle 12c上做过这个测试.

继续提问:

  1. 尝试从帖子中复制/粘贴并执行第二个查询.有时候一些令人讨厌的错别字会潜入查询中.来自您帖子的确切查询按预期为我工作.

  2. 如果你仍然得到同样的错误,请提供您使用的DDL accounts,postscomments表的创建.

  3. 请指定您正在使用的SQL客户端.错误肯定是在服务器端,但在这种奇怪的情况下,每一个小细节都可以产生影响.

我的测试数据库:

CREATE TABLE "accounts"
(
    "id" NUMBER(11) NOT NULL,
    "name" NVARCHAR2(256),
    CONSTRAINT ACCOUNTS_PK PRIMARY KEY ("id")
)
/

CREATE TABLE "posts"
(
    "id" NUMBER(11) NOT NULL,
    "author_id" NUMBER(11) NOT NULL,
    "post_text" NVARCHAR2(1024),
    CONSTRAINT POSTS_PK PRIMARY KEY ("id"),
    CONSTRAINT POST_ACCOUNT_FK FOREIGN KEY ("author_id") REFERENCES "accounts" ("id") ON DELETE CASCADE
)
/

CREATE TABLE "comments"
(
    "id" NUMBER(11) NOT NULL,
    "author_id" NUMBER(11) NOT NULL,
    "post_id" NUMBER(11) NOT NULL,
    "comment_text" NVARCHAR2(1024),
    CONSTRAINT COMMENTS_PK PRIMARY KEY ("id"),
    CONSTRAINT COMMENT_ACCOUNT_FK FOREIGN KEY ("author_id") REFERENCES "accounts" ("id") ON DELETE CASCADE,
    CONSTRAINT COMMENT_POST_FK FOREIGN KEY  ("post_id") REFERENCES "posts" ("id") ON DELETE CASCADE
)
/

INSERT INTO "accounts"("id", "name") VALUES(1, 'testuser')
/
INSERT INTO "posts"("id", "author_id", "post_text") VALUES(1, 1, 'First test post')
/
INSERT INTO "posts"("id", "author_id", "post_text") VALUES(2, 1, 'Second test post')
/
INSERT INTO "comments"("id", "author_id", "post_id", "comment_text") VALUES(1, 1, 2, 'It is a very cool post')
/
COMMIT
Run Code Online (Sandbox Code Playgroud)

  • 有趣.在12.1.0.2上,使用您的DDL和OP的查询,我得到与OP相同的ORA-00904错误.OP可能遇到错误20356733,这是在12.1.0.2中引入的. (2认同)