写在前文:
近期由于公司业务产品发展需要,要求项目逐渐国产化:(1)项目国产操作系统部署;(2)数据库国产化;国产数据库最终选型为highgo(瀚高),该数据库基于pg开发,所以要求先将mysql适配到postgresql数据库;
dockerhub官网选取自己想安装的版本(https://hub.docker.com/_/postgres/tags),我这里选取的是13.9版本;
拉取镜像到本地
docker pull postgres:13.9
docker run --name 1.postgres \ --restart=always \ -e POSTGRES_PASSWORD='jY%kng8cc&' \ -p 5432:5432 \ -v /data/postgresql:/var/lib/postgresql/data \ -d postgres:13.9
-- 建库 CREATE DATABASE "test" WITH OWNER = "testuser" -- 数据库用户 ; -- 创建模式 CREATE SCHEMA "test" AUTHORIZATION "test"; -- 设置默认查询模式 pg连接时默认使用public这个schmel,想让项目连接时使用自己创建的模式需要修改一下默认查询模式 ALTER ROLE testuser SET search_path="test";
这里放上一个学习地址,大家可以参照性学习,边使用边学习
https://www.sjkjc.com/postgresql/psql-commands/
postgresql-42.2.10(支持PostgreSQL 42)
JDK 8 - JDBC 4.2 Support for JDBC4 methods is not complete, but the majority of methods are implemented.
pg驱动版本:springboot2.5.14中默认集成的是42.2.25
org.postgresql postgresqlruntime
# 1.postgres为容器名称,也可以直接指定ip pring.datasource.url=jdbc:postgresql://1.postgres:5432/test?autoReconnect=true&autoReconnectForPools=true&useUnicode=true&characterEncoding=utf8&createDatabaseIfNotExist=true&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull spring.datasource.username=testuser spring.datasource.password=test spring.datasource.driver-class-name=org.postgresql.Driver
mysql | postgresql |
TINYINT | SMALLINT |
SMALLINT | SMALLINT |
MEDIUMINT | INTEGER |
BIGINT | BIGINT |
FLOAT | REAL |
DOUBLE | DOUBLE PRECISION |
BOOLEAN | BOOLEAN |
TINYTEXT | TEXT |
TEXT | TEXT |
MEDIUMTEXT | TEXT |
LONGTEXT | TEXT |
BINARY(n) | BYTEA |
VARBINARY(n) | BYTEA |
TINYBLOB | BYTEA |
BLOB | BYTEA |
MEDIUMBLOB | BYTEA |
LONGBLOB | BYTEA |
DATE | DATE |
TIME | TIME [WITHOUT TIME ZONE] |
DATETIME | TIMESTAMP [WITHOUT TIME ZONE] |
TIMESTAMP | TIMESTAMP [WITHOUT TIME ZONE] |
AUTO_INCREMENT | SERIAL , BIGSERIAL |
column ENUM (value1, value2, […] | column VARCHAR(255) NOT NULL, CHECK (column IN (value1, value2, […])) pg可以自定义数据类型实现类似效果: CREATE TYPE mood AS ENUM ('sad','ok','happy'); CREATE TABLE person ( current_mood mood ... ) |
语法差异 | mysql | postgresql | 是否相同 |
分页 | select * from t1 limit 2,2; | select * from tbl limit 2 offset 2; | 否 |
插入数据时:如果不存在则insert,存在则update | replace实现 | upsert | 否 |
大小写兼容 | 通过配置可兼容 | 表字段或表名为大写时,字段或表名必须添加双引号 | 否 |
if(), case when | if(), case when 条件1 then 符合值 else 不符合值 end; | case when 条件1 then 符合值 else 不符合值 end; | 否 |
round(字段,小数位数) | round(字段,小数位数) | round(case(‘字段’ as numeric),小数位数) | 否 |
null值判断 | 支持 ifnull(),NVL(),COALESCE() | 支持COALESCE() | 否 |
Update-单表更新 | 相同 | 相同(不可全表更新) | 是 |
update-更新单表多个字段 | 相同 | 相同 | 是 |
update-更新并返回 | select tem1,tem2 from update test set tem1 = '',tem2 = '' | UPDATE test SET tem1 = '',tem2 = '' RETURNING tem2,tem2; | 否 |
Update表关联更新 | 相同 | 相同 | 是 |
Insert-单行插入 | 相同 | 相同 | 是 |
Insert-插入指定字段 | 相同 | 相同 | 是 |
insert-插入多行 | 相同 | 相同 | 是 |
insert-插入并返回 | 不支持 | INSERT INTO() RETURNING did | 否 |
Insert-插入,存在则更新 | INSERT INTO () VALUE() ON DUPLICATE KEY UPDATE name = EXCLUDED.name | INSERT INTO distributors ( did , dname ) VALUES ( 9 , ' Antwerp Design' ) ON CONFLICT (did)DO UPDATE SET name = EXCLUDED.name | 否 |
insert-不存在插入,存在更新 | replace实现 | upsert语句 | 否 |
SELECT | 相同 | 相同 | 是 |
DELETE | DELETE FROM table | DELTE FROM table(不可全表删除) | 是 |
DELETE | DELETE FROM table WHERE | DELETE FROM table WHERE | 是 |
DELETE-删除并返回 | 不支持 | DELETE FROM table WHERE RETURNING * ; | |
INDEX-add | 支持alter,create创建 | 支持create | |
INDEX-delete | 支持alter,drop | 支持drop | |
字符串常量 | 支持单双引号 | 支持双引号 | 否 |
插入数据时自增主键 | 写法一:insert into t1(name) values(‘zhangshan’); 写法二:insert into t1(id, name) values(null, ‘zhangshan’); | insert into t1(name) values(‘zhangshan’); | 否 |
库名长度 | 无强制限制 | 库名、表名限制命名长度,建议表名及字段名字符总长度小于等于63。 |
网上有很多转换工具,有些需要收费,这里借助一个最简单最常用的工具-navicat premium(我这里使用的是16版本)转换mysql数据结构到PG的数据结构
点击database选择你想转换的数据库,这里选择postgreSQL,下面选择版本
至此,一份pg的数据结构就保存完整了(注意:此时转换出来的数据结构会存在一些错误,还需要额外手动处理一些问题)
(1)时间字段的CURRENT_TIMESTAMP默认值丢失
解决方案:
-- 从mysql默认表information_schema中获取默认为CURRENT_TIMESTAMP列的信息 SELECT TABLE_NAME,column_name,column_default,extra FROM information_schema.columns WHERE table_schema = 'bigdata-web' and column_default is not null AND column_default = 'CURRENT_TIMESTAMP'; -- 拼接所有时间字段默认为CURRENT_TIMESTAMP的alter 语句,提取到脚本中执行 SELECT CONCAT("ALTER TABLE \"bigdata-web\".\"",TABLE_NAME, "\" ALTER COLUMN ","\"",column_name,"\"", " SET DEFAULT ", column_default, ";") FROM information_schema.columns WHERE table_schema = 'bigdata-web' and column_default is not null AND column_default = 'CURRENT_TIMESTAMP';
(2)时间字段类型的on update CURRENT_TIMESTAMP,PG中无该使用方式
解决方案:
利用触发器和pg扩展函数实现更新数据时更新时间字段值
-- 安装pg扩展函数moddatetime(使用pg的useradmin用户) create extension moddatetime; ALTER FUNCTION "moddatetime"() OWNER TO "test用户"; -- 触发器语句:create trigger gmt_modified_timestamp_trigger before update on test_ly for each row execute procedure moddatetime(gmt_modified); -- 查询所有设置了on update CURRENT_TIMESTAMP的列 SELECT TABLE_NAME,COLUMN_NAME,EXTRA,DATA_TYPE FROM information_schema.columns WHERE table_schema = 'bigdata-web' and column_default is not null AND column_default != '' AND column_default = 'CURRENT_TIMESTAMP' AND EXTRA = 'on update CURRENT_TIMESTAMP'; -- 拼接处理默认值为on update CURRENT_TIMESTAMP字段类型的默认值,添加触发器的语句 SELECT CONCAT("create trigger ", COLUMN_NAME, "_trigger ","before update on \"", TABLE_NAME, "\" for each row execute procedure moddatetime(\"",COLUMN_NAME,"\");") FROM information_schema.columns WHERE table_schema = 'bigdata-web' and column_default is not null AND column_default != '' AND column_default = 'CURRENT_TIMESTAMP' AND EXTRA = 'on update CURRENT_TIMESTAMP';
(3)其他默认值可以参考上面拼接处理,如字符串,数字
解决方案
-- 拼接所有默认值为字符串的alter语句 SELECT TABLE_NAME,column_name,column_default,DATA_TYPE,extra FROM information_schema.columns WHERE table_schema = 'bigdata-web' and column_default is not null AND column_default != 'CURRENT_TIMESTAMP' AND DATA_TYPE = 'varchar' AND column_default != ''; SELECT CONCAT("ALTER TABLE \"bigdata-web\".\"",TABLE_NAME, "\" ALTER COLUMN ","\"",column_name,"\"", " SET DEFAULT '", column_default, "';") FROM information_schema.columns WHERE table_schema = 'bigdata-web' and column_default is not null AND column_default != 'CURRENT_TIMESTAMP' AND DATA_TYPE = 'varchar' AND column_default != ''; -- 拼接默认值字段为enum的alter语句 SELECT * FROM information_schema.columns WHERE table_schema = 'bigdata-web' and column_default is not null AND column_default != '' AND DATA_TYPE = 'enum'; SELECT CONCAT("ALTER TABLE \"bigdata-web\".\"",TABLE_NAME, "\" ALTER COLUMN ","\"",column_name,"\"", " SET DEFAULT '", column_default, "';") FROM information_schema.columns WHERE table_schema = 'bigdata-web' and column_default is not null AND column_default != '' AND DATA_TYPE = 'enum'; -- 查询默认值为数字的列,拼接alter语句 除了tinyint(1) TABLE_NAME,column_name,column_default,DATA_TYPE,extra SELECT * FROM information_schema.columns WHERE table_schema = 'bigdata-web' and column_default is not null AND column_default != 'CURRENT_TIMESTAMP' AND DATA_TYPE IN('tinyint', 'int', 'bigint') AND column_default != '' AND COLUMN_TYPE != 'tinyint(1)' and table_name = 't_user' SELECT CONCAT("ALTER TABLE \"bigdata-web\".\"",TABLE_NAME, "\" ALTER COLUMN ","\"",column_name,"\"", " SET DEFAULT ", column_default, ";") FROM information_schema.columns WHERE table_schema = 'bigdata-web' and column_default is not null AND column_default != 'CURRENT_TIMESTAMP' AND DATA_TYPE IN('tinyit', 'int', 'bigint') AND column_default != '' AND COLUMN_TYPE != 'tinyint(1)'; -- double SELECT * FROM information_schema.columns WHERE table_schema = 'bigdata-web' and column_default is not null AND DATA_TYPE IN('double') AND column_default != ''; SELECT CONCAT("ALTER TABLE \"bigdata-web\".\"",TABLE_NAME, "\" ALTER COLUMN ","\"",column_name,"\"", " SET DEFAULT ", column_default, ";") FROM information_schema.columns WHERE table_schema = 'bigdata-web' and column_default is not null AND DATA_TYPE IN('double') AND column_default != ''; -- decimal SELECT * FROM information_schema.columns WHERE table_schema = 'bigdata-web' and column_default is not null AND DATA_TYPE IN('decimal') AND column_default != ''; SELECT CONCAT("ALTER TABLE \"bigdata-web\".\"",TABLE_NAME, "\" ALTER COLUMN ","\"",column_name,"\"", " SET DEFAULT ", column_default, ";") FROM information_schema.columns WHERE table_schema = 'bigdata-web' and column_default is not null AND DATA_TYPE IN('decimal') AND column_default != '';
(4)MYSQL的tinyint(1)(业务代码中boolean值)转换为了int2
navicat工具转换映射时将mysql的bit(1)转换为了int2,需要处理该部分字段
-- tinyint SELECT * FROM information_schema.columns WHERE table_schema = 'test-database' AND COLUMN_TYPE = 'tinyint(1)' ORDER BY TABLE_NAME -- 批量转换语句拼接
navicat工具转换时将自增id设置丢失了
解决方案:
(1)修改建表语句,使用SERIAL关键字
(2)修改已经创建的表的某个字段为自增
--1、在PostgreSQL当中,我们实现ID自增首先创建一个关联序列,以下sql语句是创建一个从1开始的序列: CREATE SEQUENCE menu_id_seq START 1; --2、设置该字段默认值nextval('menu_id_seq'::regclass) ALTER TABLE menu ALTER COLUMN id SET DEFAULT nextval('menu_id_seq'::regclass);
序号 | 场景 | 示例 |
1 | mapper接口方法上使用注解编写sql语句 | @SELECT("SELECT * FROM TEST") |
2 | mapper的xml文件中的语句 | |
3 | mybatis-plus使用的实体类相关特殊列名修改 | |
4 | mybatis-plus使用QueryWrapper条件构造相关列修改 | |
5 | 代码中使用字符串拼接的sql语句 |
序号 | 差异关键字 | 差异描述 | mysql示例 | pg示例 |
1 | limit | limit 0,1 改成 limit 1 offset 0 | select * from t_user limit 0,1 | select * from t_user limit 1 offset 0 |
2 | 字段大小写 | 查询字段为驼峰命名,加上双引号,select \"startTime\" form ... | - | - |
3 | ifnull | 没有ifnull函数,改用COALESCE()函数, | select ifnull(avatar, 'aa'),"name" from t_user; | select COALESCE(avatar, 'aa'),"name" from t_user; |
4 | DATE_SUB() | 没有DATE_SUB()时间计算函数,改用select now() + '1 seconds' 或 select now() + '-1 seconds' | select DATE_SUB(logintime,INTERVAL 1 DAY) from t_user; | select logintime + '1 days' from t_user; |
5 | 别名大小写 | 查询字段的别名也需要用双引号包起来,select start_time as \"startTime\" from ... | - | - |
6 | 正则表达式、REGEXP | 正则表达式匹配,where taget_name ~ '^123$|asd'...,其中~为匹配正则表达式区分大小写,~*为不区分大小写,前面加叹号则为不匹配正则表达式如:!~ | select * from t_user where name REGEXP '^adm*'; | select * from t_user where name ~ '^adm*'; |
7 | binary | mysql的where判断加上binary来区分大小写,where binary id = ‘abc’,在pg中是直接区分大小写的,将binary去掉就行 | select * from t_user where binary name = 'ADMIN'; | select * from t_user where name = 'ADMIN'; |
8 | group_concat_max_len | set session group_concat_max_len=...在pg中没有,注释 | - | - |
9 | GROUP_CONCAT() | GROUP_CONCAT()函数没有,使用array_to_string(array_agg(target_name), ',') from ... 代替 | select GROUP_CONCAT(industry) from t_company group by province; | select array_to_string(array_agg(industry), ',') from t_company group by province; |
10 | ISNULL | ISNULL没有使用is null来进行判断 | select * from t_user where ISNULL(avatar); | select * from t_user where avatar is null; |
11 | &&,|| | &&和||没有这个符号,用and和or替换 | select * from t_user where ISNULL(avatar) && realname = '日志管理员'; | select * from t_user where avatar is null and realname = '日志管理员'; |
12 | date_format() | date_format()函数用不了,换成 to_char,select to_char(create_time, 'yyyy-MM-dd hh24:mi:ss') from ... | select date_format(logintime, '%Y-%m-%d') from t_user; | select to_char(logintime, 'yyyy-MM-dd') from t_user; |
13 | if() | 没有if()函数,改用 case when 条件 then 值 else 值 end | select if(name = 'admin', true, false) as isAdmin from t_user | select case when name = 'admin' then true else false end as isAdmin from t_user |
14 | FIND_IN_SET() | where FIND_IN_SET('123', user_ids) ... 使用不了,换成 where '123' = ANY(string_to_array(user_id, ',')) ... | select * from t_company where find_in_set('浙江省', address); | select * from t_company where '浙江省' = ANY(string_to_array(address, ',')); |
15 | 数字字符串比较、连表 | mysql中能直接对数字和字符串进行=相等判断,pg不行,换成,'123' = cast(123 as VARCHAR) 或者 123 = cast('123' as INTEGER);或者123 = '123'::INTEGER 例如 select * from t_model_layout_task_record as r left join t_model_layout_form as f on r.formId = f.id,其中r.formId是vachar,f.id是int,这样连表是报错的, 改成:select * from t_model_layout_task_record as r left join t_model_layout_form as f on r.formId = cast(f.id as VARCHAR) | select * from t_model_layout_task_record as r left join t_model_layout_form as f on r.formId = f.id | select * from t_model_layout_task_record as r left join t_model_layout_form as f on r.formId = cast(f.id as VARCHAR) |
16 | SYSDATE() | 没有 SYSDATE() 函数,换成 NOW() | select SYSDATE(); | select now(); |
17 | from_unixtime() | 没有from_unixtime()函数,换成to_timestamp() | select from_unixtime(1673833489); | select to_timestamp(1673833489); |
18 | auto_increment | mysql查询information_schema.tables的auto_increment字段获取主键自增的值,而pg的information_schema.tables中不存在auto_increment。 pg通过该函数 pg_get_serial_sequence(‘库名.表名’, '自增字段名') 获取表的自增值 | - | - |
19 | unix_timestamp() | 没有unix_timestamp()函数,换成date_part('epoch', now())::integer,例如,select date_part('epoch', start_time)::integer from t_model_layout_task_record | select unix_timestamp(createtime) from t_user; | select date_part('epoch', createtime)::integer from t_user; |
20 | ON DUPLICATE KEY UPDATE | mysql的存在则更新写法,pg换成, 改成: INSERT INTO 表名(列1,列2...) VALUES ('值1', '值2', ...)ON CONFLICT(唯一或排除约束字段名) DO UPDATE SET 列1='值', 列2='值', ...; 如果是批量插入的话改成: INSERT INTO 表名(列1,列2...) VALUES ('值1', '值2', ...)ON CONFLICT(唯一或排除约束字段名) DO UPDATE SET 列1=excluded.列1, 列2=excluded.列2, ...; 需要注意的是: 唯一或排除约束字段名必须是一个唯一索引或唯一联合索引,如果填写多个唯一索引则不生效,如果要生效的话,必须把他们建立成一个唯一联合索引。 | insert into t_user(id,name,logintime,PASSWORD,createuser,updateuser) values(1,'test',now(),'test',1,1) on duplicate key update logintime = values(logintime); | insert into t_user(id,name,logintime,"PASSWORD",createuser,updateuser) values(1,'test',now(),'test',1,1) ON CONFLICT(id) DO UPDATE SET logintime=excluded.logintime; |
21 | instr() | 没有instr()函数,改成like。例: SELECT * FROM user WHERE INSTR(username,'2')>0 SELECT * FROM user WHERE username like '%2%' | select * from t_user where INSTR(name,'a')>0 | select * from t_user where name like '%a%' |
22 | ` ` | 查询的字段 ` 号换成双引号,select \"name\" from ... | select `name` from t_user; | select "name" from t_user; |
23 | 表名大小写 | 表名称中含有大写字母需要用双引号将表格名称包含起来 | select PASSWORD from t_user; | select "PASSWORD" from t_user; |
24 | 表名别名大小写 | 查询结果别名中有大写需要用双引号包含起来如果不包含起来查询结果会自动转为小写,select area_id as "areaId" from ... | - | - |
25 | GROUP BY | GROUP BY 分组查询不能查不在分组内的数据(也不能对非GROUP BY的字段进行ORDER BY排序),需要变成连表查询,链表查需要注意是否有重复数据。 例如: SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname ORDER BY wmname;(查询失败) 如果没有重复数据换成: SELECT m.cname, m.wmname, t.mx FROM ( SELECT cname, MAX(avg) AS mx FROM makerar GROUP BY cname ) t JOIN makerar m ON m.cname = t.cname AND t.mx = m.avg ORDER BY m.wmname ; 如果有重复数据需要进行去重则换成:(利用窗口函数) SELECT cname, DISTINCT ON(wmname)wmname wmname, MAX(avg) OVER (PARTITION BY wmname) AS mx FROM makerar 如果需要对不是去重的字段进行排序则在最外层加上排序: SELECT * FROM ( SELECT cname, DISTINCT ON(wmname)wmname wmname, MAX(avg) OVER (PARTITION BY wmname) AS mx FROM makerar ) bb ORDER BY m.wmname | select name,count(type) from t_company group by type order by type desc; | select aa.name, aa.count from (select distinct on(type)type "type",name,count(type) OVER (PARTITION BY type) from t_company) aa order by aa.type desc; |
26 | uuid() | uuid()函数不存在,安装扩展函数 create extension "uuid-ossp"; | select uuid(); | select uuid_generate_v4(); |
27 | 时间模糊查询 | 在进行时间模糊查询的时候需要转换一下时间,例如: SELECT * FROM xxx WHERE to_char(create_time, 'yyyy-MM-dd hh24:mi:ss') LIKE CONCAT(#{mouth},'%') | select realname,logintime from t_user where logintime like '%2023%' | select realname,logintime from t_user where to_char(logintime, 'yyyy-MM-dd hh24:mi:ss') like '%2023%' |
28 | replace into | 在业务先删除在插入。 如果可以的话可以用下面的写法,看业务需要 pg 没有这个语法, 如果根据以为能改成存在更新不存在就插入的话可以用以下写法: INSERT INTO 表名(列1,列2...) VALUES ('值1', '值2', ...)ON CONFLICT(唯一或排除约束字段名) DO UPDATE SET 列1='值', 列2='值', ...; 如果是批量插入的话改成: INSERT INTO 表名(列1,列2...) VALUES ('值1', '值2', ...)ON CONFLICT(唯一或排除约束字段名) DO UPDATE SET 列1=excluded.列1, 列2=excluded.列2, ...; 需要注意的是: 唯一或排除约束字段名必须是一个唯一索引或唯一联合索引,如果填写多个唯一索引则不生效,如果要生效的话,必须把他们建立成一个唯一联合索引。 | - | - |
29 | like | 如果对int字段进行like判断需要将字段转为VARCHAR类型,例如: where state::VARCHAR like concat('%', #{state},'%') ... | select id from t_user where id like '%2023%' | select id from t_user where id::varchar like '%2023%' |
30 | 时间/between and | 如果判断between and是时间, 情况1、数据库是timestamp,and两边是字符串格式为'yyyy-MM-dd HH:mm:ss'则 where to_char(create_time, 'yyyy-MM-dd hh24:mi:ss') between #{startTime} and #{endTime} | select * from t_user where logintime between '2023-01-01 00:00:00' and '2023-01-16 23:59:59' | select * from t_user where to_char(logintime, 'yyyy-MM-dd hh24:mi:ss') between '2023-01-01 00:00:00' and '2023-01-16 23:59:59' |
31 | 关键字 | 关键字: name、value、label、moudle 需要双引号包含起来 | - | - |
32 | 运算符号 + | mysql 返回结果可直接通过+拼接字符串, pg不支持,改成concat方法 | - | - |
33 | 实体类 | 实体类里面@TableField里写的字段有大写或者 ` 号的要改成双引号,例如 @TableField("\"order\"")、@TableField("\"createTime\"") | - | - |
34 | curdate() | 没有curdate()函数,换成,current_date。注意没有括号例如:select current_date; | select curdate(); | select current_date; |
35 | inet_aton()、inet6_aton() | 没有inet_aton()和inet6_aton()函数,使用inet()函数进行判断 | select inet_aton('1.1.1.1') | select inet('1.1.1.1') |
36 | locate() | 没有locate()函数,换成position() | select locate('b','ayyvkhlbm') | select position('b' in 'ayyvkhlbm') |
37 | ORDER BY | 按照指定字段值排序 | ORDER BY FIELD (`field`, value1, value2) | 使用CASE WHEN END替代(eg:ORDER BY CASE field WHEN value1 THEN WHEN value2 THEN 2 END) |
38 | convert() | 没有convert()函数,用convert_from()替换 | convert(vendor using gbk) | convert_from(vendor::bytea, 'gbk') |
39 | bool字段查询和插入,不可以使用0,1;修改PG数据库内置转换规则SQL语句: update pg_cast set castcontext='i' where castsource ='integer'::regtype and casttarget='boolean'::regtype; | update pg_cast set castcontext='i' where castsource ='integer'::regtype and casttarget='boolean'::regtype; | -- eg: type为bool类型 insert into test_ly(id, type) VALUES(2,0);
select * from test_ly where type = 1; | -- eg: type为bool类型 insert into test_ly(id, type) VALUES(2,0);
select * from test_ly where type = 1; |
40 | 关联字段,条件查询字段类型不一致时,产生报错问题;强制转换,如转换为字符串 ::varchar,bigint ::BIGINT | ::BIGINT | - | select u.id from t_user as u left join t_cascade_work_order w on u.id = w.author_id::BIGINT; |
41 | substring_index() | PG中无该函数,创建自定义函数,实现该功能 CREATE OR REPLACE FUNCTION substring_index(varchar, varchar, integer) RETURNS varchar AS $$ DECLARE tokens varchar[]; length integer ; indexnum integer; BEGIN tokens := pg_catalog.string_to_array($1, $2); length := pg_catalog.array_upper(tokens, 1); indexnum := length - ($3 * -1) + 1; IF $3 >= 0 THEN RETURN pg_catalog.array_to_string(tokens[1:$3], $2); ELSE RETURN pg_catalog.array_to_string(tokens[indexnum:length], $2); END IF; END; $$ IMMUTABLE STRICT LANGUAGE PLPGSQL; |