Oracle:有子句中的命名子查询

Posted

技术标签:

【中文标题】Oracle:有子句中的命名子查询【英文标题】:Oracle: named subquery in having clause 【发布时间】:2013-05-15 08:30:04 【问题描述】:

如何从 having 子句的子选择中引用 network 列?

select distinct c.id, c.name,
(
select count(cm.id) cnt
from
company_mapping cm
where
cm.company_id_source = c.id
or
cm.company_id_target = c.id
) network
from company c
where
c.name like 'foobar%'
group by c.id, c.name
having network > 1

ORA-00904:“网络”:标识符无效。如果我省略最后一行,它会按预期工作,但我只对 network > 1 的行感兴趣。

【问题讨论】:

您是否尝试过将network 更改为其他内容,例如aaaaa "AAA": 无效标识符 - 给出相同的错误代码。 【参考方案1】:

您无法访问在group byhavingwhere 中定义在select 中的字段。

sql操作符的顺序如下:

1.FROM clause
2.WHERE clause
3.GROUP BY clause
4.HAVING clause
5.SELECT clause
6.ORDER BY clause 

这就是为什么您可以在order by 中使用network,但不能在select 之前的运算符中使用。

【讨论】:

+1 好答案! (如果你放一个文档链接会更好:)) 你可以在这里看到MSSQL的顺序-msdn.microsoft.com/en-us/library/ms189499%28v=SQL.105%29.aspx。我相信Oracle的逻辑顺序是一样的。【参考方案2】:

Ronnis 是在正确的道路上,但查询实际上应该更简单。 请尽量避免 select inside select,因为它在 99% 的情况下都是性能杀手。

select c.id
,      c.name
,      count(*) network
from   company c
join   company_mapping cm on c.id in (cm.company_id_source, cm.company_id_target)
where  c.name like 'foobar%'
group by c.id, c.name
having count(*) > 1

【讨论】:

这里有些奇怪...我删除了 having 子句并运行了我的初始版本和你的版本,用有意义的东西替换了 foobar ,在我们的数据库中,您的查询需要 30-40 秒,我的不到 2秒。此外,您的版本不会返回 network=0 的公司。 可能不同之处在于您的版本必须首先创建一个巨大的笛卡尔积,然后才能选择名称为 'foobar%' 的那些,而在我的版本中,它首先选择那些 foobar 行并仅应用子选择他们? 是的,没错。我使用联接而不是左联接,因为 count(*) 必须大于 1 的规则。如果您也希望列表中的 network = 0,请使用左联接。我猜性能不好的原因是 Oracle 没有使用带有这个 IN 连接的索引。在 company_id_source 和 company_id_target 上添加索引(一起)或更改查询以使用索引。【参考方案3】:

您是否正在尝试做这样的事情?

select c.id
      ,c.name
      ,count(*) 
      ,count(s.company_id_source) as num_sources
      ,count(t.company_id_target) as num_targets
  from company              c
  left join company_mapping s on(s.company_id_source = c.id)
  left join company_mapping t on(t.company_id_target = c.id)
 where c.name like 'foobar%'  
 group 
    by c.id
      ,c.name
having count(s.company_id_source) > 1
    or count(t.company_id_target) > 1;

编辑: 下面是针对 cme​​ts 的新查询。 查询现在返回:匹配“Foobar”的所有公司,无论它们在表 company_mapping 中是否有关联行,以及:

num_sources:company_mapping 中公司所在行的 nr 来源。 num_targets:company_mapping 中的 nr 行,其中 公司是目标。 num_mappings:company_mapping 中“连接”的编号(源或目标)

.

select c.id
      ,c.name
      ,count(s.company_id_source) + count(t.company_id_target)   as num_mappings
      ,count(s.company_id_source) as num_sources
      ,count(t.company_id_target) as num_targets
  from company              c
  left join company_mapping s on(s.company_id_source = c.id)
  left join company_mapping t on(t.company_id_target = c.id)
 where c.name like 'foobar%'  
 group 
    by c.id
      ,c.name;

【讨论】:

是的,这就是我的想法,但如果可能的话,我会使用一个 company_mapping 表并在其中放置两个连接条件。我认为左连接也可以是内连接。 是的,我已经尝试过有两个左连接,但我肯定需要在一个列中计算两个左连接。 @kosmičák,您可以使用 COUNT(*) 获得计数。由于我两次外部加入 company_mapping,因此您也可以轻松获得源/目标的个人计数。 @DavidAldridge,好点子!直到看到 Bazz 的解决方案,我才想到将它们合二为一。但我认为我以这种方式离开它只是为了提供替代解决方案。他的解决方案和我的解决方案之间的执行计划将有所不同。 @Ronnies 仍然,company_mapping 中没有关联行的公司的 count(*)=1 而不是 0,这是错误的【参考方案4】:

首先,你不能在同一个查询中有distinctgroup by这只是多余的,你是对的,我不知道为什么oracle不抛出异常.

其次,别名与查询同级不知道。您应该将其包含在外部查询中。

select id, name, network
from (
    select c.id, c.name,
      (
      select count(cm.id) cnt
      from
      company_mapping cm
      where
      cm.company_id_source = c.id
      or
      cm.company_id_target = c.id
      ) network
    from company c
    where
    c.name like 'foobar%'
    group by c.id, c.name
)
WHERE network > 1;

【讨论】:

你的意思是 distinct 是多余的?因为 Oracle 在我的示例中并没有抱怨同时使用 distinctgroup by 如果 c.id 是 company 表上的主键,那么 group by 是多余的——如果不是,那么您最好定义一个内联视图以返回不同的 c。 id 和 c.name 在选择中应用子查询之前。想想看,你还不如去掉子查询,直接从 company 加入到 company_mapping 中,使用 have 子句过滤聚合结果。

以上是关于Oracle:有子句中的命名子查询的主要内容,如果未能解决你的问题,请参考以下文章

Oracle - IF 子句中的子查询 [重复]

Oracle---SQL子查询---详解

在 Oracle 中使用 connect by 在 with 子句中访问子查询中的父别名

Oracle In子句

oracle子查询

Oracle 查询优化器是不是将*** where 子句应用于子查询或视图?