【问题标题】:SQL single-row subquery returns more than one row?SQL单行子查询返回多行?
【发布时间】:2016-08-16 06:51:00
【问题描述】:

我正在尝试从一个查询中获取 ID 和 USER 名称,但同时我正在查看我的 WHERE 子句,如果 ID 存在于其他表中。我得到了错误:

ORA-01427: single-row subquery returns more than one row

这是我的查询的样子:

SELECT s.ID, s.LASTFIRST
From USERS s
Left Outer Join CALENDAR c
On s.ID = c.USERID
Where c.SUPERVISOR = '103'
And TO_CHAR(c.DATEENROLLED,'fmmm/fmdd/yyyy') >= '4/22/2016'
And TO_CHAR(c.DATELEFT,'fmmm/fmdd/yyyy') <= '4/22/2016'
And s.ID != (SELECT USER_ID
             From RESERVATIONS
             Where EVENT_ID = '56')

where 子句中的查询返回两个 ID:158 和 159,因此这两个不应在我正在查找 s.ID 和 s.LASTFIRST 的查询中返回。什么可能导致此错误?

【问题讨论】:

    标签: sql oracle subquery


    【解决方案1】:

    使用not in 而不是!=

    !== 用于单个 ID 和值,not inin 用于多个

    And s.ID not in (SELECT USER_ID
                     From RESERVATIONS
                     Where EVENT_ID = '56')
    

    编辑:not in vs not exists

    Not exists 也是一个完全可行的选择。事实上,如果子查询结果集中有null 值的可能性,最好not existsnot in 更好——在Oracle 中,null 的存在将导致not in 不返回任何结果。作为一般规则,我将not in 用于ID,而不是空列,并将not exists 用于其他所有内容。最好总是使用not exists...我想是个人喜好。

    Not exists 会这样写:

    SELECT s.ID, s.LASTFIRST
    From USERS s
    Left Outer Join CALENDAR c
    On s.ID = c.USERID
    Where c.SUPERVISOR = '103'
    And TO_CHAR(c.DATEENROLLED,'fmmm/fmdd/yyyy') >= '4/22/2016'
    And TO_CHAR(c.DATELEFT,'fmmm/fmdd/yyyy') <= '4/22/2016'
    And not exists (SELECT USER_ID
                   From RESERVATIONS r
                   Where r.USER_ID = S.ID
                   And EVENT_ID = '56')
    

    性能

    在 Oracle 中,使用 not innot existsleft join 之间没有性能差异。

    来源https://explainextended.com/2009/09/17/not-in-vs-not-exists-vs-left-join-is-null-oracle/

    只要列表值声明为 NOT NULL,Oracle 的优化器就能够看到 NOT EXISTS、NOT IN 和 LEFT JOIN / IS NULL 在语义上是等价的。

    它对所有三种方法使用相同的执行计划,并且它们在同一时间产生相同的结果。

    【讨论】:

    • 谢谢@Aron D。我尝试使用NOT EXISTS,但没有奏效。 NOT IN 工作正常。
    • not in 很慢。有更快的方法来完成同样的事情。
    • @user3023588 更新了not exists的答案
    • @Aron D 谢谢!
    【解决方案2】:

    这是与您的问题无关的格式化评论。

    这很慢:

    And TO_CHAR(c.DATEENROLLED,'fmmm/fmdd/yyyy') >= '4/22/2016'
    

    因为您正在过滤函数结果。

    这在逻辑上是等价的,而且速度更快:

    And c.DATEENROLLED >= to_date('4/22/2016','fmmm/fmdd/yyyy')
    

    编辑从这里开始

    Aaron D 的回答说使用not in。这里有两种更快的方法来做同样的事情:

    left join reservations r on s.id = user_id
    and r.event_id = '56'
    etc
    where r.user_id is null
    

    where s.id in 
    (
    select user_id 
    from reservations
    minus
    select user_id 
    from reservations
    where event_id = 56
    )    
    

    【讨论】:

    • 您的建议是在左连接后移动 event_id 而不是在子查询中使用它?那是更有效还是?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-02
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多