1

在 Oracle SQL 相关子查询中,

我需要在子查询上有一个条件来准确地获取一行(如果有更多行,它不应该获取任何行)。

下面的查询工作得很好。但它涉及两次查询同一张表。

SELECT m.pk_1
      ,m.pk_2
      ,m.fld_3
      ,(SELECT c.pk_1
        FROM   child_tab c
        WHERE  (SELECT COUNT(*)
                FROM   child_tab c2
                WHERE  c2.pk_1 = m.pk_1
                AND    rownum <= 2) = 1
        AND    c.pk_1 = m.pk_1) c_pk_1
FROM   master_tab m
WHERE  m.pk_1 = '018'
AND    m.pk_2 = 'value'

通过使用解析函数重新设计子查询,下面的查询给出了错误

SELECT m.pk_1
      ,m.pk_2
      ,m.fld_3
      ,(SELECT ch.pk_1
        FROM   (SELECT COUNT(*) over() cnt
                      ,c.pk_1
                FROM   child_tab c
                WHERE  c.pk_1 = m.pk_1
                AND    rownum <= 2) ch
        WHERE  cnt = 1
        AND    c.pk_1 = m.pk_1) c_pk_1
FROM   master_tab m
WHERE  m.pk_1 = '018'
AND    m.pk_2 = 'value'

ORA-00904 m.pk_1 is an invalid identifier

使用 Oracle SQL 有没有更好的方法来做到这一点?

更新

我真正的要求是我将这个 SQL 生成为一个递归过程,它会导致很多这样的级别。而且我选择的数据不会局限于key。我只需要 SQL 解决方案,所以 PLSQL 不是一个选择。

4

2 回答 2

1

考虑一下。我没有任何方法来执行和检查这个。所以,请让我知道任何问题

   SELECT m.pk_1, m.pk_2, m.fld_3, C.PK1
   FROM MASTER_TAB M, CHILD_TAB C
   WHERE c.pk_1 = m.pk_1 AND m.pk_1 = '018'
   AND    m.pk_2 = 'value'
   AND EXISTS
   (
   SELECT *
   FROM CHILD_TAB C2
   c2.pk_1 = m.pk_1
   HAVING COUNT(*) = 1
   )

编辑:

   SELECT m.pk_1, m.pk_2, m.fld_3, C.PK1
   FROM MASTER_TAB M, (
   SELECT c2.pk_1
   FROM CHILD_TAB C2
   HAVING COUNT(*) = 1
   ) C
   WHERE c.pk_1 = m.pk_1 AND m.pk_1 = '018'
   AND    m.pk_2 = 'value'
于 2013-04-03T09:44:19.270 回答
1

Oracle 无法将 的值下推m.pk_1到两个级别的子查询中。但是,鉴于您m.pk_1在顶级 where 子句中提供了值,您可以将其直接放入标量子查询中,如下所示:

SELECT m.pk_1
      ,m.pk_2
      ,m.fld_3
      ,(SELECT ch.pk_1
        FROM   (SELECT COUNT(*) over() cnt
                      ,c.pk_1
                FROM   child_tab c
                WHERE  c.pk_1 = '018'
                AND    rownum <= 2) ch
        WHERE  cnt = 1
        AND    c.pk_1 = m.pk_1) c_pk_1
FROM   master_tab m
WHERE  m.pk_1 = '018'
AND    m.pk_2 = 'value';

或者,鉴于此m.pk_1 = c.pk_1,您不需要选择c.pk_1,只需使用 case 语句检查行是否存在,m.pk_1如果只有一行则返回:

SELECT m.pk_1
      ,m.pk_2
      ,m.fld_3
      ,CASE WHEN (SELECT COUNT(*)
                  FROM   child_tab c
                  WHERE  c.pk_1 = m.pk_1
                  AND    rownum <= 2) = 1 THEN
          m.pk_1
       END c_pk_1
FROM   master_tab m
WHERE  m.pk_1 = '018'
AND    m.pk_2 = 'value';

最后,您始终可以创建一个 PL/SQL 函数来为您进行查找,当存在too_many_rows. 如果你有少量不同的值m.pk_1并且你使用的是 11g,这可能是一个结果缓存函数,它应该表现良好:

CREATE FUNCTION get_id(m_pk master_tab.pk_1%TYPE) RETURN child_tab.pk_1%TYPE AS
  retval child_tab.pk_1%TYPE;
BEGIN

  SELECT t.pk_1
  INTO   retval
  FROM   child_tab t
  WHERE  t.pk_1 = m_pk
  AND    ROWNUM <= 2;

  RETURN retval;

EXCEPTION
  WHEN TOO_MANY_ROWS THEN
    RETURN NULL;
END;

SELECT m.pk_1
      ,m.pk_2
      ,m.fld_3
      ,get_id(m.pk_1) c_pk_1
FROM   master_tab m
WHERE  m.pk_1 = '018'
AND    m.pk_2 = 'value';
于 2013-04-03T14:29:25.387 回答