select START_TIME, count(*)
from
(select to_char(START_TIME, 'MON-YY') START_TIME from MyTable)
group by START_TIME;
我正在使用上面的查询,它返回数据库中存在的所有月份数据的计数 我需要在日期之间限制它,并且该日期应该由用户输入。
任何人都可以帮我构建并纠正上述查询吗?
答案 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;
语法可能会有所不同,具体取决于您用于获取数据的工具。
<强> [编辑] 强>
这是基于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');