推荐 最新
刘传疯子

问一个多表查询的简单问题?

有两张表,分别储存产品信息与检测信息 检测信息表procheck中pro_id关联到产品信息表pro中的id,每个产品对应有多个检测信息记录 我现在需要把某个公司(产品表中company_id)生产的所有产品的最新一次检测报告调出来? SELECT `pro`.`id`,`pro`.`pm`,`pro`.`company_id`,procheck.id AS procheck__id,procheck.pro_id AS procheck__pro_id,procheck.checkdate AS procheck__checkdate FROM `da_pro` `pro` LEFT JOIN `da_procheck` `procheck` ON `pro`.`id`=`procheck`.`pro_id` WHERE `pro`.`company_id` = 487 但是得出的结果却是: "image.png" (https://wmlx-new-image.oss-cn-shanghai.aliyuncs.com/images/20250121/1fc0154e7d3cecbee677766287c8508b.png) 全部给我找了出来,而我真正需要的是箭头上的三条即可。 求SQL语句.....

13
1
0
浏览量394
M78的社畜

mysql如何使用SUBSTRING_INDEX函数对对应的字符串实现列转行的效果?

问题描述 mysql如何使用SUBSTRING_INDEX函数对对应的字符串实现列转行的效果? 我在用mysql的SUBSTRING_INDEX函数处理表中的一个字段值时遇到了这个问题,这个字段中的数据是如下这个样子(注意这是一个单元格内的数据长这个样子,不是多个单元格) 使用过【35869209395881】的号码清单: [20230608,广东,广州,19876298082],[20230609,广东,广州,19864391337],[20230610,广东,广州,19875475010] 使用过【86536704443634】的号码清单: [20230320,广东,广州,13660250915],[20230412,广东,广州,13527671189],[20230523,广东,广州,13527864794],[20230523,广东,广州,13602403001],[20230523,广东,广州,15975441679],[20230524,广东,广州,13760718971],[20230525,广东,广州,13710479054],[20230606,广东,广州,13710034067],[20230608,广东,广州,13416248767],[20230608,广东,广州,19864070445],[20230608,广东,广州,19876298082] 使用过【86855103086379】的号码清单: [20230407,广东,广州,17278756850],[20230407,广东,广州,19513761138],[20230407,广东,广州,19576161138],[20230408,广东,广州,13512761189],[20230408,广东,广州,13527671189],[20230524,广东,广州,13527864794],[20230525,广东,广州,13609703269],[20230610,广东,广州,19864070445],[20230610,广东,广州,19876298082],[20230612,广东,广州,15790403652] 使用过【86947206218694】的号码清单: [20230613,广东,广州,19876298082] 问题出现的环境背景及自己尝试过哪些方法 我的问题是出现在mysql处理这个表phone_number_demo中的IMEI_rel字段过程时遇到的, 当时我采用了SUBSTRING_INDEX函数进行拆分,但是我的处理方法最终只实现到了将[]内的内容拆分出来,【】中的序列号始终没有拆分出来。 相关代码 SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(data, ',', 1), '[', -1) AS date, SUBSTRING_INDEX(SUBSTRING_INDEX(data, ',', 2), ',', -1) AS region, SUBSTRING_INDEX(SUBSTRING_INDEX(data, ',', 3), ',', -1) AS city, SUBSTRING_INDEX(SUBSTRING_INDEX(data, ',', -1), ']', 1) AS phone FROM ( SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(IMEI_rel, ']', n), '[', -1) AS data FROM phone_number_demo, (SELECT 1 AS n UNION ALL SELECT 2 UNION ALL SELECT 3) AS numbers WHERE IMEI_rel LIKE '使用过%' ORDER BY IMEI_rel, n ) AS subquery; 你期待的结果是什么?实际看到的错误信息又是什么? 我的目的是将【】和[]中的数据拆分出来并且一一对应,实现的效果为 序列号 日期 省份 城市 手机号 XXX XXX XXX XXX XXX 最终拆分出来的效果是"image.png" (https://wmprod.oss-cn-shanghai.aliyuncs.com/images/20250107/b4b251d57d07d46da0e278b1c8a01072.png) 根据一楼评论的sql跑出来的数据是如下这个样子的 "image.png" (https://wmprod.oss-cn-shanghai.aliyuncs.com/images/20250107/4345f9b91b299dc846d8ecaf6272bb48.png) "image.png" (https://wmprod.oss-cn-shanghai.aliyuncs.com/images/20250107/b19c96132cefeae1d2b48869bbdb8fdf.png)

14
1
0
浏览量315
无敌铃铃后

update set name = 'xxx' where id = 1 and is_delete = 0 where后面同时用了两个字段id和is_delete,锁表还是锁行?

** 我发现mysql的group by 后面的字段没有严格要求,甚至select后面的字段可以不出现在group by后面**

14
1
0
浏览量436
七安前

如何优化orm框架产生的重复sql?

使用laravel开发了一套系统,并使用eloquent作为orm框架,查看sql日志发现执行了大量的重复sql,是否存在专业工具可以分析出这些不必要的sql,进行针对性的优化? 比如对慢查询日志来讲,有mysqldumpslow这个工具可以分析,那么对于自己导出的sql日志来讲,有什么分析工具吗?或者我应该结合什么工具和sql日志的输出格式来达成sql分析的目的?

13
1
0
浏览量344
走你啊啊啊啊啊

mysql 必须group by全部select中的字段?

** 我发现mysql的group by 后面的字段没有严格要求,甚至select后面的字段可以不出现在group by后面**

11
1
0
浏览量330
脑袋困掉惹

php导出sql数据到csv时为什么不保存为文件?

这段代码导出sql数据到csv文件,数据是出来了,显示在网页上,不保存成csv文件,请教一下什么原因? if (isset($_POST['export'])) { $sql = 'SELECT col1, col2, col3 FROM `sheet1`'; $result = $conn->query($sql); if ($result === FALSE) { echo "Error fetching data: " . $conn->error . ""; } elseif ($result->num_rows > 0) { // create a file pointer connected to the output stream $output = fopen('php://output', 'w'); // output the column headings fputcsv($output, ['col1', 'col2', 'col3']); // loop over the rows, outputting them while ($row = $result->fetch_assoc()) fputcsv($output, $row); // Output CSV-specific headers header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename=data.csv'); fclose($output); exit; } else { echo "0 results"; } $conn->close(); }

8
1
0
浏览量279
秋叶无缘

MYSQL 事务的底层原理 | 京东物流技术团队

事务的底层原理在事务的实现机制上,MySQL 采用的是 WAL:Write-ahead logging,预写式日志,机制来实现的。在使用 WAL 的系统中,所有的修改都先被写入到日志中,然后再被应用到系统中。通常包含 redo 和 undo 两部分信息。为什么需要使用 WAL,然后包含 redo 和 undo 信息呢?举个例子,如果一个系统直接将变更应用到系统状态中,那么在机器掉电重启之后系统需要知道操作是成功了,还是只有部分成功或者是失败了。如果使用了 WAL,那么在重启之后系统可以通过比较日志和系统状态来决定是继续完成操作还是撤销操作。redo log 称为重做日志,每当有操作时,在数据变更之前将操作写入 redo log,这样当发生掉电之类的情况时系统可以在重启后继续操作。undo log 称为撤销日志,当一些变更执行到一半无法完成时,可以根据撤销日志恢复到变更之间的状态。MySQL 中用 redo log 来在系统 Crash 重启之类的情况时修复数据,而 undo log 来保证事务的原子性。事务 id一个事务可以是一个只读事务,或者是一个读写事务:可以通过 START TRANSACTION READ ONLY 语句开启一个只读事务。在只读事务中不可以对普通的表进行增、删、改操作,但可以对用户临时表做增、删、改操作。可以通过 START TRANSACTION READ WRITE 语句开启一个读写事务,或者使用 BEGIN、START TRANSACTION 语句开启的事务默认也算是读写事务。在读写事务中可以对表执行增删改查操作。如果某个事务执行过程中对某个表执行了增、删、改操作,那么 InnoDB 存储引擎就会给它分配一个独一无二的事务 id,针对 MySQL 5.7 分配方式如下:对于只读事务来说,只有在它第一次对某个用户创建的临时表执行增、删、改操作时才会为这个事务分配一个事务 id,否则的话是不分配事务 id 的。对于读写事务来说,只有在它第一次对某个表执行增、删、改操作时才会为这个事务分配一个事务 id,否则的话也是不分配事务 id 的。有的时候虽然开启了一个读写事务,但是在这个事务中全是查询语句,并没有执行增、删、改的语句,那也就意味着这个事务并不会被分配一个事务 id。这个事务 id 本质上就是一个数字,它的分配策略和隐藏列 row_id 的分配策略大抵相同,具体策略如下:服务器会在内存中维护一个全局变量,每当需要为某个事务分配一个事务 id 时,就会把该变量的值当作事务 id 分配给该事务,并且把该变量自增 1。每当这个变量的值为 256 的倍数时,就会将该变量的值刷新到系统表空间的页号为 5 的页面中一个称之为 Max Trx ID 的属性处,这个属性占用 8 个字节的存 储空间。当系统下一次重新启动时,会将上边提到的 Max Trx ID 属性加载到内存中,将该值加上 256 之后赋值给全局变量,因为在上次关机时该全局变量的值可能大于Max Trx ID 属性值。这样就可以保证整个系统中分配的事务 id 值是一个递增的数字。先被分配 id 的事务得到的是较小的事务 id,后被分配 id 的事务得到的是较大的事务 id。mvcc全称 Multi-Version Concurrency Control,即多版本并发控制,主要是为了提高数据库的并发性能。同一行数据平时发生读写请求时,会上锁阻塞住。但 MVCC 用更好的方式去处理读写请求,做到在发生读写请求冲突时不用加锁。这个读是指的快照读,而不是当前读,当前读是一种加锁操作,是悲观锁。MVCC 原理在事务并发执行遇到的问题如下:脏读:如果一个事务读到了另一个未提交事务修改过的数据,那就意味着发生了脏读;不可重复读:如果一个事务只能读到另一个已经提交的事务修改过的数据,并且其他事务每对该数据进行一次修改并提交后,该事务都能查询得到最新值,那就意味着发生了不可重复读;幻读:如果一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来,那就意味着发生了幻读,幻读强调的是一个事务按照某个相同条件多次读取记录时,后读取时读到了之前没有读到的记录,幻读只是重点强调了读取到了之前读取没有获取到的记录。MySQL 在 REPEATABLE READ 隔离级别下,是可以很大程度避免幻读问题的发生的。版本链对于使用 InnoDB 存储引擎的表来说,它的聚簇索引记录中都包含两个必要的隐藏列:trx_id:每次一个事务对某条聚簇索引记录进行改动时,都会把该事务的事务 id 赋值给 trx_id 隐藏列;roll_pointer:每次对某条聚簇索引记录进行改动时,都会把旧的版本写入到 undo 日志中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修 改前的信息;演示-- 创建表 CREATE TABLE mvcc_test ( id INT, name VARCHAR(100), domain varchar(100), PRIMARY KEY (id) ) Engine=InnoDB CHARSET=utf8; -- 添加数据 INSERT INTO mvcc_test VALUES(1, 'habit', '演示mvcc'); 假设插入该记录的事务 id=50,那么该条记录的展示如图:假设之后两个事务 id 分别为 70、90 的事务对这条记录进行 UPDATE 操作。trx_id=70trx_id=90beginbeginupdate mvcc_test set name='habit_trx_id_70_01' where id=1update mvcc_test set name='habit_trx_id_70_02' where id=1commitupdate mvcc_test set name='habit_trx_id_90_01' where id=1update mvcc_test set name='habit_trx_id_90_02' where id=1commit每次对记录进行改动,都会记录一条 undo 日志,每条 undo 日志也都有一个 roll_pointer 属性,可以将这些 undo 日志都连起来,串成一个链表。对该记录每次更新后,都会将旧值放到一条 undo 日志中,就算是该记录的一个旧版本,随着更新次数的增多,所有的版本都会被 roll_pointer 属性连接成一个链表,把这个链表称之为版本链,版本链的头节点就是当前记录最新的值。另外,每个版本中还包含生成该版本时对应的事务 id。于是可以利用这个记录的版本链来控制并发事务访问相同记录的行为,那么这种机制就被称之为:多版本并发控制,即 MVCC。ReadView对于使用 READ UNCOMMITTED 隔离级别的事务来说,由于可以读到未提交事务修改过的记录,所以直接读取记录的最新版本就好了。对于使用 SERIALIZABLE 隔离级别的事务来说,InnoDB 使用加锁的方式来访问记录。对于使用 READ COMMITTED 和 REPEATABLE READ 隔离级别的事务来说,都必须保证读到已经提交了的事务修改过的记录,也就是说假如另一个事务已经修改了记录但是尚未提交,是不能直接读取最新版本的记录的,核心问题就是:READ COMMITTED 和 REPEATABLE READ 隔离级别在不可重复读和幻读上的区别是从哪里来的,其实结合前面的知识,这两种隔离级别关键是需要判断一下版本链中的哪个版本是当前事务可见的。为此,InnoDB 提出了一个 ReadView 的概念,这个 ReadView 中主要包含 4 个比较重要的内容:m_ids:表示在生成 ReadView 时当前系统中活跃的读写事务的事务id 列表;min_trx_id:表示在生成 ReadView 时当前系统中活跃的读写事务中最小的事务 id,也就是 m_ids 中的最小值;max_trx_id:表示在生成 ReadView 时系统中应该分配给下一个事务的 id 值,注:max_trx_id 并不是 m_ids 中的最大值,事务 id 是递增分配的。比方说现在有 id 为 1,2,3 这三个事务,之后 id 为 3 的事务提交了。那么一个新的读事务在生成 ReadView 时,m_ids 就包括 1 和 2,min_trx_id 的值就是 1,max_trx_id 的值就是 4;creator_trx_id:表示生成该 ReadView 的事务的事务 id;有了这个 ReadView,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见:如果被访问版本的 trx_id 属性值与 ReadView 中的 creator_trx_id 值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问;如果被访问版本的 trx_id 属性值小于 ReadView 中的 min_trx_id 值,表明生成该版本的事务在当前事务生成 ReadView 前已经提交,所以该版本可以被当前事务访问;如果被访问版本的 trx_id 属性值大于或等于 ReadView 中的 max_trx_id 值,表明生成该版本的事务在当前事务生成 ReadView 后才开启,所以该版本不可以被当前事务访问;如果被访问版本的 trx_id 属性值在 ReadView 的 min_trx_id 和 max_trx_id之间 min_trx_id < trx_id < max_trx_id,那就需要判断一下 trx_id 属性值是不是在 m_ids 列表中,如果在,说明创建 ReadView 时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建 ReadView 时生成该版本的事务已经被提交,该版本可以被访问;如果某个版本的数据对当前事务不可见的话,那就顺着版本链找到下一个版本的数据,继续按照上边的步骤判断可见性,依此类推,直到版本链中的最后一个版本。如果最后一个版本也不可见的话,那么就意味着该条记录对该事务完全不可见,查询结果就不包含该记录;在 MySQL 中,READ COMMITTED 和 REPEATABLE READ 隔离级别的一个非常大的区别就是它们生成 ReadView 的时机不同。还是以表 mvcc_test 为例,假设现在表 mvcc_test 中只有一条由事务 id 为 50 的事务插入的一条记录,接下来看一下 READ COMMITTED 和 REPEATABLE READ 所谓的生成 ReadView 的时机不同到底不同在哪里。READ COMMITTED: 每次读取数据前都生成一个 ReadView;比方说现在系统里有两个事务id 分别为 70、90 的事务在执行:-- T 70 UPDATE mvcc_test  SET name = 'habit_trx_id_70_01' WHERE id = 1; UPDATE mvcc_test  SET name = 'habit_trx_id_70_02' WHERE id = 1; 此时表 mvcc_test 中 id 为 1 的记录得到的版本链表如下所示:假设现在有一个使用 READ COMMITTED 隔离级别的事务开始执行:-- 使用 READ COMMITTED 隔离级别的事务 BEGIN; -- SELECE1:Transaction 70、90 未提交 SELECT * FROM mvcc_test  WHERE id = 1; -- 得到的列 name 的值为'habit' 这个 SELECE1 的执行过程如下:在执行 SELECT 语句时会先生成一个 ReadView,ReadView 的 m_ids 列表的内容就是[70, 90],min_trx_id 为 70,max_trx_id 为 91,creator_trx_id 为 0。然后从版本链中挑选可见的记录,从图中可以看出,最新版本的列 name 的内容是 habit_trx_id_70_02,该版本的 trx_id 值为 70,在 m_ids 列表内,所以不符合可见性要求第 4 条:**如果被访问版本的 trx_id 属性值在 ReadView 的 min_trx_id 和 max_trx_id之间 min_trx_id < trx_id < max_trx_id,那就需要判断一下trx_id 属性值是不是在 m_ids 列表中,如果在,说明创建 ReadView 时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建 ReadView 时生成该版本的事务已经被提交,该版本可以被访问。**根据 roll_pointer 跳到下一个版本。下一个版本的列 name 的内容是 habit_trx_id_70_01,该版本的 trx_id 值也为 70,也在 m_ids 列表内,所以也不符合要求,继续跳到下一个版本。下一个版本的列 name 的内容是 habit,该版本的 trx_id 值为 50,小于 ReadView 中的 min_trx_id 值,所以这个版本是符合要求的第 2 条:**如果被访问版本的 trx_id 属性值小于 ReadView 中的 min_trx_id 值,表明生成该版本的事务在当前事务生成 ReadView 前已经提交,所以该版本可以被当前事务访问。**最后返回的版本就是这条列 name 为 habit 的记录。之后,把事务 id 为 70 的事务提交一下,然后再到事务 id 为 90 的事务中更新一下表 mvcc_test 中 id 为 1 的记录:-- T 90 UPDATE mvcc_test  SET name = 'habit_trx_id_90_01' WHERE id = 1; UPDATE mvcc_test  SET name = 'habit_trx_id_90_02' WHERE id = 1; 此时表 mvcc 中 id 为 1 的记录的版本链就长这样:然后再到刚才使用 READ COMMITTED 隔离级别的事务中继续查找这个 id 为 1 的记录,如下:-- 使用 READ COMMITTED 隔离级别的事务 BEGIN; -- SELECE1:Transaction 70、90 均未提交 SELECT * FROM mvcc_test WHERE id = 1; -- 得到的列 name 的值为'habit' -- SELECE2:Transaction 70 提交,Transaction 90 未提交 SELECT * FROM mvcc_test WHERE id = 1; -- 得到的列 name 的值为'habit_trx_id_70_02' 这个SELECE2 的执行过程如下:在执行 SELECT 语句时又会单独生成一个 ReadView,该 ReadView 的 m_ids 列表的内容就是[90],min_trx_id 为90,max_trx_id 为 91,creator_trx_id 为 0。然后从版本链中挑选可见的记录,从图中可以看出,最新版本的列 name 的内容是 habit_trx_id_90_02,该版本的 trx_id 值为 90,在 m_ids 列表内,所以不符合可见性要求,根据 roll_pointer 跳到下一个版本。下一个版本的列 name 的内容是 habit_trx_id_90_01,该版本的 trx_id 值为 90,也在 m_ids 列表内,所以也不符合要求,继续跳到下一个版本。下一个版本的列 name 的内容是 habit_trx_id_70_02,该版本的 trx_id 值为 70,小于 ReadView 中的 min_trx_id 值 90,所以这个版本是符合要求的,最后返回这个版本中列 name 为 habit_trx_id_70_02 的记录。以此类推,如果之后事务 id 为 90 的记录也提交了,再次在使用 READ COMMITTED 隔离级别的事务中查询表 mvcc_test 中 id 值为 1 的记录时,得到的结果就是 habit_trx_id_90_02 了。**总结:**使用 READ COMMITTED 隔离级别的事务在每次查询开始时都会生成一个独立的 ReadView。**REPEATABLE READ:**在第一次读取数据时生成一个 ReadView;对于使用 REPEATABLE READ 隔离级别的事务来说,只会在第一次执行查询语句时生成一个 ReadView,之后的查询就不会重复生成了。比方说现在系统里有两个事务id 分别为 70、90 的事务在执行:-- T 70 UPDATE mvcc_test  SET name = 'habit_trx_id_70_01' WHERE id = 1; UPDATE mvcc_test  SET name = 'habit_trx_id_70_02' WHERE id = 1; 此时表 mvcc_test 中 id 为 1 的记录得到的版本链表如下所示:假设现在有一个使用 REPEATABLE READ 隔离级别的事务开始执行:-- 使用 REPEATABLE READ 隔离级别的事务 BEGIN; -- SELECE1:Transaction 70、90 未提交 SELECT * FROM mvcc_test WHERE id = 1; -- 得到的列name 的值为'habit' 这个 SELECE1 的执行过程如下:在执行 SELECT 语句时会先生成一个 ReadView,ReadView 的 m_ids 列表的内容就是[70, 90],min_trx_id 为 70,max_trx_id 为 91,creator_trx_id 为 0。然后从版本链中挑选可见的记录,从图中可以看出,最新版本的列 name 的内容是 habit_trx_id_70_02,该版本的 trx_id 值为 70,在 m_ids 列表内,所以不符合可见性要求,根据 roll_pointer 跳到下一个版本。下一个版本的列 name 的内容是 habit_trx_id_70_01,该版本的 trx_id 值也为 70,也在 m_ids 列表内,所以也不符合要求,继续跳到下一个版本。下一个版本的列 name 的内容是 habit,该版本的 trx_id 值为 50,小于 ReadView 中的 min_trx_id 值,所以这个版本是符合要求的,最后返回的就是这条列name 为 habit 的记录。之后,把事务 id 为 70 的事务提交一下,然后再到事务 id 为 90 的事务中更新一下表 mvcc_test 中 id 为 1 的记录:-- 使用 REPEATABLE READ 隔离级别的事务 BEGIN; UPDATE mvcc_test  SET name = 'habit_trx_id_90_01' WHERE id = 1; UPDATE mvcc_test  SET name = 'habit_trx_id_90_02' WHERE id = 1; 此刻,表 mvcc_test 中 id 为 1 的记录的版本链就长这样:然后再到刚才使用 REPEATABLE READ 隔离级别的事务中继续查找这个 id 为 1 的记录,如下:-- 使用 REPEATABLE READ 隔离级别的事务 BEGIN; -- SELECE1:Transaction 70、90 均未提交 SELECT * FROM mvcc_test WHERE id = 1; -- 得到的列 name 的值为'habit' -- SELECE2:Transaction 70 提交,Transaction 90 未提交 SELECT * FROM mvcc_test WHERE id = 1;  -- 得到的列 name 的值为'habit' 这个 SELECE2 的执行过程如下:因为当前事务的隔离级别为 REPEATABLE READ,而之前在执行 SELECE1 时已经生成过 ReadView 了,所以此时直接复用之前的 ReadView,之前的 ReadView的 m_ids 列表的内容就是[70, 90],min_trx_id 为 70,max_trx_id 为 91, creator_trx_id 为 0。然后从版本链中挑选可见的记录,从图中可以看出,最新版本的列 name 的内容是 habit_trx_id_90_02,该版本的 trx_id 值为 90,在 m_ids 列表内,所以不符合可见性要求,根据 roll_pointer 跳到下一个版本。下一个版本的列 name 的内容是 habit_trx_id_90_01,该版本的 trx_id 值为 90,也在 m_ids 列表内,所以也不符合要求,继续跳到下一个版本。下一个版本的列 name 的内容是 habit_trx_id_70_02,该版本的 trx_id 值为 70,而 m_ids 列表中是包含值为 70 的事务 id 的,所以该版本也不符合要求,同理下一个列 name 的内容是 habit_trx_id_70_01 的版本也不符合要求。继续跳到下一个版本。下一个版本的列 name 的内容是 habit,该版本的 trx_id 值为 50,小于 ReadView 中的 min_trx_id 值 70,所以这个版本是符合要求的,最后返回给用户的版本就是这条列 name 为 habit 的记录。也就是说两次 SELECT 查询得到的结果是重复的,记录的列 name 值都是 habit,这就是可重复读的含义。如果之后再把事务 id 为 90 的记录提交了,然后再到刚才使用 REPEATABLE READ 隔离级别的事务中继续查找这个 id 为 1 的记录,得到的结果还是 habit。MVCC 下的幻读解决和幻读现象REPEATABLE READ 隔离级别下 MVCC 可以解决不可重复读问题,那么幻读呢?MVCC 是怎么解决的?幻读是一个事务按照某个相同条件多次读取记录时,后读取时读到了之前没有读到的记录,而这个记录来自另一个事务添加的新记录。可以想想,在 REPEATABLE READ 隔离级别下的事务 T1 先根据某个搜索条件读取到多条记录,然后事务 T2 插入一条符合相应搜索条件的记录并提交,然后事务 T1 再根据相同搜索条件执行查询。结果会是什么?按照 ReadView 中的比较规则中的第 3 条和第 4 条不管事务 T2 比事务 T1 是否先开启,事务 T1 都是看不到 T2 的提交的。但是,在 REPEATABLE READ 隔离级别下 InnoDB 中的 MVCC 可以很大程度地避免幻读现象,而不是完全禁止幻读。怎么回事呢?来看下面的情况:首先在事务 T1 中执行:select * from mvcc_test where id = 30; 这个时候是找不到 id = 30 的记录的。在事务 T2 中,执行插入语句:insert into mvcc_test values(30,'luxi','luxi');此时回到事务 T1,执行:update mvcc_test set domain='luxi_t1' where id=30; select * from mvcc_test where id = 30; 事务T1 很明显出现了幻读现象。在 REPEATABLE READ 隔离级别下,T1 第一次执行普通的 SELECT 语句时生成了一个 ReadView,之后 T2 向 mvcc_test 表中新插入一条记录并提交。ReadView 并不能阻止 T1 执行 UPDATE 或者 DELETE 语句来改动这个新插入的记录,由于 T2 已经提交,因此改动该记录并不会造成阻塞,但是这样一来,这条新记录的 trx_id 隐藏列的值就变成了 T1 的事务 id。之后 T1 再使用普通的 SELECT 语句去查询这条记录时就可以看到这条记录了,也就可以把这条记录返回给客户端。因为这个特殊现象的存在,可以认为 MVCC 并不能完全禁止幻读。mvcc 总结从上边的描述中可以看出来,所谓的 MVCC(Multi-Version Concurrency Control ,多版本并发控制)指的就是在使用 READ COMMITTD、REPEATABLE READ 这两种隔离级别的事务在执行普通的 SELECT 操作时访问记录的版本链的过程,这样子可以使不同事务的读写、写读操作并发执行,从而提升系统性能。READ COMMITTD、REPEATABLE READ 这两个隔离级别的一个很大不同就是:生成 ReadView 的时机不同,READ COMMITTD 在每一次进行普通 SELECT 操作前都会生成一个 ReadView,而 REPEATABLE READ 只在第一次进行普通 SELECT 操作前生成一个 ReadView,之后的查询操作都重复使用这个 ReadView 就好了,从而基本上可以避免幻读现象。InnoDB 的 Buffer Pool对于使用 InnoDB 作为存储引擎的表来说,不管是用于存储用户数据的索引,包括:聚簇索引和二级索引,还是各种系统数据,都是以页的形式存放在表空间中的,而所谓的表空间只不过是 InnoDB 对文件系统上一个或几个实际文件的抽象,也就是说数据还是存储在磁盘上的。但是磁盘的速度慢,所以 InnoDB 存储引擎在处理客户端的请求时,当需要访问某个页的数据时,就会把完整的页的数据全部加载到内存中,即使只需要访问一个页的一条记录,那也需要先把整个页的数据加载到内存中。将整个页加载到内存中后就可以进行读写访问了,在进行完读写访问之后并不着急把该页对应的内存空间释放掉,而是将其缓存起来,这样将来有请求再次访问该页面时,就可以省去磁盘 IO 的开销了。Buffer PoolInnoDB 为了缓存磁盘中的页,在 MySQL 服务器启动的时候就向操作系统申请了一片连续的内存,这块连续内存叫做:Buffer Pool,中文名:缓冲池。默认情况下 Buffer Pool 只有 128M 大小。查看该值:show variables like 'innodb_buffer_pool_size';可以在启动服务器的时候配置 innodb_buffer_pool_size 参数的值,它表示 Buffer Pool 的大小,配置如下:[server] innodb_buffer_pool_size = 268435456 其中,268435456 的单位是字节,也就是指定 Buffer Pool 的大小为 256M,Buffer Pool 也不能太小,最小值为 5M,当小于该值时会自动设置成 5M。启动 MySQL 服务器的时候,需要完成对 Buffer Pool 的初始化过程,就是先向操作系统申请 Buffer Pool 的内存空间,然后把它划分成若干对控制块和缓 存页。但是此时并没有真实的磁盘页被缓存到 Buffer Pool 中,之后随着程序的运行,会不断的有磁盘上的页被缓存到 Buffer Pool 中。在 Buffer Pool 中会创建多个缓存页,默认的缓存页大小和在磁盘上默认的页大小是一样的,都是 16KB。那么怎么知道该页在不在 Buffer Pool 中呢?在查找数据的时候,先通过哈希表中查找 key 是否在哈希表中,如果在证明 Buffer Pool 中存在该缓存也信息,如果不存在证明不存该缓存也信息,则通过读取磁盘加载该页信息放到 Buffer Pool 中,哈希表中的 key 是通过表空间号+ 页号作组成的,value 是 Buffer Pool 的缓存页。flush 链表的管理如果修改了 Buffer Pool 中某个缓存页的数据,那它就和磁盘上的页不一致了,这样的缓存页也被称为:脏页。最简单的做法就是每发生一次修改就立即同步到磁盘上对应的页上,但是频繁的往磁盘中写数据会严重的影响程序的性能。所以每次修改缓存页后,并不着急把修改同步到磁盘上,而是在未来的某个时间进行同步。 但是如果不立即同步到磁盘的话,那之后再同步的时候怎么知道 Buffer Pool 中哪些页是脏页,哪些页从来没被修改过呢?总不能把所有的缓存页都同步到磁盘上吧,如果 Buffer Pool 被设置的很大,那一次性同步会非常慢。所以,需要再创建一个存储脏页的链表,凡是修改过的缓存页对应的控制块都会作为一个节点加入到一个链表中,因为这个链表节点对应的缓存页都是需要被刷新到磁盘上的,所以也叫 flush 链表。刷新脏页到磁盘后台有专门的线程每隔一段时间负责把脏页刷新到磁盘,这样可以不影响用户线程处理正常的请求。从 flush 链表中刷新一部分页面到磁盘,后台线程也会定时从 flush 链表中刷新一部分页面到磁盘,刷新的速率取决于当时系统是不是很繁忙。这种刷新页面的方式被称之为:BUF_FLUSH_LIST。redo 日志redo 日志的作用InnoDB 存储引擎是以页为单位来管理存储空间的,增删改查操作其实本质上都是在访问页面,包括:读页面、写页面、创建新页面等操作。在真正访问页面之前,需要把在磁盘上的页缓存到内存中的 Buffer Pool 之后才可以访问。但是在事务的时候又强调过一个称之为持久性的特性,就是说对于一个已经提交的事务,在事务提交后即使系统发生了崩溃,这个事务对数据库中所做的更改也不能丢失。如果只在内存的 Buffer Pool 中修改了页面,假设在事务提交后突然发生了某个故障,导致内存中的数据都失效了,那么这个已经提交了的事务对数据库中所做的更改也就跟着丢失了,这是所不能忍受的。那么如何保证这个持久性呢?一个很简单的做法就是在事务提交完成之前把该事务所修改的所有页面都刷新到磁盘,但是这个简单粗暴的做法有些问题:刷新一个完整的数据页太浪费了;有时候仅仅修改了某个页面中的一个字节,但是在 InnoDB 中是以页为单位来进行磁盘 IO 的,也就是说在该事务提交时不得不将一个完整的页面从内存中刷新到磁盘,一个页面默认是16KB 大小,只修改一个字节就要刷新 16KB 的数据到磁盘上显然是太浪费了。随机 IO 刷起来比较慢;一个事务可能包含很多语句,即使是一条语句也可能修改许多页面,该事务修改的这些页面可能并不相邻,这就意味着在将某个事务修改的 Buffer Pool 中的页面刷新到磁盘时,需要进行很多的随机 IO,随机 IO 比顺序 IO 要慢,尤其对于传统的机械硬盘来说。只是想让已经提交了的事务对数据库中数据所做的修改永久生效,即使后来系统崩溃,在重启后也能把这种修改恢复出来。其实没有必要在每次事务提交时就把该事务在内存中修改过的全部页面刷新到磁盘,只需要把修改了哪些东西记录一下就好,比方说:某个事务将系统表空间中的第 5 号页面中偏移量为 5000 处的那个字节的值 0 改成 5 只需要记录一下:将第 5 号表空间的 5 号页面的偏移量为 5000 处的值更新为:5。这样在事务提交时,把上述内容刷新到磁盘中,即使之后系统崩溃了,重启之后只要按照上述内容所记录的步骤重新更新一下数据页,那么该事务对数据库中所做的修改又可以被恢复出来,也就意味着满足持久性的要求。因为在系统崩溃重启时需要按照上述内容所记录的步骤重新更新数据页,所以上述内容也被称之为:重做日志,即:redo log。与在事务提交时将所有修改过的内存中的页面刷新到磁盘中相比,只将该事务执行过程中产生的 redo log 刷新到磁盘的好处如下:redo log 占用的空间非常小存储表空间 ID、页号、偏移量以及需要更新的值所需的存储空间是很小的;redo log 是顺序写入磁盘的在执行事务的过程中,每执行一条语句,就可能产生若干条 redo log,这些日志是按照产生的顺序写入磁盘的,也就是使用顺序 IO;redo log 的写入过程InnoDB 为了更好的进行系统崩溃恢复,把一次原子操作生成的 redo log 都放在了大小为 512 字节的块(block)中。为了解决磁盘速度过慢的问题而引入了 Buffer Pool。同理,写入 redo log 时也不能直接写到磁盘上,实际上在服务器启动时就向操作系统申请了一大片称之为 redo log buffer 的连续内存空间,即:redo log 缓冲区,也可以简称:log buffer。这片内存空间被划分成若干个连续的 redo log block,可以通过启动参数innodb_log_buffer_size 来指定 log buffer 的大小,该启动参数的默认值为:16MB。向 log buffer 中写入 redo log 的过程是顺序的,也就是先往前边的 block 中写,当该 block 的空闲空间用完之后再往下一个 block 中写。redo log 刷盘时机log buffer 什么时候会写入到磁盘呢?log buffer 空间不足时,如果不停的往这个有限大小的 log buffer 里塞入日志,很快它就会被填满。InnoDB 认为如果当前写入 log buffer 的 redo log 量已 经占满了 log buffer 总容量的大约一半左右,就需要把这些日志刷新到磁盘上。事务提交时,必须要把修改这些页面对应的 redo log 刷新到磁盘。后台有一个线程,大约每秒都会刷新一次 log buffer 中的 redo log 到磁盘。正常关闭服务器时等等。undo 日志事务需要保证原子性,也就是事务中的操作要么全部完成,要么什么也不做。但是偏偏有时候事务执行到一半会出现一些情况,比如:情况一:事务执行过程中可能遇到各种错误,比如服务器本身的错误,操作系统错误,甚至是突然断电导致的错误。情况二:程序员可以在事务执行过程中手动输入 ROLLBACK 语句结束当前的事务的执行。这两种情况都会导致事务执行到一半就结束,但是事务执行过程中可能已经修改了很多东西,为了保证事务的原子性,需要把东西改回原先的样子,这个过程就称之为回滚,即:rollback,这样就可以造成这个事务看起来什么都没做,所以符合原子性要求。每当要对一条记录做改动时,都需要把回滚时所需的东西都给记下来。比方说:插入一条记录时,至少要把这条记录的主键值记下来,之后回滚的时候只需要把这个主键值对应的记录删掉。删除了一条记录,至少要把这条记录中的内容都记下来,这样之后回滚时再把由这些内容组成的记录插入到表中。修改了一条记录,至少要把修改这条记录前的旧值都记录下来,这样之后回滚时再把这条记录更新为旧值。这些为了回滚而记录的这些东西称之为撤销日志,即:undo log。这里需要注意的一点是,由于查询操作并不会修改任何用户记录,所以在查询操作执行时,并不需要记录相应的 undo log。undo 日志的格式为了实现事务的原子性,InnoDB 存储引擎在实际进行增、删、改一条记录时,都需要先把对应的 undo 日志记下来。一般每对一条记录做一次改动,就对应着一条 undo 日志,但在某些更新记录的操作中,也可能会对应着 2 条 undo 日志。一个事务在执行过程中可能新增、删除、更新若干条记录,也就是说需要记录很多条对应的 undo 日志,这些 undo 日志会被从 0 开始编号,也就是说根据生成的顺序分别被称为第 0 号 undo 日志、第 1 号 undo 日志、...、第 n 号 undo 日志等,这个编号也被称之为 undo no。这些 undo 日志是被记录到类型为 FIL_PAGE_UNDO_LOG 的页面中。这些页面可以从系统表空间中分配,也可以从一种专门存放 undo 日志的表空间,也就是所谓的 undo tablesp中分配。

0
0
0
浏览量1023
攻城狮无远

【Databend】分组集:教你如何快速分组汇总

分组集定义和数据准备分组集是多个分组的并集,用于在一个查询中,按照不同的分组列对集合进行聚合运算,等价于对单个分组使用"union all",计算多个结果集的并集。Databend 常见的分组集有三种 grouping sets 、rollup 、cube 。数据准备:drop table if exists sales_data; create table if not exists sales_data ( region varchar(255), product varchar(255), sales_amount int ); insert into sales_data (region, product, sales_amount) values ('North', 'WidgetA', 200), ('North', 'WidgetB', 300), ('South', 'WidgetA', 400), ('South', 'WidgetB', 100), ('West', 'WidgetA', 300), ('West', 'WidgetB', 200);group by grouping setsgroup by grouping sets 是 group by 子句的强大扩展,允许在单个语句中计算多个 group by子句,组集是一组维度列。效果等同于同一结果集中两个或多个 group by 操作的 union all:group by grouping sets((a))等同于单分组集操作 group by a。group by grouping sets((a),(a,b))等同于 group by a union all group by a,b。基础语法:select ... from ... [ ... ] group by grouping sets ( groupset [ , groupset [ , ... ] ] ) [ ... ] -- groupset ::= { <column_alias> | <position> | <expr> }其中,column_alias 表示列的别名,position 表示 select 中列的位置,expr 表示当前范围内表上的任何表达式。根据准备的数据,需求是统计区域销量和产品销量。-- 方法一:使用 group by grouping sets 语法 select region , product , sum(sales_amount) as total_sales from sales_data group by grouping sets(region, product) order by region, product; -- 方法二:使用 union all select region ,null as product , sum(sales_amount) as total_sales from sales_data group by region union all select null as region , product , sum(sales_amount) as total_sales from sales_data group by product;根据准备的数据,需求是在原数据的基础上,统计区域销量和产品销量。select region , product , sum(sales_amount) as total_sales from sales_data group by grouping sets(region, product,(region, product)) order by region, product;group by rollupgroup by rollup 子句会在分组的基础上产生小计行以及总计行,语法如下:select ... from ... [ ... ] group by rollup ( grouprollup [ , grouprollup [ , ... ] ] ) [ ... ] -- grouprollup ::= { <column_alias> | <position> | <expr> }其中,column_alias 表示列的别名,position 表示 select 中列的位置,expr 表示当前范围内表上的任何表达式。根据准备的数据,需求是在原数据的基础上,统计区域下产品销量小计和总计数据。-- 方法一:使用 group by rollup 语法 select region , product , sum(sales_amount) as total_sales from sales_data group by rollup(region, product) order by region, product; -- 方法二:union all select region , product , sum(sales_amount) as total_sales from sales_data group by region,product union all select region ,null as product , sum(sales_amount) as total_sales from sales_data group by region union all select null as region , null as product , sum(sales_amount) as total_sales from sales_data order by region, product;这种汇总方式在分析看板里经常看到,比如 Power BI 和 Tableau 中做表格时,可以选择小计和总计。可以看到使用 group by rollup 子句能快速实现汇总,代码也简洁。group by cubegroup by cube 子句类似 group by rollup 子句,除了生成 group by rollup 子句的所有行外,还会多一些维度,对所有列交叉分组汇总。select ... from ... [ ... ] group by cube ( groupcube [ , groupcube [ , ... ] ] ) [ ... ] -- groupcube ::= { <column_alias> | <position> | <expr> }其中,column_alias 表示列的别名,position 表示 select 中列的位置,expr 表示当前范围内表上的任何表达式。根据准备的数据,需求是在原数据基础上分析所有可能情况的销售汇总。-- 方法一:使用 group by cube 语法 select region , product , sum(sales_amount) as total_sales from sales_data group by cube(region, product) order by region, product; -- 方法二:使用 group by grouping sets 子句和 union all 结合 select region , product , sum(sales_amount) as total_sales from sales_data group by grouping sets(region, product,(region, product)) union all select null as region , null as product , sum(sales_amount) as total_sales from sales_data order by region, product;总结Databend 中 grouping sets、rollup、cube 都是对 group by 的扩展,相对于 union all 来看,代码较简洁,效率也高,可以试着在实际工作中多用用,如果不支持或者理不清,使用 union all 实现的效果也是一样的。参考资料:Databend Group Bys:https://docs.databend.com/guides/query/groupby/

0
0
0
浏览量1016
秋叶无缘

从ClickHouse通往MySQL的几条道路

一、应用背景简介ClickHouse 是 Yandex(俄罗斯最大的搜索引擎)开源的一个用于实时数据分析的基于列存储的数据库,其处理数据的速度比传统方法快 100-1000 倍。ClickHouse 的性能超过了目前市场上可比的面向列的 DBMS,每秒钟每台服务器每秒处理数亿至十亿多行和数十千兆字节的数据。它是一个用于联机分析(OLAP)的列式数据库管理系统;(OLAP是仓库型数据库,主要是读取数据,做复杂数据分析,侧重技术决策支持,提供直观简单的结果)那 ClickHouse OLAP 适用场景有:1)读多于写;2)大宽表,读大量行但是少量列,结果集较小;3)数据批量写入,且数据不更新或少更新;4)无需事务,数据一致性要求低;5)灵活多变,不适合预先建模。MySQL是一个关系型数据库管理系统,广泛用于各种应用程序和网站开发。MySQL容易上手和学习,已经被广泛应用于各种生产环境中有良好的稳定性和可靠性,MySQL支持事务处理,能够保证数据的完整性和一致性,适合需要复杂数据处理和事务控制的应用。在我们应用中的使用场景来看,简单来说通常会看中了clickhouse在处理大批量数据的写入和读取分析方面的性能,MySQL会主要负责一些基于模型进行指标二次加工的高频查询及复杂join的查询。二、实际应用中存在的问题在数据相关应用处理过程中,一般会按下图的分层进行数据处理;现有的一个实际应用场景中,会把MySQL中的数据进行全量的更替,即在新一批基于ck模型加工的数据插入MySQL库表时删除原表全量数据,来实现对于最新全量数据的刷新;该处理机制因为完全不用考虑历史数据的包袱,每批次都是全量加工和替换,是一种运行简单、有效、数据加工的准确率高的机制,在小业务量场景下可以节省开发量和时间,弊端是在大业务场景下性能端会出现与之关联的多种问题;这些告警可能包括因为读写频率高引起的CPU使用率过高、因为binlog产生量过大导致的磁盘使用量告警等;负责加工的后端应用也可能也会因数据加工量过大而引发内存使用率过高的风险。基于现有架构设计和问题背景,需要对相关的问题进行一定的调研,来探索优化的可能性;三、几种处理方法及适配的场景分析1.使用数据库触发器(Trigger):在 ClickHouse 表中创建触发器,当订单数据发生变化时,触发器可以将更新操作发送到 MySQL 表中进行更新。触发器可以监视 ClickHouse 表中的 INSERT、UPDATE 和 DELETE 操作,并将相应的操作转发到 MySQL 表中。在类似于同步数据表的场景下,触发器场景比较合适,但是在面对需要高度定制化的数据加工场景下,就显得不太合适了,也不方便调试。2.此外,也会有通过外部触发器结合消息队列的方案可以支持处理这种情况。这里边会涉及到需要监听ClickHouse的binlog记录或者CDC(Change Data Capture)流,在数据发生变化时进行解析和转发。3.也可以在应用层面,来监听ClickHouse数据库的数据变化,并在变化发生时发送相关的消息到消息队列。例如使用Debezium库来监听ClickHouse数据库的数据变化。你可以根据自己的需求来配置连接信息、监听的表等,并在监听器中编写业务逻辑处理数据变化事件。这几种处理方式相对来说对于处理的变化量来说是比较大的,即所有对于数据库的操作过程都会被监听端响应处理,所以如果数据变化量非常大的话,那么监听消费端的压力也会随之上升;4.轮询查询:Java 应用可以定期轮询查询 ClickHouse 表的变化,通过比对新的订单数据和已有的订单数据,找出有变动的数据行,并进行相应的加工处理和更新操作。这其中的关键就是采用何种方法进行比对了。比对方法可以通过逐个字段的比对来筛选该行数据是否有变化,这种方法简单有效,但是瓶颈也比较明显:不适合处理大量数据,因为性能不算好;也可以通过把数据行进行哈希算法和摘要处理,来实现更快速的字段变化的比对,这种方式相对来说会更适合处理数据量大一些的场景;当然,处理过程并不限于查询过程,有些场景是在查询阶段并不需要筛选数据,而是基于原始模型加工完的数据结果上进行字段值比对或哈希处理,用来标记处理完的数据结果是否有变化,有变化的更新无则不处理,从而减小对指标结果数据的更新范围;以上内容是对于所与到问题的处理方法的一个浅显分析,如果您还有什么指标加工方面好的经验,欢迎指正和交流。

0
0
0
浏览量1020
攻城狮无远

【Databend】数据类型

数据类型列表Databend 作为一款开源、弹性、低成本,基于对象存储也可以做实时分析的新式数仓,有必要清楚支持的数据类型有哪些,通过学习收集,常见类型如下:数据大类数据类型别名字节大小类型描述整数类型tinyintint81 byte范围从-128至127整数类型smallintint162 bytes范围从-32768至32767整数类型intint322 bytes范围从-2147483648至2147483647整数类型bigintint642 bytes范围从-9223372036854775808至9223372036854775807浮点数类型float-4 byte单精度浮点数浮点数类型double-8 byte双精度浮点数浮点数类型decimal-可变用于精确十进制数值的定点类型,可以指定精度和小数点位置字符串类型varcharstring可变存储可变长度的字符串日期时间类型date-4 byte存储范围从’1000-01-01’至’9999-12-31’的日期,格式为’YYYY-MM-DD’日期时间类型timestamp-8 byte存储日期时间,格式为’YYYY-MM-DD HH:SS.ffffff’布尔类型booleanbool1 byte用于存储布尔类型0或1其他数据类型array-可变如[1, 2, 3, 4],相同数据类型的值的集合,通过其索引访问其他数据类型tuple-可变如(‘2023-02-14’,‘Valentine’),不同数据类型的值的有序集合,通过其索引访问其他数据类型map-可变如{“a”:1, “b”:2, “c”:3},一组键值对,其中每个键都是唯一的,并映射到一个值其他数据类型variantjson可变如[1,{“a”:1,“b”:{“c”:2}}],收集不同数据类型的元素,包括array和object其他数据类型bitmap-可变如0101010101,一种二进制数据类型,表示一组值,其中每个位表示值的存在或缺失通过以上表格,可以看到和 Mysql 数据类型还是存在一些差异,不过不影响,有此表格希望对你建表过程中有参考依据。转换数据类型为什么要数据类型转换,不知在使用 Databend 过程中是否遇到过以下问题。select 39 > '301';-- 报错 select concat(39,'a');-- unable to unify `UInt8` with `String`发生报错的原因都是因为类型没有统一,可能在 Mysql 没有统一也能进行比较而不会报错,但是对于 Databend 是不允许的!!!在使用其他数据库时,由于数据类型不一致也能计算,导致我们也会忽略它带来的影响,如:select 39 > 301;-- 0 select '39' > '301';-- 1字符串比较是根据每一位字符对应的的ASCII值大小进行比较,由于’9’字符比’0’大,导致 select '39' > '301';得出的结果为真。可见,当没有对数据设置正确的类型时,进行比较大小将返回错误的结果,那如果又是将比较用在了排名排序上,得出的结果也是有问题的。因此,特别需要对数据类型转换,常见的转换函数如下:函数语法函数描述数据样例cast(expr as data_type)将数据转换另一种类型cast(1 as varchar),输出’1’expr::data_type将数据转换另一种类型,cast 的别名1::varchar,输出’1’try_cast(expr as data_type)将数据转换另一种类型,错误时返回NULL1::varchar,输出’1’数据类型扩展有了数据类型列表,但是在实际应用过程中还是会有些扩展或疑惑。整数类型对于整数类型,可能会遇到“无符号整数”,我们可以进一步使用unsigned,示例如下:create table test_numeric ( tiny tinyint, tiny_unsigned tinyint unsigned, small smallint, small_unsigned smallint unsigned, i int, i_unsigned int unsigned, bigi bigint, bigi_unsigned bigint unsigned );布尔类型布尔类型,常见的就是 true 和 false,但也存在隐式转换。数值类型和字符串类型转换:0 转换为 false。任何非 0 数值都转换为 true。字符串为’true’会被转化为:true字符串为’false’会被转化为:false而且转换不区分大小写。所有其他非'TRUE'和'FALSE'文本字符串无法转换为布尔值,它将获得Code: 1010错误。浮点数类型十进制类型对于需要精确的十进制表示的应用程序非常有用,例如财务计算或科学计算。使用decimal(p, s)来指示十进制类型。p是精度,即数字中的总位数,其范围为[1,76]。s是刻度,即小数点右侧的位数,它的范围是[0,p]。如果您有decimal(10, 2),您可以存储最多 10 digits 值,小数点右侧为 2 digits。最小值为-9999999.99,最大值为9999999.99。此外,decimal 有一套复杂的精确推理规则。不同的规则将适用于不同的表达式,以推断精度。算术运算加法/减法:decimal(a, b) + decimal(x, y) -> decimal(max(a - b, x - y) + max(b, y) + 1, max(b, y))这意味着整数和十进制部分都使用两个操作数的较大值。乘法:decimal(a, b) * d(x, y) -> decimal(a + x, b + y)分部:decimal(a, b) / decimal(x, y) -> decimal(a + y, b)比较操作十进制可以与其他数字类型进行比较。十进制可以与其他十进制类型进行比较。聚合操作sum:sum(decimal(a, b)) -> decimal(max, b)avg:arg(decimal(a, b)) -> decimal(max, max(b, 4))字符串类型varchar 在 Mysql 中是可变字符串,通常会给定变长数值,但是在 Databend 上不需要给定,如下:create table string_table(text varchar);日期时间类型在 Databend 中只有两种类型 date 和 timestamp ,而且 timestamp 格式为’YYYY-MM-DD HH:SS.ffffff’,这与 Mysql 有些不同,需要注意。如要了解更多,可查看 Databend 日期时间类型。其它数据类型array 数据类型可以定义可变的,这种数据类型类似于 Python 列表,但 Python 中的列表索引是从0开始的,而 Databend 是从1开始。create table array_int64_table(arr array(int64)); select array_value[0],array_value[4],array_value[6] from (select [1, 2, 3, 4] as array_value) as t1; +----------------+----------------+----------------+ | array_value[0] | array_value[4] | array_value[6] +----------------+----------------+----------------+ | NULL | 4 | NULL +----------------+----------------+----------------+Databend对数组使用基于1的编号约定。由n个元素组成的数组以数组[1]开始,以数组[n]结束。tuple 元组是有序、不可变和异构元素的集合,在大多数编程语言中用括号()表示。换句话说,元组是不同数据类型元素的有限有序列表,一旦创建,其元素就无法更改或修改,这点和 Python 元组 类似。示例如下:create table t_table(event tuple(timestamp, varchar)); insert into t_table values(('2023-02-14 08:00:00','valentines day')); select event,event[1] from t_table; +------------------------------------------+---------------------+ | event |. event[1] | +------------------------------------------+---------------------+ | ('2023-02-14 08:00:00','valentines day') | 2023-02-14 08:00:00 | +------------------------------------------+---------------------+tuple 与 array 有点不同是如果索引不在范围内,则直接报错。map 数据结构用于保存一组Key:Value键值对,类似于 Python 字典。Key具有指定的基本数据类型,包括布尔值、数字、十进制、字符串、日期或时间戳。Key的值不能为空,也不允许重复。Value可以是任何数据类型,包括嵌套数组、元组等。select map_value1,map_value2, map_value1['k1'] from (select {'k1': 1, 'k2': 2} map_value1, map([1, 2], ['v1', 'v2']) as map_value2) as t1; +-----------------+-----------------+----------------+ | map_value1 | map_value2 | map_value1['k1'] +-----------------+-----------------+----------------+ | {'k1':1,'k2':2} | {1:'v1',2:'v2'} | 1 +-----------------+-----------------+----------------+总结通过本文对 Databend 数据类型介绍可知,合理正确使用数据类型至关重要,所以在设计表结构过程中,一定要多次甄别,如果数据类型设计或用错,可及时参照本文进行更正!!参考资料:Databend Data Type :https://databend.rs/sql/sql-reference/data-types/

0
0
0
浏览量524