目录结构
注:提前言明 本文借鉴了以下博主、书籍或网站的内容,其列表如下:
PostgreSQL数据库 实现MySQL数据库find_in_set函数

文章快速说明索引
学习目标:
做数据库内核开发久了就会有一种 少年得志,年少轻狂 的错觉,然鹅细细一品觉得自己其实不算特别优秀 远远没有达到自己想要的。也许光鲜的表面掩盖了空洞的内在,每每想到于此,皆有夜半临渊如履薄冰之感。为了睡上几个踏实觉,即日起 暂缓其他基于PostgreSQL数据库的兼容功能开发,近段时间 将着重于学习分享Postgres的基础知识和实践内幕。
备注1:
遇一本好书,很难得
读一本好书,很重要
我给大家推荐一本书:《PostgreSQL指南:内幕探索》,点击前往
学习内容:(详见目录)
1、PostgreSQL数据库 兼容MySQL数据库find_in_set()函数 的实现
学习时间:
2022年03月20日 13:46:39
学习产出:
1、PostgreSQL数据库基础知识回顾 1个
2、CSDN 技术博客 1篇
3、PostgreSQL数据库内核深入学习
注:下面我们所有的学习环境是Centos7+PostgreSQL14.2+Oracle11g+MySQL8.0
postgres=# select version();
version
-----------------------------------------------------------------------------
PostgreSQL 14.2 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 7.1.0, 64-bit
(1 row)
postgres=#
#-----------------------------------------------------------------------------#
SQL> select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
PL/SQL Release 11.2.0.1.0 - Production
CORE 11.2.0.1.0 Production
TNS for Linux: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 - Production
SQL>
#-----------------------------------------------------------------------------#
mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.27 |
+-----------+
1 row in set (0.06 sec)
mysql>
问题描述背景说明
在开始之前,先来看一下 MySQL的官方手册 find_in_set函数,点击前往,如下:
# FIND_IN_SET(str,strlist)
mysql> SELECT FIND_IN_SET('b','a,b,c,d');
-> 2
官方解释如下:
Returns a value in the range of 1 to N if the string str is in the string list strlist consisting of N substrings.
A string list is a string composed of substrings separated by , characters.
If the first argument is a constant string and the second is a column of type SET, the FIND_IN_SET() function is optimized to use bit arithmetic.
Returns 0 if str is not in strlist or if strlist is the empty string.
Returns NULL if either argument is NULL.
This function does not work properly if the first argument contains a comma (,) character.
翻译如下:
/*
* 如果字符串 str 在由 N 个子字符串组成的字符串列表 strlist 中,则返回 1 到 N 范围内的值。
*
* 字符串列表是由 , 字符分隔的子字符串组成的字符串。
*
* 如果第一个参数是一个常量字符串,而第二个参数是一个 SET 类型的列,则 FIND_IN_SET() 函数被优化为使用位算术。
*
* 如果 str 不在 strlist 中或 strlist 是空字符串,则返回 0。
* 如果任一参数为 NULL,则返回 NULL。
*
* 如果第一个参数包含逗号 (,) 字符,此函数将无法正常工作。
*/
示例如下:
mysql> SELECT FIND_IN_SET('b','a,b,c,d');
+----------------------------+
| FIND_IN_SET('b','a,b,c,d') |
+----------------------------+
| 2 |
+----------------------------+
1 row in set (0.00 sec)
mysql> SELECT FIND_IN_SET('e','a,b,c,d');
+----------------------------+
| FIND_IN_SET('e','a,b,c,d') |
+----------------------------+
| 0 |
+----------------------------+
1 row in set (0.00 sec)
mysql> SELECT FIND_IN_SET('e','');
+---------------------+
| FIND_IN_SET('e','') |
+---------------------+
| 0 |
+---------------------+
1 row in set (0.00 sec)
mysql> SELECT FIND_IN_SET('','a,b,c,d');
+---------------------------+
| FIND_IN_SET('','a,b,c,d') |
+---------------------------+
| 0 |
+---------------------------+
1 row in set (0.00 sec)
mysql>
特殊情况如下:
mysql> SELECT FIND_IN_SET('a,b','a,b,c,d');
+------------------------------+
| FIND_IN_SET('a,b','a,b,c,d') |
+------------------------------+
| 0 |
+------------------------------+
1 row in set (0.00 sec)
mysql> SELECT FIND_IN_SET(null,'a,b,c,d');
+-----------------------------+
| FIND_IN_SET(null,'a,b,c,d') |
+-----------------------------+
| NULL |
+-----------------------------+
1 row in set (0.00 sec)
mysql> SELECT FIND_IN_SET('e',null);
+-----------------------+
| FIND_IN_SET('e',null) |
+-----------------------+
| NULL |
+-----------------------+
1 row in set (0.00 sec)
mysql> SELECT FIND_IN_SET(null,null);
+------------------------+
| FIND_IN_SET(null,null) |
+------------------------+
| NULL |
+------------------------+
1 row in set (0.00 sec)
mysql>
mysql> SELECT FIND_IN_SET('a','a,b,c,d');
+----------------------------+
| FIND_IN_SET('a','a,b,c,d') |
+----------------------------+
| 1 |
+----------------------------+
1 row in set (0.00 sec)
mysql> SELECT FIND_IN_SET('a','a,b,c,d,a');
+------------------------------+
| FIND_IN_SET('a','a,b,c,d,a') |
+------------------------------+
| 1 |
+------------------------------+
1 row in set (0.00 sec)
mysql>
小结一下:
- 正常返回的是在 set 里面的对应下标(从1开始)
- 若是集合里面有多个 返回第一次的下标
设计实现思路说明
因为这个功能比较简单,下面直接说一下思路:
- 这个函数功能比较简单,我们没有必要去复杂化
- 因为PostgreSQL默认加载了 plpgsql 插件,我们选择的语言就是 plpgsql
- 将函数find_in_set放到 #2 中的插件中即可
- 函数首先要对参数进行判断;利用string_to_array进行下一步处理
注意:这里返回的是下标,因此网上的其他方案 诸如用 ANY/SOME 的,就可以不用看了。关于 ANY/SOME 的原理解析,可以去看我之前的博客:PostgreSQL的学习心得和知识总结(六十三)|详解PostgreSQL数据库 ANY/SOME&&ALL 的使用及原理,点击前往
因为这个功能开发起来非常简单,有兴趣的小伙伴们 可以自行去开发!下面是功能的使用和测试
功能实现结果测试
postgres=# select version();
version
-----------------------------------------------------------------------------
PostgreSQL 14.2 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 7.1.0, 64-bit
(1 row)
postgres=# \dx
List of installed extensions
Name | Version | Schema | Description
---------+---------+------------+------------------------------
plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language
(1 row)
postgres=# \df find_in_set
List of functions
Schema | Name | Result data type | Argument data types | Type
------------+-------------+------------------+------------------------+------
pg_catalog | find_in_set | integer | str text, strlist text | func
(1 row)
postgres=#
postgres=# SELECT FIND_IN_SET('b','a,b,c,d');
find_in_set
-------------
2
(1 row)
postgres=# SELECT FIND_IN_SET('e','a,b,c,d');
find_in_set
-------------
0
(1 row)
postgres=# SELECT FIND_IN_SET('e','');
find_in_set
-------------
0
(1 row)
postgres=# SELECT FIND_IN_SET('','a,b,c,d');
find_in_set
-------------
0
(1 row)
postgres=# SELECT FIND_IN_SET('a,b','a,b,c,d');
find_in_set
-------------
0
(1 row)
postgres=# SELECT FIND_IN_SET(null,'a,b,c,d');
find_in_set
-------------
(1 row)
postgres=# SELECT FIND_IN_SET('e',null);
find_in_set
-------------
(1 row)
postgres=# SELECT FIND_IN_SET(null,null);
find_in_set
-------------
(1 row)
postgres=#
postgres=# SELECT FIND_IN_SET('a','a,b,c,d');
find_in_set
-------------
1
(1 row)
postgres=# SELECT FIND_IN_SET('a','a,b,c,d,a');
find_in_set
-------------
1
(1 row)
postgres=#
注:下面来看一种不一样的例子:
postgres=# CREATE TABLE tb_test (
postgres(# id int NOT NULL,
postgres(# name text NOT NULL,
postgres(# list text NOT NULL,
postgres(# PRIMARY KEY (id)
postgres(# );
CREATE TABLE
postgres=#
postgres=# INSERT INTO tb_test VALUES (1, 'name', 'mike,allen,jack,jay');
INSERT 0 1
postgres=# INSERT INTO tb_test VALUES (2, 'name2', 'jay,pojo,jay');
INSERT 0 1
postgres=# INSERT INTO tb_test VALUES (3, 'name3', 'allen,mike,yago');
INSERT 0 1
postgres=# SELECT id,name,list from tb_test WHERE FIND_IN_SET('jay',list) != 0;
id | name | list
----+-------+---------------------
1 | name | mike,allen,jack,jay
2 | name2 | jay,pojo,jay
(2 rows)
postgres=#
在MySQL中,可以直接如下使用,但是PostgreSQL不行 where后面必须是 bool 值,大家在使用的时候注意一下即可:
mysql> SELECT id,name,list from tb_test WHERE FIND_IN_SET('jay',list);
+----+-------+---------------------+
| id | name | list |
+----+-------+---------------------+
| 1 | name | mike,allen,jack,jay |
| 2 | name2 | jay,pojo,jay |
+----+-------+---------------------+
2 rows in set (0.00 sec)
mysql> SELECT id,name,list from tb_test WHERE FIND_IN_SET('jay',list) != 0;
+----+-------+---------------------+
| id | name | list |
+----+-------+---------------------+
| 1 | name | mike,allen,jack,jay |
| 2 | name2 | jay,pojo,jay |
+----+-------+---------------------+
2 rows in set (0.00 sec)
mysql>
postgres=# SELECT id,name,list from tb_test WHERE FIND_IN_SET('jay',list);
2022-03-20 15:45:25.455 CST [43569] ERROR: argument of WHERE must be type boolean, not type integer at character 40
2022-03-20 15:45:25.455 CST [43569] STATEMENT: SELECT id,name,list from tb_test WHERE FIND_IN_SET('jay',list);
ERROR: argument of WHERE must be type boolean, not type integer
LINE 1: SELECT id,name,list from tb_test WHERE FIND_IN_SET('jay',lis...
^
postgres=#
后面 我会上传资源patch,有兴趣的小伙伴们可以自行前去开发!