相关文章推荐
刚分手的甘蔗  ·  C++11 ...·  1 年前    · 

一、时间比较的语法分析

在mysql中,通常时间是一个必不可少的类型,而这种类型的一个特殊地方就在于它的比较函数。其实,即使对于字符串的比较函数和对于int的比较也是完全不同的,但是这个差别并不想以datetime保存的时间这么具有视觉的震撼性和表现的这么明显。这里还有一个问题在于,当mysql的yacc在进行语法分析的时候,这个时候语法分析器并没有读取一个field中的结构,或者说并没有对fiedl进行fix操作,所以当parser看到一个 field > "2016-01-11 00:00:00"这样的字符串的时候,它并不知道这个地方是要进行一个字符串的比较还是一个时间的比较,而需要在对于一个sql的表达式的各个field进行fix之后才能够找到这个对应的field的类型,并进而进行类型的判断,这里主要说明的是一个比较函数延迟设置的问题。

由于mysql有内置的mysql.event表格包含了有datetime类型的字段,所以使用这个内置的类型进行比较说明是比较合适的:

select SQL_NO_CACHE * from event where created > "2016-01-10 00:00:00";

在sql_yacc.yy的语法分析函数中,此时使用不同的creator创建了比较的item:

bool_pri:

bool_pri IS NULL_SYM %prec IS

$$= new (YYTHD->mem_root) Item_func_isnull($1);

if ($$ == NULL)

MYSQL_YYABORT;

| bool_pri comp_op predicate %prec EQ

$$= (*$2)(0)->create($1,$3);

if ($$ == NULL)

MYSQL_YYABORT;

comp_op:

EQ     { $$ = &comp_eq_creator; }

| GE     { $$ = &comp_ge_creator; }

| GT_SYM { $$ = &comp_gt_creator; }

| LE     { $$ = &comp_le_creator; }

| LT     { $$ = &comp_lt_creator; }

| NE     { $$ = &comp_ne_creator; }

Item_bool_func2* Ge_creator::create(Item *a, Item *b) const

return new Item_func_ge(a, b);

二、具体比较方法的确定

setup_conds-->>Item_bool_func2::fix_length_and_dec-->>Item_bool_func2::set_cmp_func-->>Arg_comparator::set_cmp_func-->>

可以看到的是,在进入函数之后,优先判断的datetime类型的比较类型,这个优先级最高,如果比较的两方一个是datetime类型,而另一个是string类型,则会执行对string向时间的转换。

int Arg_comparator::set_cmp_func(Item_result_field *owner_arg,

Item **a1, Item **a2,

Item_result type)

enum enum_date_cmp_type cmp_type;

ulonglong const_value= (ulonglong)-1;

thd= current_thd;

owner= owner_arg;

set_null= set_null && owner_arg;

a= a1;

b= a2;

thd= current_thd;

if ((cmp_type= can_compare_as_dates(*a, *b, &const_value)))

a_type= (*a)->field_type();

b_type= (*b)->field_type();

a_cache= 0;

b_cache= 0;

if (const_value != (ulonglong)-1)

cache_converted_constant can't be used here because it can't

correctly convert a DATETIME value from string to int representation.

Item_cache_int *cache= new Item_cache_int(MYSQL_TYPE_DATETIME);

/* Mark the cache as non-const to prevent re-caching. */

cache->set_used_tables(1);

if (!(*a)->is_datetime())

cache->store((*a), const_value);

a_cache= cache;

a= (Item **)&a_cache;

cache->store((*b), const_value);

b_cache= cache;

b= (Item **)&b_cache;

is_nulls_eq= is_owner_equal_func();

func= &Arg_comparator::compare_datetime;

get_value_a_func= &get_datetime_value;

get_value_b_func= &get_datetime_value;

return 0;

else if (type == STRING_RESULT && (*a)->field_type() == MYSQL_TYPE_TIME &&

(*b)->field_type() == MYSQL_TYPE_TIME)

/* Compare TIME values as integers. */

a_cache= 0;

b_cache= 0;

is_nulls_eq= is_owner_equal_func();

func= &Arg_comparator::compare_datetime;

get_value_a_func= &get_time_value;

get_value_b_func= &get_time_value;

return 0;

else if (type == STRING_RESULT &&

(*a)->result_type() == STRING_RESULT &&

(*b)->result_type() == STRING_RESULT)

DTCollation coll;

coll.set((*a)->collation.collation);

if (agg_item_set_converter(coll, owner->func_name(),

b, 1, MY_COLL_CMP_CONV, 1))

return 1;

else if (try_year_cmp_func(type))

return 0;

a= cache_converted_constant(thd, a, &a_cache, type);

b= cache_converted_constant(thd, b, &b_cache, type);

return set_compare_func(owner_arg, type);

如果datefield的另一端不是string类型,此时比较进入另一份分支,按照int类型来比较:

void Item_bool_func2::fix_length_and_dec()

if (!thd->lex->is_ps_or_view_context_analysis())

if (args[0]->real_item()->type() == FIELD_ITEM)

Item_field *field_item= (Item_field*) (args[0]->real_item());

if (field_item->field->can_be_compared_as_longlong() &&

!(field_item->is_datetime() &&

args[1]->result_type() == STRING_RESULT))

if (convert_constant_item(thd, field_item, &args[1]))

cmp.set_cmp_func(this, tmp_arg, tmp_arg+1,

INT_RESULT); // Works for all types.

args[0]->cmp_context= args[1]->cmp_context= INT_RESULT;

return;

三、datetime类型的解析

enum enum_mysql_timestamp_type

str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,

uint flags, int *was_cut)

for (i = start_loop;

i < MAX_DATE_PARTS-1 && str != end &&

my_isdigit(&my_charset_latin1,*str);

while (str != end &&

(my_ispunct(&my_charset_latin1,*str) ||

my_isspace(&my_charset_latin1,*str)))

if (my_isspace(&my_charset_latin1,*str))

if (!(allow_space & (1 << i)))

*was_cut= 1;

DBUG_RETURN(MYSQL_TIMESTAMP_NONE);

found_space= 1;

str++;

found_delimitier= 1;                      /* Should be a 'normal' date */

这里可以看到,它对各个字段的分隔符并没有和我们常见的所谓“YYYY-MM-DD HH:MM:SS”,而是任意的一个分隔符都可以,一我们默认的latin字符集为例,可以看到大量的字符都可以作为分隔符,下面所有和0x10逻辑与之后非零的字符都可以具有分隔符的功能,MysQL对于这个没有任何要求,也就是它并不挑食mysql-5.1.61\strings\ctype-latin1.c:

static uchar ctype_latin1[] = {

32, 32, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 32, 32,

32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,

72, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,

132,132,132,132,132,132,132,132,132,132, 16, 16, 16, 16, 16, 16,

16,129,129,129,129,129,129,  1,  1,  1,  1,  1,  1,  1,  1,  1,

1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, 16, 16, 16, 16, 16,

16,130,130,130,130,130,130,  2,  2,  2,  2,  2,  2,  2,  2,  2,

2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2, 16, 16, 16, 16, 32,

16,  0, 16,  2, 16, 16, 16, 16, 16, 16,  1, 16,  1,  0,  1,  0,

0, 16, 16, 16, 16, 16, 16, 16, 16, 16,  2, 16,  2,  0,  2,  1,

72, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,

16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,

1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,

1,  1,  1,  1,  1,  1,  1, 16,  1,  1,  1,  1,  1,  1,  1,  2,

2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,

2,  2,  2,  2,  2,  2,  2, 16,  2,  2,  2,  2,  2,  2,  2,  2

mysql> select date( "2016|01!10 00%00%00"),  time( "2016-01-10 11%22$33");

+------------------------------+------------------------------+

| date( "2016|01!10 00%00%00") | time( "2016-01-10 11%22$33") |

+------------------------------+------------------------------+

| 2016-01-10                   | 11:22:33                     |

+------------------------------+------------------------------+

1 row in set (0.00 sec)

mysql>

四、datetime的存储

这个非常有意思,跳出了我们常见的utc保存的常规思路,那么为什么它就可以使用这么简洁直观的方式呢?究其原因,就是有空间就是任性,现在的Unix时间通常是按照long存储的,但是早期的long类型都是32字节,所以即使从1970年开始,到2038年就会产生负数,所以必须进行压缩,也就是转换为从1970开始的秒数,现在使用更长的存储空间来存储,所以这个方法就是直接使用这种格式存储就可以了

/* Convert time value to integer in YYYYMMDDHHMMSS format */

ulonglong TIME_to_ulonglong_datetime(const MYSQL_TIME *my_time)

return ((ulonglong) (my_time->year * 10000UL +

my_time->month * 100UL +

my_time->day) * ULL(1000000) +

(ulonglong) (my_time->hour * 10000UL +

my_time->minute * 100UL +

my_time->second));

mysql> select cast( cast('2007-12-25' as DATETIME)  as UNSIGNED INT);

+--------------------------------------------------------+

| cast( cast('2007-12-25' as DATETIME)  as UNSIGNED INT) |

+--------------------------------------------------------+

|                                         20071225000000 |

+--------------------------------------------------------+

1 row in set (0.01 sec)

mysql>

五、比较中使用哪种类型的选择

和很多的非强制类型一样,mysql允许在多种不同的类型之间进行自动转换,这里就涉及到选择结构中的数据类型是向哪一个靠拢的问题,当然如果说两个类型都是相同的话,那么皆大欢喜。问题在于经常会遇到类型不一致的两个变量进行操作,此时就需要判断结果到底是什么类型,这里需要在具体的环境下进行具体的分析,但是大致来说有一个大致的原则在一个函数中体现mysql-5.1.61\sql\item.cc:

Item_result item_cmp_type(Item_result a,Item_result b)

if (a == STRING_RESULT && b == STRING_RESULT)

return STRING_RESULT;

if (a == INT_RESULT && b == INT_RESULT)

return INT_RESULT;

else if (a == ROW_RESULT || b == ROW_RESULT)

return ROW_RESULT;

if ((a == INT_RESULT || a == DECIMAL_RESULT) &&

(b == INT_RESULT || b == DECIMAL_RESULT))

return DECIMAL_RESULT;

return REAL_RESULT;

这里可以看到的是,如果两个都是string,那么结果就是string,但是如果两者不一致,只要有任何一个是一个int类型,那么尽量向int类型靠拢。

总起来说,这里讲到的问题在实际中的意义并不大,可以说没有什么意义。只是说为了这个问题遇到的情况比较多,或者说这里看到的问题比较常见,所以希望大家如果从这里能够看到一些其它的更为需要的流程来说,就非常有意义了。

一、时间比较的语法分析在mysql中,通常时间是一个必不可少的类型,而这种类型的一个特殊地方就在于它的比较函数。其实,即使对于字符串的比较函数和对于int的比较也是完全不同的,但是这个差别并不想以datetime保存的时间这么具有视觉的震撼性和表现的这么明显。这里还有一个问题在于,当mysql的yacc在进行语法分析的时候,这个时候语法分析器并没有读取一个field中的结构,或者说并没有对fied...
CubedProgrammerC DateTime Windows和Linux的用C语言编写的用于处理日期和时间的库。 将两个源文件编译到共享库中,在Windows上链接ntdll.dll。 gcc -O3 -I./include -I./header_only_include -c cpcdt_time_epoch.c -fPIC gcc -O3 -I./include -I./header_only_include -c cpcdt_date_struct.c -fPIC gcc -shared -o libcpcdt.so cpcdt_time_epoch.o cpcdt_date_struct.o 在Windows上请记住-lntdll。
做项目的时候开始纠结于用2013-01-01 12-12-12存储还是用 2013-01-01存储,这个设计到的问题是 mysql 中时间字段的选择问题:date、time或者 datetime ; 第二个纠结的地方是C#中是否有数据类型与之一一对应,答案是否定的,就一个 datetime ,这个 datetime 可以容纳大部分时间类型数据; 最重要的大家记住,反正 mysql 中时间字段最
MySQL 中的 datetime 类型用于存储日期和时间,采用的格式为“YYYY-MM-DD HH:MM:SS”。在定义 datetime 类型的字段时,可以设置默认值。默认值表示插入新行时若该字段没有指定值,则自动将默认值赋给该字段。 MySQL datetime 类型默认值的设置方法如下: 1. 在创建表时使用DEFAULT关键字来指定默认值,例如: CREATE TABLE `table_name` ( ` datetime _column` datetime NOT NULL DEFAULT '2000-01-01 00:00:00', 2. 使用ALTER TABLE语句来修改表结构,并设置默认值,例如: ALTER TABLE `table_name` MODIFY COLUMN ` datetime _column` datetime NOT NULL DEFAULT '2000-01-01 00:00:00'; 需要注意的是,默认值必须符合 datetime 类型的格式要求,否则会抛出错误。同时,若插入新行时指定了 datetime 类型的字段值,则默认值将被忽略。