【问题标题】:Oracle: Get data on all months, 0 if no dataOracle:获取所有月份的数据,如果没有数据,则为 0
【发布时间】:2013-07-22 16:57:34
【问题描述】:

我有一个 Mview,它通过 idNumberMonth 带来数据组。因此,如果特定月份没有数据,我想显示 0。这是我的查询:

select MonthName, myCost, myNumber 
  from
     (
       select MONTH mm, myCost, myNumber
         from myOracle_mv
     ) myTotals,
     (
       select to_char(date '2012-12-1' + numtoyminterval(level,'month'), 'mm') MonthName
        from dual
      connect by level <= 12
     ) ALLMONTHS
where mm = MonthName

所以我期待:

Month Number Data
-----------------------
1     abc123 4444
2     0
3     abc123 4444
4     abc123 4444
5     0
6     abc123 4444
7     abc123 4444
8     0
9     abc123 4444
10    abc123 4444
11    0
12    abc123 4444

相反,我仍然得到:

1    abc123  4444
3    abc123  4444
4    abc123  4444
6    abc123  4444
7    abc123  4444
9    abc123  4444
10   abc123  4444
12   abc123  4444

有什么想法吗?

谢谢!

编辑:感谢您的回答。我的查询中确实有外连接,但忘记输入,因为我正集中精力更改表/列的名称。

所以是的,我已经尝试了 OUTER JOIN,但仍然没有得到预期的结果。非常感谢任何反馈。

编辑:这是 myOracle_MV 上的数据:

3777.24     AAA 1   2012
49973.12    AAA 2   2012
4049.91     AAA 3   2012
469.485     AAA 4   2012
5872.22     AAA 5   2012
65837.71    AAA 6   2012
566.23      AAA 7   2012
18432.95    AAA 8   2012
4337.75     AAA 12  2011
18811       BBB 1   2012
29872.67    BBB 2   2012
29068.55    BBB 3   2012
264957.8    BBB 4   2012
67673       BBB 5   2012
855.02      BBB 6   2012
5226.1      BBB 7   2012
2663.24     BBB 8   2012
5490.58     BBB 12  2011
3845.47     CCC 1   2012
3050.54     CCC 2   2012
3784.44     CCC 3   2012
799.73      CCC 4   2012
124884.2    CCC 5   2012
5157.24     CCC 6   2012
19184.78    CCC 7   2012
2280.05     CCC 8   2012
107.07      DDD 3   2012
181.78      DDD 4   2012
110.09      DDD 5   2012
18016.19    DDD 6   2012
1772.95     DDD 7   2012
63.32       DDD 8   2012

【问题讨论】:

  • 外连接会得到什么结果;您是否以正确的方式进行操作(在这种情况下是右外,而不是左外)?
  • 嗨@AlexPoole 我已经尝试了这里发布的所有建议,他们发布的方式,更改了连接顺序,表格顺序等。什么都没有。我真的不明白为什么它不起作用。我的 myOracle_mv 是一个 MView,这会有所不同吗?
  • 你可以在 SQL Fiddle 上看到这些工作; your originalschurik's answerJustin's answer。所以有些事情你没有告诉我们。 where 子句中是否还有其他破坏外连接的内容?这在使用 Justin 的 ANSI 语法时最为明显。不,它是一个 MV 是无关紧要的,它就像一张桌子一样对待,这是重点的一部分。
  • @AlexPoole 他需要为每个 myNumber 值重复 12 个月(AAA 为 12 次/月,BBB 为另一个...等),我猜每年的值需要 12 个月.检查我的答案的评论,@user1270446 如果我错了,请纠正我
  • @AlexPoole 感谢您抽出宝贵时间 Alex。我从表中发布数据。现在我没有在查询中添加任何代码,虽然我以后可能会这样做。但贾法尔在查看数据后是正确的。我真的认为这并不重要,因为我只需要将月份设为 0。我在 MS SQL 中有一个类似的查询,我将系统中的月份放入 TEMP 表中并加入它,它就可以工作了。所以我在 Oracle 中尝试了类似的工作。谢谢!

标签: oracle left-join


【解决方案1】:

与现有答案非常相似,但是:

select months.month, mv.mycost, coalesce(mv.mynumber, 0) as mynumber
from (
  select to_char(date '1970-01-01'
    + numtoyminterval(level - 1, 'month'), 'mm') as month
  from dual
  connect by level <= 12) months
left join myoracle_mv mv
on mv.month = months.month
order by months.month, mv.mycost, mv.mynumber;

用你发布的数据给出这个:

MONTH MYCOST   MYNUMBER
----- ------ ----------
01    AAA       3777.24 
01    BBB         18811 
01    CCC       3845.47 
02    AAA      49973.12 
02    BBB      29872.67 
02    CCC       3050.54 
03    AAA       4049.91 
03    BBB      29068.55 
03    CCC       3784.44 
03    DDD        107.07 
04    AAA       469.485 
04    BBB      264957.8 
04    CCC        799.73 
04    DDD        181.78 
05    AAA       5872.22 
05    BBB         67673 
05    CCC      124884.2 
05    DDD        110.09 
06    AAA      65837.71 
06    BBB        855.02 
06    CCC       5157.24 
06    DDD      18016.19 
07    AAA        566.23 
07    BBB        5226.1 
07    CCC      19184.78 
07    DDD       1772.95 
08    AAA      18432.95 
08    BBB       2663.24 
08    CCC       2280.05 
08    DDD         63.32 
09                    0 
10                    0 
11                    0 
12    AAA       4337.75 
12    BBB       5490.58 

 35 rows selected

如果您希望在 mynumber 列中出现零,那么您可以这样做:

select months.month, mv.mycost, coalesce(mv.mynumber, 0) as mynumber

给出:

...
08    DDD         63.32 
09                    0 
10                    0 
11                    0 
12    AAA       4337.75 
...

从 Jafar 的回答中的 cmets 看来,您自己可能已经做到了这一点,但您希望所有月份的所有 mycost 值都为零。如果是这种情况,那么您需要获取mycost 的可能值列表,并对其进行外部连接。这将采用 MV 中已经存在的所有值:

select months.month, costs.mycost, coalesce(mv.mynumber, 0) as mynumber
from (
  select to_char(date '1970-01-01'
    + numtoyminterval(level - 1, 'month'), 'mm') as month
  from dual
  connect by level <= 12) months
cross join (
  select distinct mycost
  from myoracle_mv) costs
left join myoracle_mv mv
on mv.month = months.month
and mv.mycost = costs.mycost
order by months.month, costs.mycost, mv.mynumber;

并给出:

MONTH MYCOST   MYNUMBER
----- ------ ----------
01    AAA       3777.24 
01    BBB         18811 
01    CCC       3845.47 
01    DDD             0 
02    AAA      49973.12 
02    BBB      29872.67 
02    CCC       3050.54 
02    DDD             0 
03    AAA       4049.91 
03    BBB      29068.55 
03    CCC       3784.44 
03    DDD        107.07 
04    AAA       469.485 
04    BBB      264957.8 
04    CCC        799.73 
04    DDD        181.78 
05    AAA       5872.22 
05    BBB         67673 
05    CCC      124884.2 
05    DDD        110.09 
06    AAA      65837.71 
06    BBB        855.02 
06    CCC       5157.24 
06    DDD      18016.19 
07    AAA        566.23 
07    BBB        5226.1 
07    CCC      19184.78 
07    DDD       1772.95 
08    AAA      18432.95 
08    BBB       2663.24 
08    CCC       2280.05 
08    DDD         63.32 
09    AAA             0 
09    BBB             0 
09    CCC             0 
09    DDD             0 
10    AAA             0 
10    BBB             0 
10    CCC             0 
10    DDD             0 
11    AAA             0 
11    BBB             0 
11    CCC             0 
11    DDD             0 
12    AAA       4337.75 
12    BBB       5490.58 
12    CCC             0 
12    DDD             0 

 48 rows selected 

但希望您有另一个表来保存可能的 mycost 值(假设它代表诸如成本中心之类的东西,而不是价格;有点难以分辨是什么)并且您可以使用它而不是子查询。

SQL Fiddle.

另请注意,如果您想添加过滤器,例如要将数据限制在特定年份,您需要在 left join 子句中执行此操作,而不是作为 where 子句,否则您会将外部联接恢复为内部联接。例如adding this:

where mv.year = 2011

意味着你只得到了两行:

MONTH MYCOST   MYNUMBER
----- ------ ----------
12    AAA       4337.75 
12    BBB       5490.58 

但是如果你made than another condition on the outer join 你仍然会得到 48 行,其中 46 行是零,两个是上面的值:

...
left join myoracle_mv mv
on mv.month = months.month
and mv.mycost = costs.mycost
and mv.year = 2011
order by months.month, costs.mycost, mv.mynumber;

...
11    CCC             0 
11    DDD             0 
12    AAA       4337.75 
12    BBB       5490.58 
12    CCC             0 
12    DDD             0 

 48 rows selected 

【讨论】:

    【解决方案2】:

    您需要在两个内联视图之间进行外部连接

    select MonthName, myCost, myNumber 
      from (select MONTH mm, myCost, myNumber
              from myOracle_mv
           ) myTotals
           right outer join
           (select to_char(date '2012-12-1' + numtoyminterval(level,'month'), 'mm') MonthName
              from dual
           connect by level <= 12) ALLMONTHS
           on( myTotals.mm = allmonths.MonthName )
    

    您也可以使用旧的 Oracle 特定 (+) 语法进行外连接,但我通常建议使用 SQL 标准语法。

    【讨论】:

    • 您好,感谢您的回复。抱歉,我有外部连接的代码,但没有发布。我仍然得到相同的结果。它不会返回所有月份的空值。
    • @user1270446 - 请编辑您的帖子以获取您正在使用的实际 SQL 语句。您是说尽管标记了“左连接”问题并完全从您的问题中省略了外连接,但您一直在使用右外连接?
    【解决方案3】:

    可能是这样的

    select MonthName, COALESCE(myCost,0), myNumber 
      from
         (
           select to_char(date '2012-12-1' + numtoyminterval(level,'month'), 'mm') MonthName
            from dual
          connect by level <= 12
         ) ALLMONTHS LEFT OUTER JOIN
         (
           select MONTH mm, myCost, myNumber
             from myOracle_mv
         ) myTotals ON 
        mm = MonthName
    

    【讨论】:

    • 谢谢。不,仍然没有按预期获得数据。
    • 你得到了什么?我在我的数据库中测试了这个查询,当第二个查询没有匹配的月份时,它确实返回 0
    • 嗨,不返回 0 数据行。最后我得到:09 0 NULL 10 0 NULL 11 0 Null
    • 您的myOracle_mv 视图每月是否包含多条记录?您是否希望为每个可能的myNumber 返回 12 个月/记录? (例如,您提供的样本输出中的 AAA 和 BBB)
    • 您好 Jafar,myOracle_mv 最多返回 4 个不同的数字,每个最多 12 个月。让我们看看是否超过一年。
    【解决方案4】:

    您需要一个外连接((+) 在您的查询末尾):

    select MonthName, myCost, myNumber from
    (
    select
    MONTH mm, myCost, myNumber
    from
    myOracle_mv
    ) myTotals,
    (
    select
    to_char(date '2012-12-1' + numtoyminterval(level,'month'), 'mm') MonthName
    from
    dual
    connect by level <= 12
    )ALLMONTHS
    where mm = MonthName(+)
    

    对于您的示例,您不需要计算日期:

     select MONTH mm, NVL(myCost, 0), myNumber
     from
        (select level from dual connect by level <= 12) NUM
        left outer join  myOracle_mv MV  ON ( MV.MONTH  = NUM.level )
    ;
    

    【讨论】:

    • 您好,第一个查询仍然没有返回空月份的空数据。第二个查询给了我一个错误和第一个。
    • ORA-01747: 无效的 user.table.column、table.column 或列规范 01747。00000 - “无效的 user.table.column、table.column 或列规范” *原因:*行动:行错误:177 列:75 这是第二个查询的错误。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-16
    • 2021-10-10
    • 1970-01-01
    相关资源
    最近更新 更多