从0开始写数据库-02-条件查询

本文介绍MyBase数据库中条件过滤的实现。先对数据进行抽象,用DBVal表示所有类型数据,表的每行数据用std::vector<DBVal>表示。又对查询条件抽象为字段、运算符、运算数,简化为字段下标、运算符、运算数,并给出接口示例,还以age > 20为例说明,其他条件实现可参考源码。

本篇介绍MyBase数据库中条件过滤的实现。您可以在 https://github.com/zhangqhn/mybase 下载到MyBase项目源码。

  1. 数据的抽象

MyBase支持整数、浮点数、字符串数据类型,在介绍条件查询之前我们需要先对数据进行抽象,目标是能用一个数据类型表示所有类型的数据。在MyBase的实现中,这个数据类型是DBVal (dbval.h) 它的定义如下:

typedef struct _DBVal
{
  int valType_; // value type , null, int, double, string
  int dataLen_; // data length
  union {
    int64_t intVal_;
    double doubleVal_;
    const char* strVal_;
  } val_;
}DBVal;

valType_ :表示当前存储的值类型,null,int,double还是string。

dataLen_ :表示数据的长度,比如:字符串的长度。

val_ : 中存储对应的值。

将数据值通过DBVal存储后,表的每一行数据就可以表示为一个DBVal的数组,在MyBase中我们将每行数据以:std::vector<DBVal> 来表示,条件查询就可以理解为判断一个std::vector<DBVal>对象是否满足给定的条件。

  1. 查询条件的抽象

在MyBase中我们简单的将条件抽象为 字段、运算符、运算数。

例如:age > 20 , name = zhangsan 等等。

也可以没有运算数,例如: score is not null

在表中,字段的位置已经确定,进一步将查询条件简化为:字段下标、运算符、运算数。

根据上面的内容定义出下面的接口:

class ConditionItem
{
public:
  virtual bool GetLogic(const std::vector<DBVal>& vals) = 0;
};

不同的条件只需要定义一个类继承ConditionItem ,执行查询时创建该类的实例并将行数据传递给GetLogic的参数,就可以知道该行数据是否满足指定的条件。

下面以age > 20的示例来说明:

实现在整数类型上执行大于判断的类IntGtCondition

class IntGtCondition : public ConditionItem
{
public:
  IntGtCondition(int fieldPos, int64_t valParam)
  {
    this->fieldPos_ = fieldPos;
    this->valParam_ = valParam;
  }

  virtual ~IntGtCondition() {}

  virtual bool GetLogic(const std::vector<DBVal>& vals)
  {
    if (VALUE_TYPE::VAL_INT == vals[fieldPos_].valType_)
    {
      return vals[fieldPos_].val_.intVal_ > valParam_;
    }

    return false;
  }

private:
  int fieldPos_;
  int64_t valParam_;
};

fieldPos_ 即 字段下标;

valParam_ 即 运算数;

运算符也就是大于;

若表的创建语句如下: CREATE TABLE stuinfo(id int, name string, age int)

那么条件: where age > 20 可以转换为

ConditionItem pCondition = new IntGtCondition(2, 20);

将每行数据分别调用 pCondition的GetLogic 方法即可得到该行数据是否满足 age > 20的条件了。

其他条件的实现类似,具体可以参考源码中:condition.h condition.cpp 文件

<think>我们正在编一个Oracle SQL查询,用于选择特定时间范围内的记录。 在Oracle中,日期和时间通常存储在DATE或TIMESTAMP数据类型的列中。 我们可以使用BETWEEN运算符或使用>=和<=来指定时间范围。 假设我们有一个表名为`my_table`,其中有一个表示时间的列`event_time`(类型为DATE或TIMESTAMP)。 我们想要选择在某个开始时间和结束时间之间的记录。 注意:在Oracle中,日期字面量需要使用TO_DATE函数或使用ANSI日期字面量(例如DATE 'YYYY-MM-DD')进行转换,或者使用TIMESTAMP字面量。 示例:选择2023年1月1日上午9点到2023年1月1日下午5点之间的记录。 方法1:使用BETWEEN ```sql SELECT * FROM my_table WHERE event_time BETWEEN TO_DATE('2023-01-01 09:00:00', 'YYYY-MM-DD HH24:MI:SS') AND TO_DATE('2023-01-01 17:00:00', 'YYYY-MM-DD HH24:MI:SS'); ``` 方法2:使用>=和<= ```sql SELECT * FROM my_table WHERE event_time >= TO_DATE('2023-01-01 09:00:00', 'YYYY-MM-DD HH24:MI:SS') AND event_time <= TO_DATE('2023-01-01 17:00:00', 'YYYY-MM-DD HH24:MI:SS'); ``` 注意:如果列是TIMESTAMP类型,可以使用TO_TIMESTAMP函数,但通常TO_DATE也可以用于比较(Oracle会自动转换类型)。不过,为了精确到秒以下,应使用TO_TIMESTAMP。 如果时间范围是跨天的,或者需要更精确的时间,同样适用。 另外,如果表中的时间列包含时区信息(TIMESTAMP WITH TIME ZONE),则需要考虑时区转换,但本问题中我们假设是本地时间。 重要提示:在Oracle中,如果使用BETWEEN,注意结束时间是否包含在内。因为如果时间列包含秒或更小单位,而结束时间只精确到分钟,那么结束时间那一天的17:00:00之后的数据(比如17:00:01)不会被包含。因此,有时为了避免遗漏,我们可能会将结束时间增加1秒(但这样会包含17:00:00到17:00:01之间的数据)。或者,更常见的做法是使用半开区间(即包含开始时间,不包含结束时间): ```sql SELECT * FROM my_table WHERE event_time >= TO_DATE('2023-01-01 09:00:00', 'YYYY-MM-DD HH24:MI:SS') AND event_time < TO_DATE('2023-01-01 17:00:01', 'YYYY-MM-DD HH24:MI:SS'); -- 或者直接使用第二天的0点,但这里我们精确到秒 ``` 但是,根据需求,如果确实需要包含结束时间点,那么使用BETWEEN或<=都是可以的。但要注意,如果时间列有小数秒,那么使用BETWEEN可能不会包含结束时间点的小数秒部分(因为TO_DATE只精确到秒)。因此,对于TIMESTAMP类型,建议使用TO_TIMESTAMP,并确保格式匹配。 示例(TIMESTAMP列): ```sql SELECT * FROM my_table WHERE event_time >= TO_TIMESTAMP('2023-01-01 09:00:00.000', 'YYYY-MM-DD HH24:MI:SS.FF3') AND event_time < TO_TIMESTAMP('2023-01-02 00:00:00.000', 'YYYY-MM-DD HH24:MI:SS.FF3'); ``` 这里我们选择了一整天的数据(从2023-01-01 09:00:00到2023-01-01结束之前)。 根据用户需求,是特定时间区间范围,所以我们需要明确时间范围是闭区间还是开区间。 另外,如果用户需要查询最近一段时间(例如过去一小时),可以使用SYSDATE或SYSTIMESTAMP。 例如,查询过去一小时内的记录: ```sql SELECT * FROM my_table WHERE event_time >= SYSDATE - 1/24 AND event_time <= SYSDATE; ``` 因为一天有24小时,所以1/24表示1小时。 总结:根据具体的时间范围和列的数据类型,选择合适的函数和区间。 注意:在Oracle中,日期计算可以直接使用数字,其中1表示1天,1/24表示1小时,1/(24*60)表示1分钟,以此类推。 最后,请根据实际情况替换表名、列名和时间范围。 如果时间范围很大,考虑性能问题,确保在时间列上有索引。 相关问题: 1. 如何在Oracle中查询两个日期之间的记录,包括日期边界? 2. Oracle中如何获取当前时间并用于时间范围查询? 3. 在Oracle中,DATE和TIMESTAMP类型有什么区别? 4. 如何优化Oracle中按时间范围查询的性能?</think>### Oracle SQL 查询特定时间区间范围的记录 在 Oracle 中查询特定时间范围的记录,需使用 `BETWEEN` 运算符或 `>=`/`<=` 比较符,结合日期函数处理时间格式。以下是具体方法和示例: #### 1. **基础查询(精确到秒)** ```sql SELECT * FROM your_table WHERE your_date_column BETWEEN TO_DATE('2023-01-01 09:00:00', 'YYYY-MM-DD HH24:MI:SS') AND TO_DATE('2023-01-01 17:30:00', 'YYYY-MM-DD HH24:MI:SS'); ``` - **说明**: - `TO_DATE()` 将字符串转换为日期类型 - `YYYY-MM-DD HH24:MI:SS` 指定时间格式(24小时制) - `BETWEEN` 包含边界值(即包含开始和结束时间点) #### 2. **排除结束时间边界(半开区间)** ```sql SELECT * FROM your_table WHERE your_date_column >= TO_DATE('2023-01-01 09:00:00', 'YYYY-MM-DD HH24:MI:SS') AND your_date_column < TO_DATE('2023-01-01 17:30:00', 'YYYY-MM-DD HH24:MI:SS'); ``` - **适用场景**:避免结束时间点重复计入(如日志分段查询) #### 3. **使用时间戳类型(精确到毫秒)** 若列为 `TIMESTAMP` 类型: ```sql SELECT * FROM your_table WHERE your_timestamp_column BETWEEN TO_TIMESTAMP('2023-01-01 09:00:00.000', 'YYYY-MM-DD HH24:MI:SS.FF3') AND TO_TIMESTAMP('2023-01-01 17:30:00.000', 'YYYY-MM-DD HH24:MI:SS.FF3'); ``` #### 4. **动态时间范围(如最近24小时)** ```sql SELECT * FROM your_table WHERE your_date_column >= SYSDATE - 1; -- 当前时间减去1天 ``` - **其他动态范围**: - `SYSDATE - 1/24`:最近1小时 - `SYSDATE - 5/(24*60)`:最近5分钟 #### 5. **日期截断(忽略时间部分)** 查询某一天的所有记录: ```sql SELECT * FROM your_table WHERE TRUNC(your_date_column) = TO_DATE('2023-01-01', 'YYYY-MM-DD'); ``` #### 性能优化建议 1. **索引使用**: ```sql CREATE INDEX idx_time ON your_table(your_date_column); ``` - 确保查询条件中的列有索引[^1] 2. **避免函数转换**: - 对列使用函数(如 `TRUNC(your_date_column)`)会导致索引失效,建议改为: ```sql WHERE your_date_column >= TO_DATE('2023-01-01', 'YYYY-MM-DD') AND your_date_column < TO_DATE('2023-01-02', 'YYYY-MM-DD') ``` #### 示例场景 查询 `orders` 表在 2023 年 6 月 1 日上午 8 点到下午 3 点的订单: ```sql SELECT order_id, customer_id, order_time FROM orders WHERE order_time BETWEEN TO_DATE('2023-06-01 08:00:00', 'YYYY-MM-DD HH24:MI:SS') AND TO_DATE('2023-06-01 15:00:00', 'YYYY-MM-DD HH24:MI:SS'); ``` ### 关键注意事项 1. **时区处理**:若需时区转换,使用 `FROM_TZ(your_timestamp, 'UTC') AT TIME ZONE 'Asia/Shanghai'` 2. **格式一致性**:输入时间字符串必须与格式模型完全匹配 3. **性能监控**:复杂查询可通过 `v$sql_monitor` 实时分析执行计划[^1] ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值