【问题标题】:SELECTING data with CASE statement issue选择带有 CASE 语句问题的数据
【发布时间】:2016-11-22 11:10:39
【问题描述】:

我目前正在尝试制定一个查询来确定是否在所述主机上安装了某些软件应用程序。我们在数据库中的表的设置方式是包含包含主机名和软件的列,因此例如可能有 10 条主机 A 记录,其中展示了在其上找到的 10 个单独的软件。前任。下面

ID   Host          SW        
--------------------------
1    Host A        SW A
1    Host A        SW B     
1    Host A        SW C
2    Host B        SW B
2    Host B        SW C
3    Host C        SW F

我要查找的是所有安装了软件 A(标志 Y/N)和软件 B(标志 Y/N)的主机,并以某种方式(使用上面的示例)格式化结果会回来...

Expected Results    

Host          SWA_Installed      SWB_Installed
----------------------------------------------
Host A        Y                  Y
Host B        N                  Y
Host C        N                  N

下面的查询是我目前所拥有的(注意实际的 DB 代码,这些是实际的 SW 名称)...

SELECT DISTINCT
 HOST.a_display_label AS HOST,
 CASE 
  WHEN 
   sof.a_display_label = 'HPS' THEN 'Y' 
  ELSE 'N' 
 END AS HPS_INSTALLED,
 CASE 
  WHEN 
   sof.a_display_label = 'PowerToken' THEN 'Y' 
  ELSE 'N' 
 END AS PT_INSTALLED
FROM vcms.node_1 HOST
  JOIN vcms.installed_software_1 sof
    ON LOWER (sof.a_root_container) = LOWER (HOST.cmdb_id)

问题在于,即使它获取了 DISTINCT 结果,它仍然会返回多个主机记录,因为它上面安装的每个软件都有多个主机记录。最终结果如下所示:

Query Results

Host          SWA_Installed      SWB_Installed
----------------------------------------------
Host A        Y                  N
Host A        N                  Y
Host A        N                  N
Host B        N                  N
Host B        N                  Y
Host C        N                  N

我想知道返回“预期结果”数据需要哪些额外信息。提前谢谢你。

【问题讨论】:

  • 这意味着您的源数据中有多行,而预期结果中的每一行。以Host A 为例,至少一排只有 SWA_Installed,至少一排只有 SWB_Installed,至少一排两者都没有。尝试SELECT * 而不是您的查询,您应该会在数据中看到所有这些不同的组合。那么问题就变成了,如果 HostA 的某些行说“是”而某些“行”说“否”,该怎么办?是应该超越否吗?如果是这样,您需要GROUP BY Host.a_display_labelMAX(CASE blah END),而不是DISTINCT

标签: sql oracle select join case


【解决方案1】:

只需使用条件聚合:

select h.a_display_label as host,
       max(case when s.a_display_label = 'HPS' then 'Y' else 'N'
           end) as hps_installed,
       max(case when s.a_display_label = 'PowerToken' then 'Y' else 'N'
           end) as pt_installed
from vcms.node_1 h join
     vcms.installed_software_1 s
     on lower(s.a_root_container) = lower(h.cmdb_id)
group by h.a_display_label;

注意:这取决于'Y' > 'N' 的事实,因此该逻辑不适用于min() 而不是max()。我通常只使用“1”表示是,“0”表示否,而不是字符。

我还应该评论说,我发现带有聚合函数的 SELECT DISTINCT 是一个非常尴尬的构造。对于聚合查询,您真的应该考虑 GROUP BY 而不是 SELECT DISTINCT

【讨论】:

  • 太棒了!感谢 Gordon 的快速响应和帮助
【解决方案2】:

您需要在 Host 列上进行聚合。在case 表达式上使用maxmin,因为任何一个结果都与case 表达式每行返回一个值相同。

SELECT 
 HOST.a_display_label AS HOST,
 MAX(CASE 
  WHEN 
   sof.a_display_label = 'HPS' THEN 'Y' 
  ELSE 'N' 
 END) AS HPS_INSTALLED,
 MAX(CASE 
  WHEN 
   sof.a_display_label = 'PowerToken' THEN 'Y' 
  ELSE 'N' 
 END) AS PT_INSTALLED
FROM vcms.node_1 HOST
  JOIN vcms.installed_software_1 sof
    ON LOWER (sof.a_root_container) = LOWER (HOST.cmdb_id)
GROUP BY HOST.a_display_label

仅当基于join 条件的每个a_display_label 有一行时,结果才是可靠的。否则你仍然会得到多行并且在这种情况下聚合是不正确的。

【讨论】:

    【解决方案3】:

    我只是意识到我的答案不如其他答案有效,仅供参考。

    注意这是 sql server,但同样适用于 oracle

    SQL FIDDLE DEMO

    SELECT  [Host],
            CASE WHEN 
                 COUNT( CASE WHEN [SW] = 'SW A' THEN 1 END)  >= 1 THEN 'YES' 
                                                                  ELSE 'NO' 
            END as sw_a,
            CASE WHEN 
                 COUNT( CASE WHEN [SW] = 'SW B' THEN 1 END)  >= 1 THEN 'YES' 
                                                                  ELSE 'NO' 
            END as sw_b
    
    FROM hosts
    GROUP BY [Host]
    

    输出

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多