【问题标题】:SQL to return a single value when two are sometimes presentSQL 在有时存在两个值时返回单个值
【发布时间】:2016-11-01 11:57:13
【问题描述】:

SQL 初学者

我的表包含访问要求,两种类型都保存在同一个表中。 记录可以有 'ADP''SENT'Both。 我需要返回一个值来提供给另一个查询。

当两种访问类型都存在时,我的 SELECT 查询返回 2 行,那么我如何对其进行编码以返回单个值。 因此,如果仅存在 ADP,我会得到 ADP, 如果只找到 SENT,我会收到 SENT 如果同时找到 ADP 和 SENT,我只会收到 SENT。

我的表包含很多字段,但本质上是这样的

SUA_PERUSER_ID  SUA_ACCESS_TYPE  
      10000000  ADP  
      10000001  SENT  
      10000002  ADP  
      10000002  SENT

我的查询是这样的

SELECT S_USER_ACCESS.SUA_PERUSER_ID, S_USER_ACCESS.SUA_ACCESS_TYPE  
FROM   S_USER_ACCESS  
WHERE  S_USER_ACCESS.SUA_PERUSER_ID = <Client_ID>  

&lt;client_ID&gt; 由用户输入。

【问题讨论】:

  • 您能否提供一些额外的信息?表格结构、数据示例和预期结果
  • 请向我们展示您的表格和查询。
  • 请注意:虽然通过表名来限定列名是一种很好的做法,但当查询中只有一个表时通常不会这样做。而且,为了便于阅读,您可以使用表别名(在 Oracle 中不使用关键字 AS;语法是 FROM S_USER_ACCESS S - 然后在其他子句中您可以说 S.SUA_PERUSER_IDS.SUA_ACCESS_TYPE。更容易眼睛!
  • 执行此操作的最关键部分是了解使您确定在存在多个值时返回哪个是正确值的业务规则。没有这个,任何解决方案都是猜测。它可能会返回数据,但不一定是正确的数据。

标签: sql oracle


【解决方案1】:

将您的字段称为“access_type”并将您的表称为“access”并想象字段“所有者”的存在,您可以尝试使用此脏 sql

select max(access_type) , owner from access group by owner

它有点脏,并且使用了词法上 SENT 大于 ADP 的事实

编辑 由于需要 3 个结果,它会发生很大变化:

select decode (sum(partial.cnt),1,'ADP',2,'SENT',3,'BOTH') access_type, patial.owner from (
select decode(access_type,'ADP', 1,'SENT',2,0) cnt, access_type, owner from     access ) partial
group by partial.owner

部分子查询将 access_type 转换为数字,外部查询解码结果的总和。 约束是每个“所有者”的行数不得超过 2 行。

抱歉,如果有一些 sql 错误,因为我现在没有要测试的 sql 控制台

【讨论】:

  • Massimo 这行得通,它变得多么复杂,我想要三个“合乎逻辑”的结果。一份用于 SENT,一份用于 ADP,一份用于 BOTH。
  • @RayRudd - 你是什么意思? BOTH 是列中的另一个可能值,还是如果SENTADP 都存在于sua_peruser_id,您是否要返回BOTH?这两种解释有很大不同。
  • @RayRudd 让我想想
  • 发布提案
  • @mathguy - 扩展您的示例案例语句,以便 Case SENT = 1 - Case ADP = 2 和 Case SENT & ADP = 3
【解决方案2】:

正如 Massimo 所说,您需要group by sua_peruser_id 才能获得一个值。或者,如果您只需要为一个sua_peruser_id 执行此操作,您将有一个where 子句(并且不需要group by)。

如果您不想按字典顺序使用 'SENT' &gt; 'ADP' 的意外,您可以这样做(更多代码,但出现错误的机会更少):

select sua_peruser_id, 
       max(sua_access_type) keep (dense_rank first order by
                                          case sua_access_type when 'SENT' then 1
                                                               when 'ADP'  then 2
                                          end) as sua_access_type
from s_user_access
group by sua_peruser_id

如果您有两个以上的可能值SENTADP,则更好的解决方案是将这些可能的值放在一个单独的表中,并将它们的优先级顺序放在第二列中......但也许这有点矫枉过正在您的特定用例中。

编辑 - 仍在等待 OP 澄清修改后的要求。似乎如果'SENT''ADP' 都存在,结果应该显示'BOTH' 而不是'SENT'。这可以通过聚合函数min()max() 来完成。有两个版本 - 如果'SENT''ADP' 保证每个sua_peruser_id 出现不超过一次,则更简单的版本,如果可能重复,则更复杂的版本。下面我展示了更复杂的版本,它适用于更一般的情况。

select sua_peruser_id, 
       case when min(sua_access_type) != max(sua_access_type) then 'BOTH'
            else min(sua_access_type) 
       end  as sua_access_type
from   s_user_access
where  sua_peruser_id = <customer_id>  -- (provided by user)

【讨论】:

  • 这超出了我的 sql 技能,但是当返回两条记录时,ADP 将始终满足第二个 case 语句并返回 2 而不是 1 -RR
  • @RayRudd - 不,不会。你会得到两行,一排是SENT, 1,另一排是ADP, 2。从这两个中,keep (dense_rank FIRST) 将只选择带有SENT, 1 的行。从这一行开始,max(sua_access_type) 将只是 SENT(现在是单个值的最大值!)
  • 无论如何,在你的情况下(如果它没有比你描述的更复杂的话),Massimo 的解决方案可能是最好的 - 它非常简单,并且可以由不熟悉 first 的开发人员维护和last 函数。 Massimo 的解决方案得到了我的支持!
  • OK 澄清一下 - 如果查询结果返回 SENT,那么我希望答案是 SENT,如果结果是 ADP,那么我想要 ADP,如果结果返回两行,那么我想要一个两者的答案。所以我得到一个答案作为查询的输出。
  • 好的,这正是我在答案的添加部分中所做的。我还介绍了SENT 可能有两行而ADP 没有两行的可能性。如果在您的基表中无法进行此类重复,您可以将min() != max() 的条件替换为更简单的条件,例如count(*) = 2(在case 表达式的when 条件中)。
【解决方案3】:

如果 ADP 和 SENT 是同一张表中的两列,其中一列可能为空,请使用NVL() 函数:

SELECT NVL(SENT, ADP) FROM ....

如果 SENT 不为空,则上述将评估为 SENT 的值。否则,它将评估为 ADP 的值。

【讨论】:

  • ADP 和 SENT 是列中两个可能的,有时对 ID 的查询可能会找到该 ID 的两个值。不确定如何将其解释为“两列”。
猜你喜欢
  • 2011-12-21
  • 2020-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-31
相关资源
最近更新 更多