0
点赞
收藏
分享

微信扫一扫

PostgreSQL的学习心得和知识总结(六十五)|关于PostgreSQL数据库 实现MySQL数据库find_in_set()函数 的实现方案

猎书客er 2022-03-21 阅读 92

注:提前言明 本文借鉴了以下博主、书籍或网站的内容,其列表如下:


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>

小结一下:

  1. 正常返回的是在 set 里面的对应下标(从1开始)
  2. 若是集合里面有多个 返回第一次的下标

设计实现思路说明

因为这个功能比较简单,下面直接说一下思路:

  1. 这个函数功能比较简单,我们没有必要去复杂化
  2. 因为PostgreSQL默认加载了 plpgsql 插件,我们选择的语言就是 plpgsql
  3. 将函数find_in_set放到 #2 中的插件中即可
  4. 函数首先要对参数进行判断;利用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,有兴趣的小伙伴们可以自行前去开发!

举报

相关推荐

0 条评论