我目前正在研究Oracle数据库中的一个表,其中有时间值以AM / PM格式存储。默认情况下,时间是VARCHAR2数据类型,所以我一直使用to_date函数转换它们。
我正在做的是比较表中的两个不同时间并运行一个查询,该查询应该返回该时间间隔内包含的所有时间的结果。通过使用between条件获取间隔。这是一个例子:
SELECT TIME
FROM TIME_TABLE
WHERE to_date(TIME,'HH:MI:SS PM') between
to_date('04:00:00 PM', 'HH:MI:SS PM') and
to_date('08:00:00 PM', 'HH:MI:SS PM');
问题在于,当您未使用to_date函数声明日期时,它将自动默认为当前月份的第1天。因此,如果我想从晚上11:59到凌晨12:00获得所有结果,我将得到不正确的结果。
基本上,我想以一种循环的方式来计算时间间隔,而不是像往常一样只在一个方向上进行。有没有办法实现这个目标?
答案 0 :(得分:4)
这里最大的问题是,当您添加更多数据时,事情会越来越慢。当您在字段本身上调用函数时,很难有效地使用索引。它必须扫描整个表以满足查询。有三种方法可以避免这种情况:
不要将日期或时间存储为字符串。使用正确的字段类型。这是最好的方法。
比较使用字符串而不是先转换。这只有在您保证词典排序的情况下才有效。你可以24小时这样做,但不能用12小时,因为上午/下午会妨碍你。
正如Barett在评论中启发我一样,Oracle有Function-Based Indexes的概念。您可以创建一个预先计算to_date
结果的文件,这样当您在查询中使用它时,它就会有一个可以使用的索引。虽然有几个disadvantages,所以如果你采取这种方法,我建议你仔细考虑后果。
关于在仅限时间值的午夜循环的问题,一般算法如下:
范围的起始值< =范围结束值?
如果是,则如果测试值为> =范围的开始且<结束范围。否则返回false。
如果不是,则如果测试值为> =范围的开始或<结束范围。否则返回false。
无需使用:59,或任何其他类型的值。这样做会妨碍其他事情,例如获取范围的持续时间或测试如下值:59.5。
答案 1 :(得分:1)
不是真的。您需要添加一些逻辑来检测您的标准之间的“循环”。如果有循环(endRange< startRange),则标准应为:
to_char(to_date(TIME,'HH:MI:SS PM'),'HH24:MI:SS') >= '08:00:00' // endRange
OR to_char(to_date(TIME,'HH:MI:SS PM'),'HH24:MI:SS') <= '04:00:00' // startRange
另一方面,我重申wolφi建议使用24小时的时间。否则你也会遇到AM / PM问题。
答案 2 :(得分:0)
您无法将am / pm varchar转换为24h varchar并将其与BETWEEN
进行比较吗?
SELECT TIME
FROM TIME_TABLE
WHERE to_char(to_date(TIME,'HH:MI:SS PM'),'HH24:MI:SS')
BETWEEN '04:00:00' AND '05:00:00';
答案 3 :(得分:0)
您可以测试第二次是否比第一次'更早',如果是,则将其调整到第二天。你需要以同样的方式调整表时间。使用CTE只是为了不必重复字符串值和转换:
WITH R AS (
SELECT to_date('11:59:00 PM', 'HH:MI:SS PM') start_t,
to_date('12:00:00 AM', 'HH:MI:SS PM') end_t
FROM DUAL
),
T AS (
SELECT to_date(TIME,'HH:MI:SS PM') AS TIME
FROM TIME_TABLE
)
SELECT to_char(TIME, 'HH:MI:SS PM')
FROM R
JOIN T
ON T.time + case when T.time < R.start_t then 1 else 0 end
between R.start_t and
R.end_t + case when R.end_t < R.start_t then 1 else 0 end
ORDER BY T.time + case when T.time < R.start_t then 1 else 0 end;
这不是很愉快。
希望这是一张小桌子,你不会看到Matt Johnson提到的性能问题......
您可以通过在您的范围内生成所有可能的时间并将其与实际表格作为字符串进行比较来避免这种情况:
WITH R AS (
SELECT to_date('11:10:00 PM', 'HH:MI:SS PM') start_t,
to_date('12:30:00 AM', 'HH:MI:SS PM') end_t
FROM DUAL
),
T AS (
SELECT level AS rn,
to_char(start_t
+ numtodsinterval(level - 1, 'SECOND'),
'HH:MI:SS PM') AS time
FROM R
CONNECT BY level <= 86400 *
(end_t + case when end_t < start_t then 1 else 0 end - start_t)
)
SELECT TT.time
FROM T
JOIN TIME_TABLE TT
ON TT.time = T.time
ORDER BY T.rn;
第一个CTE会阻止您再次重复该字符串。第二个使用第一个作为基础并使用CONNECT BY
查找中间的所有时间,添加开始和(调整的)第二个字符串时间之间的秒数。那包裹到第二天,但它没关系,因为它被转换回字符串并丢失了日期部分;然后将 字符串用作真实表格的过滤器。
你有24小时相同的问题,因为仍然没有日期部分,但它可能使逻辑更容易遵循。我已经做了12小时的任何事情,所以很难在午夜输入'12'......