如何在以下查询中的日期之间做

时间:2017-12-29 07:56:53

标签: sql oracle

select START_TIME, count(*) 
from 
(select to_char(START_TIME, 'MON-YY') START_TIME from MyTable)
group by START_TIME;

我正在使用上面的查询,它返回数据库中存在的所有月份数据的计数 我需要在日期之间限制它,并且该日期应该由用户输入。

任何人都可以帮我构建并纠正上述查询吗?

3 个答案:

答案 0 :(得分:1)

由于您使用的是SQL * Plus,因此您可以将范围日期作为替换变量提供,但是您需要将字符串提供的任何内容转换为日期(或时间戳,因为这似乎是基于报告的错误的列数据类型在其他评论中)。

如果您将日期作为SQL * Plus处理的一部分,则可以提示输入值:

accept date_from date format YYYY-MM prompt 'Enter date-from as YYYY-MM: '
accept date_to date format YYYY-MM prompt 'Enter date-to as YYYY-MM: '

select to_char(START_TIME, 'MON-YY') as START_TIME, count(*) 
from MyTable
where START_TIME >= to_timestamp('&date_from', 'YYYY-MM') 
and START_TIME < to_timestamp('&date_to', 'YYYY-MM') + interval '1' month
group by to_char(START_TIME, 'MON-YY');

即,在开始月的第一天午夜或之后的所有内容(这是默认情况下使用该格式掩码获得的),以及在该月的第一天之后的午夜之前 em>月末。

如果要从shell脚本将日期字符串传递给SQL * Plus,则可以跳过accept并使用位置替换变量:

select to_char(START_TIME, 'MON-YY') as START_TIME, count(*) 
from MyTable
where START_TIME >= to_timestamp('&1', 'YYYY-MM')
and START_TIME < to_timestamp('&2', 'YYYY-MM') + interval '1' month
group by to_char(START_TIME, 'MON-YY');

并将其命名为

sqlplus user/pass@db @script.sql "$date_rom" "$date_to"

这些shell变量的格式是预期的。

无论哪种方式,如果您传入参数“2017-11”和“2017-12”,则查询条件将变为:

where START_TIME >= to_timestamp('2017-11', 'YYYY-MM')
and START_TIME < to_timestamp('2017-12', 'YYYY-MM') + interval '1' month

并且使用带有该格式掩码的to_timestamp()to_date()会在指定月份的第一天产生午夜,相当于:

where START_TIME >= timestamp '2017-11-01 00:00:00.000'
and START_TIME < timestamp '2017-12-01 00:00:00.000' + interval '1' month

where START_TIME >= timestamp '2017-11-01 00:00:00.000'
and START_TIME < timestamp '2018-01-01 00:00:00.000'

涵盖您感兴趣的两个月内所有可能的日期和时间。

您可以使用between,但使用简单形式:

where START_TIME between to_timestamp('&date_from', 'YYYY-MM') 
  and to_timestamp('&date_to', 'YYYY-MM')

变为

where START_TIME between timestamp '2017-11-01 00:00:00.000'
  and timestamp '2017-12-01 00:00:00.000'

错过了最后一个月的任何数据,除了第一天的午夜。您可以尝试选择该月的最后一天:

where START_TIME between to_timestamp('&date_from', 'YYYY-MM') 
  and last_day(to_timestamp('&date_to', 'YYYY-MM'))

成为

where START_TIME between timestamp '2017-11-01 00:00:00.000'
  and timestamp '2017-12-31 00:00:00.000'

几乎整个最后一天仍然错过了。您可以更进一步,将结束范围值调整为23:59:59或更好23:59:59.999999999,但它很混乱,容易出错。

对于日期和时间戳,使用>=<范围而不是between通常更简单,更安全,除非 - 也许 - 您知道所有时间都设置为午夜,或者它们只是一个月的第一天,或者您可以依赖的其他一些受控条件。情况并非如此。

另一个问题。你用字符串查询和分组,所以如果你按顺序排序,你会在'NOV-17'之前得到'DEC-17',除了计数之外你不能通过其他任何东西订购而不是将它添加到通过...分组。如果您想按时间顺序排序结果,可以按the first day of each month as a date分组,而不是字符串,只需在最后一刻格式化:

select to_char(trunc(START_TIME, 'MM'), 'MON-YY') as START_TIME, count(*) 
from MyTable
where START_TIME >= to_timestamp('&1', 'YYYY-MM')
and START_TIME < to_timestamp('&2', 'YYYY-MM') + interval '1' month
group by trunc(START_TIME, 'MM')
order by trunc(START_TIME, 'MM');

也许值得一提的是MON日期格式元素依赖于您的NLS设置;转换函数有一个可选的第三个参数,允许您指定要使用的日期语言,因此无论用户的会话设置如何,您都可以强制它始终以某种语言显示。或者您可以在SQL脚本中使用alter session,或在shell脚本中设置NLS_LANG ...

答案 1 :(得分:0)

包含WHERE子句,例如

select START_TIME, count(*) 
from (select to_char(START_TIME, 'MON-YY') START_TIME 
      from MyTable
      where start_time between &TIME_FROM and &TIME_TO   --> this
     )
group by START_TIME;

语法可能会有所不同,具体取决于您用于获取数据的工具。

  • &amp; TIME_FROM适用于SQL * Plus
  • :TIME_FROM适用于TOAD,Forms,Apex 等

<强> [编辑]

这是基于Scott的EMP表的示例。

如果将以下查询存储到.SQL脚本中(让我们称之为P.SQL):

select to_char(hiredate, 'yyyy-mm') hiredate, count(*)
from emp
where to_char(hiredate, 'yyyy-mm') between '&1' and '&2'
group by to_char(hiredate, 'yyyy-mm')
order by 1;

然后您通过在SQL脚本名称后面传递两个参数(在WHERE子句中注意&amp; 1和&amp; 2)来从操作系统命令提示符(在我的情况下为MS Windows)中调用它:

M:\>sqlplus scott/tiger@orcl @p.sql 1981-05 1981-12

SQL*Plus: Release 11.2.0.1.0 Production on Pet Pro 29 10:10:40 2017


old   3: where to_char(hiredate, 'yyyy-mm') between '&1' and '&2'
new   3: where to_char(hiredate, 'yyyy-mm') between '1981-05' and '1981-12'

HIREDAT   COUNT(*)
------- ----------
1981-05          1
1981-06          1
1981-09          2
1981-11          1
1981-12          2

SQL>

有关更多有用信息,请阅读Alex在答案中所写的内容。

答案 2 :(得分:0)

您不需要任何子查询:

select to_char(START_TIME, 'MON-YY') as START_TIME, count(*) 
from MyTable
WHERE START_TIME BETWEEN &date_1 AND &date_2
group by to_char(START_TIME, 'MON-YY');