在单个 oracle sql 语句中返回多个值

Posted

技术标签:

【中文标题】在单个 oracle sql 语句中返回多个值【英文标题】:Return multiple values in a single oracle sql statement 【发布时间】:2016-11-01 20:46:38 【问题描述】:

下面是java中的查询

SELECT achieve_STATUS_CD
FROM achievemnt
WHERE env_id = '?' AND ROWNUM = 1 
ORDER BY achive_status_dt

需要根据另外两个表注册返回值'Y',提供以下条件

    enrollment.achieve_intent = '2' 注册.BEGIN_DT >= 迄今为止 ( 2011 年 5 月 1 日, 'MM/DD/YYYY') 3.provision.RELEASE_DT

对于其余所有条件,它应该返回原始查询中的值(SELECT perform_STATUS_CD FROM achievementmnt WHERE env_id = '?' AND ROWNUM = 1 ORDER BY achive_status_dt)

achievemnt 是注册的子表

这个逻辑怎么写?

我已经编写了下面的查询,但它在 2 个地方有 env_id,但我们现在无法在我们的 java 中更改它,所以我需要编写一个仅一次接受 env_id 的查询

WITH TMP
     AS (SELECT 'Y' AS achieve_STATUS_CD, env_id
           FROM enrollment r, provision a
          WHERE     r.prov_id = a.prov_id
                AND r.achieve_intent = '2'
                AND a.BEGIN_DT >=
                       TO_DATE (
                          05/01/2011,
                          'MM/DD/YYYY')
                AND a.RELEASE_DT <= SYSDATE AND R.env_ID = '?')
SELECT NVL (F.achieve_STATUS_CD,TMP.achieve_STATUS_CD)
FROM TMP FULL OUTER JOIN
     (SELECT achieve_STATUS_CD
      FROM achievemnt
      WHERE env_id = '?' AND
            ROWNUM = 1
      ORDER BY achive_status_dt
     ) F
     ON TMP.env_ID = F.env_ID;

【问题讨论】:

首先,我怀疑您的初始查询不正确。 rownum = 1 谓词将在 order by 之前应用,因此您可以选择首先遇到的任意行并对该行进行排序。您似乎可能想要第一个 achive_status_dt 行中的值。我不确定我是否理解你的其余问题。如果您只希望使用单个绑定变量的最后一个查询的逻辑,只需创建另一个 CTE,从 dual 中选择绑定值并在其余查询中尽可能多次引用该 CTE。 你能告诉我如何使用 CTE 从 dual 中选择绑定值,我可以参考 这是在 Oracle 中 【参考方案1】:

您的初始查询很可能不正确。此查询中的rownum = 1 谓词在ORDER BY 之前应用,因此您要求任意行,其中env_id 是任意绑定值,然后对生成的单行进行排序。对单行进行排序显然是没有意义的。

SELECT achieve_STATUS_CD
  FROM achievemnt
 WHERE env_id = '?' 
   AND ROWNUM = 1 
 ORDER BY achive_status_dt

如果您想对多行进行排序并选择第一个 achive_status_dt 的行,您需要类似

SELECT *
  FROM (SELECT achieve_STATUS_CD
          FROM achievemnt
         WHERE env_id = '?' 
         ORDER BY achive_status_dt) a
 WHERE a.ROWNUM = 1 

但是,如果您的实际问题是第二个查询执行您想要的操作(鉴于我刚才提到的问题,这似乎不太可能)但您想使用单个绑定变量来执行此操作,您可以简单地添加另一个 CTE 来选择dual 中的绑定变量,然后在任何你喜欢的地方引用该 CTE

WITH env
     AS (SELECT '?' env_id from dual)
    ,TMP
     AS (SELECT 'Y' AS achieve_STATUS_CD, env_id
           FROM enrollment r, provision a, env
          WHERE     r.prov_id = a.prov_id
                AND r.achieve_intent = '2'
                AND a.BEGIN_DT >=
                       TO_DATE (
                          05/01/2011,
                          'MM/DD/YYYY')
                AND a.RELEASE_DT <= SYSDATE 
                AND R.env_ID = env.env_ID)
SELECT NVL (F.achieve_STATUS_CD,TMP.achieve_STATUS_CD)
FROM TMP FULL OUTER JOIN
     (SELECT achieve_STATUS_CD
      FROM achievemnt
           join env
             on achievement.env_id = env.env_id
      WHERE ROWNUM = 1
      ORDER BY achive_status_dt
     ) F
     ON TMP.env_ID = F.env_ID;

【讨论】:

感谢贾斯汀,它成功了,我学会了如何使用上述方法只使用一次绑定变量【参考方案2】:

假设:

1- rownum 的问题是正确的(我同意 Justin)

2- 总是填充成就表

你可以解除条件

AND R.env_ID = '?'

关于 TMP 子查询。 在这种情况下,查询是基于 env_id 的 LEFT 外连接就足够了。

所以完整的查询应该变成这样(我没有sql控制台来测试它)

WITH TMP
 AS (SELECT 'Y' AS achieve_STATUS_CD, env_id
       FROM enrollment r, provision a
      WHERE     r.prov_id = a.prov_id
            AND r.achieve_intent = '2'
            AND a.BEGIN_DT >=
                   TO_DATE (
                      05/01/2011,
                      'MM/DD/YYYY')
            AND a.RELEASE_DT <= SYSDATE ),
F 
AS (SELECT TMP.achieve_STATUS_CD
  FROM achievemnt
  WHERE env_id = '?' AND
        ROWNUM = 1
  ORDER BY achive_status_dt)
SELECT NVL (TMP.achieve_STATUS_CD, F.achieve_STATUS_CD)
FROM F LEFT OUTER JOIN TMP
 ON  F.env_ID=TMP.env_ID;

请注意,我反转了 NVL 字段:如果 TMP.achieve_STATUS_CD 不为空,即如果存在一行,则应采用始终为 Y 的值,否则采用来自成就的值。

如果假设 2 不正确,您可以反转外连接中的 2 个子查询。

如果你需要最旧日期的成就记录,F可能会变成这样

SELECT TMP.achieve_STATUS_CD, achive_status_dt, env_id, min(achive_status_dt)  OVER(partition by end_id) min_date
  FROM achievemnt
  WHERE env_id = '?' AND
        achive_status_dt=min_date

【讨论】:

【参考方案3】:

这就是我对您的问题的理解(我可能完全错了):如果 env_id 显示“Y”至少存在一个符合您条件的注册/提供对,否则显示它的第一个成就状态。

select 
  case when exists
  (
    select *
    from enrollment e
    join provision p on p.prov_id = e.prov_id
    where e.achieve_intent = '2'
    and p.begin_dt >= date '2011-05-01'
    and p.release_dt <= sysdate 
    and e.env_id = :env_id
  ) then 'Y'
  else
  (
    select max(achieve_status_cd) keep (dense_rank first order by achive_status_dt)
    from achievement
    where env_id = :env_id
  ) as status
from dual;

如果您希望 :env_id 在查询中只包含一次,请将其稍微更改为:

select 
  case when exists
  (
    select ...
    and e.env_id = main.env_id
  ) then 'Y'
  else
  (
    select ...
    where env_id = main.env_id
  ) as status
from (select :env_id as env_id from dual) main;

【讨论】:

感谢 Thorsten,为我提供了替代解决方案。

以上是关于在单个 oracle sql 语句中返回多个值的主要内容,如果未能解决你的问题,请参考以下文章

如何写sql语句去掉oracle返回结果中的空值(NULL)

java中如何获取oracle存储过程返回的多个值。

oracle面试题

ORACLE存储过程里可以声明过程和函数吗

Oracle中function和procedure的区别

oracle怎么写IF语句?