组合不同的SQL查询

时间:2013-02-05 07:37:30

标签: sql oracle

我有两个问题,如下所示。查询选择相同的表,但具有不同的条件和列数。我无法解决如何将这些结合起来;有什么办法吗?

第一个查询是:

SELECT b.centerBranchNumber,
       b.countryCode,
       a.type,
       SUM(DECODE(a.status, 1, 1, 0)) filled
  FROM tableA a,
       tableB b
 WHERE a.branchCode = b.branchCode
   AND a.registerNumber IN (SELECT c.registerNumber
                              FROM tableC c
                             WHERE c.registerDate <= '01.02.2013'
                               AND c.state = 'A'
                               AND c.contractState != 2)
 GROUP BY b.centerBranchNumber,
          b.countryCode,
          a.type;

结果如下:

BranchNumber     CountryCode    Type Filled
1452                 USA          0    5
1452                 USA          2    8
1452                 USA          1    16
7854                 GER          0    10
7854                 GER          1    3

第二个查询是:

SELECT b.centerBranchNumber,
       b.countryCode,
       a.type,
       SUM(DECODE(a.status, 0, 1, 0)) free, 
       SUM(DECODE(a.status, 2, 1, 0)) damaged    
  FROM tableA a,
       tableB b  
 WHERE a.kks_kayitdrm = 'A'    
   AND a.branchCode = b.branchCode    
   AND a.recordDate <= '01.02.2013'  
 GROUP BY b.centerBranchNumber,
           a.type,
           b.countryCode;

结果是:

BranchNumber     CountryCode    Type Free     Damage
1452                 USA          0    5        2  
1452                 USA          2    8        1  
1452                 USA          1    16       5
7854                 GER          0    10       9  
7854                 GER          1    3        16

如果我合并,我希望结果如下:

BranchNumber     CountryCode    Type   Filled    Free     Damage
1452                 USA          0      1         5        2  
1452                 USA          2      2         8        1  
1452                 USA          1      65        16       5
7854                 GER          0      7         10       9  
7854                 GER          1      45        3        16   

1 个答案:

答案 0 :(得分:1)

您的第一个查询可以重写如下:

SELECT b.centerBranchNumber,
       b.countryCode,
       a.type,
       SUM(DECODE(a.status, 1, 1, 0)) filled
  FROM tableA a
  JOIN tableB b
    ON a.branchCode = b.branchCode
  JOIN tableC 
    ON a.registerNumber = c.registerNumber
 WHERE c.registerDate <= '01.02.2013'
   AND c.state = 'A'
   AND c.contractState != 2
 GROUP BY b.centerBranchNumber,
          b.countryCode,
          a.type

将您的第二个转换为ANSI语法(它会帮助处理更复杂的查询,因为当您做错了事情时它非常明显)它会变成这样:

SELECT b.centerBranchNumber,
       b.countryCode,
       a.type,
       SUM(DECODE(a.status, 0, 1, 0)) free, 
       SUM(DECODE(a.status, 2, 1, 0)) damaged    
  FROM tableA a
  JOIN tableB b
    ON a.branchCode = b.branchCode      
 WHERE a.kks_kayitdrm = 'A'
   AND a.recordDate <= '01.02.2013'  
 GROUP BY b.centerBranchNumber,
           a.type,
           b.countryCode

正如您所看到的,两个查询之间存在大量相似性。为了获得您想要的结果集,有两种方法可以实现。将不相似的条件移动到SUM中的CASE语句中。或者,您可以将两个查询联合起来并再次求和。我宁愿使用CASE声明,因为你减少了你必须做的工作量;缺点是查询变得不那么容易阅读。

您的查询的常见部分是:

SELECT b.centerBranchNumber,
       b.countryCode,
       a.type
  FROM tableA a
  JOIN tableB b
    ON a.branchCode = b.branchCode 
 GROUP BY b.centerBranchNumber,
          a.type,
          b.countryCode

以此为基础,您可以慢慢添加它,直到获得相同的结果。您必须记住将相同的条件添加到WHERE / JOINS以确保获得相同的结果。

如果我们先添加所有条件;你必须在第一个查询中将tableC上的JOIN更改为LEFT OUTER JOIN。这与WHERE子句将限制结果集完全相同,就好像它是一个INNER JOIN。

SELECT b.centerBranchNumber,
       b.countryCode,
       a.type
  FROM tableA a
  JOIN tableB b
    ON a.branchCode = b.branchCode
  LEFT OUTER JOIN tableC 
    ON a.registerNumber = c.registerNumber
 WHERE c.registerDate <= '01.02.2013'
   AND c.state = 'A'
   AND c.contractState != 2
 GROUP BY b.centerBranchNumber,
          b.countryCode,
          a.type

在第二个查询中添加条件,您现在需要在WHERE子句中使用OR:

SELECT b.centerBranchNumber,
       b.countryCode,
       a.type
  FROM tableA a
  JOIN tableB b
    ON a.branchCode = b.branchCode
  LEFT OUTER JOIN tableC 
    ON a.registerNumber = c.registerNumber
 WHERE ( c.registerDate <= '01.02.2013'
         AND c.state = 'A'
         AND c.contractState != 2 
             )
    OR ( a.kks_kayitdrm = 'A'
         AND a.recordDate <= '01.02.2013' 
             )
 GROUP BY b.centerBranchNumber,
          b.countryCode,
          a.type

接下来,你的日期似乎有点奇怪。您似乎没有将字符串文字'01.02.2013'转换为日期,因为它不是YYYYMMDD形式,您的比较将无法正常工作。您依赖于DD.MM.YYYY格式为WHERE ( c.registerDate <= to_date('01.02.2013', 'dd.mm.yyyy') 的{​​{1}},您无法保证每次会话。要比较日期,您应该使用内置的NLS_DATE_FORMAT函数以及相应的TO_DATE()或ANSI format model进行显式转换,因为我在datetime literal中进行了更全面的解释

我只想说Oracle this answer因为:

  
      
  • 使用显式数据类型转换函数时,SQL语句更容易理解。

  •   
  • 隐式数据类型转换可能会对性能产生负面影响,尤其是当列值的数据类型转换为常量的数据类型而不是相反时。

  •   
  • 隐式转换取决于它发生的上下文,并且在每种情况下可能无法以相同的方式工作。例如,从datetime值到VARCHAR2值的隐式转换可能会返回意外的年份,具体取决于NLS_DATE_FORMAT的值   参数。

  •   
  • 隐式转换的算法可能会在软件版本和Oracle产品之间发生变化。显式转换的行为更具可预测性。

  •   

为了比较您的日期,我建议您使用TO_DATE(),例如:

registerNumber

最后我们可以添加SUMs;这意味着重复您之前的条件。您可以为第一个查询简化此操作,因为某些内容是否已“填充”仅取决于LEFT OUTER JOIN,这意味着如果该表中的SELECT b.centerBranchNumber , b.countryCode , a.type , sum(case when a.status = 1 and c.registernumber is not null then 1 else 0 end) as filled , sum(case when a.status = 0 and a.kks_kayitdrm = 'A' and a.recordDate <= to_date('01.02.2013', 'dd.mm.yyyy') then 1 else 0 end) as free , sum(case when a.status = 2 and a.kks_kayitdrm = 'A' and a.recordDate <= to_date('01.02.2013', 'dd.mm.yyyy') then 1 else 0 end) as damaged FROM tableA a JOIN tableB b ON a.branchCode = b.branchCode LEFT OUTER JOIN tableC ON a.registerNumber = c.registerNumber WHERE ( c.registerDate <= to_date('01.02.2013', 'dd.mm.yyyy') AND c.state = 'A' AND c.contractState != 2 ) OR ( a.kks_kayitdrm = 'A' AND a.recordDate <= to_date('01.02.2013', 'dd.mm.yyyy') ) GROUP BY b.centerBranchNumber , b.countryCode , a.type 在JOIN中不为null,则条件为true

在这两种情况下,我使用CASE语句而不是DECODE;这是因为CASE功能更强大,并强制执行正确的数据类型explicitly recommends against using implicit conversion

将所有这些放在一起成为您的最终查询:

{{1}}

您可以更通用地使用此方法在适当的位置组合查询。