Oracle SQL Developer vs Spring Query - 子选择问题

时间:2015-09-18 12:24:49

标签: sql oracle spring-jdbc

我有以下SQL查询在Oracle SQL开发人员中正确运行,但是当它通过标准的java spring JDBCTemptate查询执行时,它会失败。

SELECT  
  REPLACE(:RUN_DATE,'-','/') as rep_day,
  9,
  'BILLING', 
  ROUND(x.duration_minutes),
  ROUND(x.duration_minutes),
  '(' || bc.description || ') ' || x.total_ba || ' Accounts; ' || (case when (x.duration_minutes/60) > 1 then FLOOR(x.duration_minutes/60)  ||'h'  end) || round(mod(x.duration_minutes,60)) || 'min'
FROM
  (SELECT bt.bill_cycle_id,
    bt.invoice_period,
    bt.invoice_period_end_date,
    sum((bt.execution_end - bt.execution_start)*24*60) AS duration_minutes,
    sum((
    CASE
      WHEN bt.task = 'BILL'
      THEN bt.number_of_accounts 
      else 0
    end))AS total_ba,
    min(trunc(bt.execution_start)) as start_date    
  FROM billing.billing_tasks bt
  WHERE bt.type                     = 'REAL_BILL_RUN'
  AND bt.status                     = 'COMP'
  AND trunc(bt.execution_start) = (SELECT to_date(MAX(START_DATE),'dd-mm-yyyy') FROM BILLING.BILL_RUNS WHERE TYPE='REAL_BILL_RUN')
  group by  bt.bill_cycle_id,
    bt.invoice_period,
    bt.invoice_period_end_date
  ) x
INNER JOIN billing.bill_cycles bc
ON bc.id = x.bill_cycle_id

java代码是

import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

import javax.annotation.Resource;
import javax.sql.DataSource;

@Override
public List<KpiData> runQuery(String runDate) throws Exception {
    NamedParameterJdbcTemplate jdbcTemplate = new NamedParameterJdbcTemplate(oracleDataSource);
    Map map = new HashMap<>();
    map.put(GenerateDailyReport.RUN_DATE, runDate);
    List<KpiData> kpis = jdbcTemplate.query(sql, map, new KpiDataRowMapper());
    return kpis;
}

我知道问题在于'trunc(bt.execution_start)'子句。原始日期子句是这样的,其中:RUN_DATE是参数

AND trunc(bt.execution_start) = to_date(:RUN_DATE,'dd-mm-yyyy');

但我已更新查询以获取最近的帐单运行日期。

AND trunc(bt.execution_start) = (SELECT to_date(MAX(START_DATE),'dd-mm-yyyy') FROM BILLING.BILL_RUNS WHERE TYPE='REAL_BILL_RUN')

有人发现导致此失败的SQL问题吗?

2 个答案:

答案 0 :(得分:0)

在子选择中而不是使用&#39; to_date&#39;

AND trunc(bt.execution_start) = (SELECT to_date(MAX(START_DATE),'dd-mm-yyyy') FROM BILLING.BILL_RUNS WHERE TYPE='REAL_BILL_RUN')

我刚才打电话给&#39; trunc()&#39;将现有日期类型字段START_DATE转换为与左侧操作符相同的格式&#39; trunc(bt.execution_start)&#39;。

答案 1 :(得分:0)

我很高兴您找到了解决问题的方法。

有趣的是,你的回答提供了关于你的原始查询无效的原因的缺失线索,我认为解释它是非常有用的。

原帖中不清楚的是start_date已经是date字段。既然如此,在to_date()字段上调用date是没有意义的。 to_date()只应用于将字符串转换为日期,而不是日期转换为日期。

事实上,尝试在日期值上调用to_date通常会导致非常不直观的结果,这就是您所经历的。

让我们假装max(start_date)Sept 21st, 2015的一秒钟。不管你信不信,做to_date(max(start_date), 'dd-mm-yyyy')会给你Sept 21st, 0015(不是2015)。尝试此查询以确认奇怪的行为:

select to_char(to_date(max(start_date), 'dd-mm-yyyy'), 'YYYY-MM-DD')
  from BILLING.BILL_RUNS
 where TYPE='REAL_BILL_RUN'

你到了什么日期?不是你的期望?

之所以发生这种情况,是因为to_date()需要一个字符串参数。因此,当您传递日期时,它需要在调用to_date()函数之前使用默认日期格式将您的日期隐式转换为字符串。换句话说:

to_date(max(start_date), 'dd-mm-yyyy')

......真的变成了这个:

to_date(to_char(max(start_date)), 'dd-mm-yyyy') -- notice the to_char()

如果是Sept 21, 2015之类的日期,则to_char()函数可能会返回21-SEP-15之类的内容(注意2位数年份)。

所以,从逻辑上讲,尝试解析一个2位数年份的字符串,同时指定4位数的年份格式,如下所示:

to_date('21-SEP-15', 'dd-mm-yyyy')

...将导致0015年而不是2015。不好。

总结: 永远不会将日期参数传递给对to_date() 的调用。