分类目录归档:Database

Windows安装PostgreSQL

需要管理身份运行powershell

# 初始化数据库
D:\server\PostgreSQL\15\bin\pg_ctl.exe init 
# 开启服务
D:\server\PostgreSQL\15\bin\pg_ctl.exe start -N "postgres_15" -D "D:\server\PostgreSQL\15\data"
# 关闭服务
D:\server\PostgreSQL\15\bin\pg_ctl.exe stop -N "postgres_15" -D "D:\server\PostgreSQL\15\data"
# 注册windows系统服务并设置自动启动
D:\server\PostgreSQL\15\bin\pg_ctl.exe register -N "postgres_15" -D "D:\server\PostgreSQL\15\data" -S "auto"

运行postgres_15服务,默认本地连接时不需要密码的。

密码等设置在D:\server\PostgreSQL\15\data\pg_hba.conf

宝塔Postgresql扩展安装

以pg_stat_statements为例

编译扩展

cd /usr/local/pgsql/contrib/pg_stat_statements/
make && make install

启用扩展

切换为postgres用户sudo su - postgres,运行sql命令行psql,启用扩展

CREATE EXTENSION pg_stat_statements;

修改postgresql.conf

进入宝塔面板,修改postgres配置

shared_preload_libraries= 'pg_stat_statements'

重启postgresql

PostgreSQL之json字符字段查找

根据字段值查找

SELECT t.*
FROM table_name t
WHERE json_field->>'type'='5' and  json_field->>'value'!=''

查找并替换

update
table_name d
set cover_content=(cover_content::jsonb || jsonb_build_object('cover_img_id',((select f.id  from file f WHERE  f.path = SUBSTRING(cover_content->>'value',44))  limit 1)::text)::jsonb)
where cover_content->>'type'='5' and  cover_content->>'value'!='' and cover_content->>'cover_img_id' is null;

更新嵌套对象的键值:

如果 jsonb 字段包含嵌套对象,例如:

'{"user": {"name": "Alice", "age": 30}}'

你想更新 user 对象中的 age 为 35,可以使用:

UPDATE your_table
SET jsonb_column = jsonb_set(jsonb_column, '{user,age}', '"35"');

数组里面的字段查找

字段里面存的是数组,数组里面存的对象,可以通过以下语句直接查询对象属性的值

select *
    from cart,
jsonb_array_elements(data::jsonb) as elem where ( elem->>'cover_img_id' is null or  elem->>'cover_img_id'='') order by created_at desc;

数组字段查找并替换

update cart set data= (
  SELECT jsonb_agg(
    CASE
      WHEN element->>'cover_img_url' <> '' THEN jsonb_set(element, '{cover_img_id}',
          ((((select f.id  from file f WHERE  f.path = SUBSTRING(element->>'cover_img_url',44))  limit 1)::text)::jsonb)
          )
      ELSE element
    END
  )
  FROM jsonb_array_elements(data::jsonb) AS element
)
where id in (
    select id
    from cart,
jsonb_array_elements(data::jsonb) as elem where elem->>'cover_img_url' <> '' and ( elem->>'cover_img_id' is null or  elem->>'cover_img_id'='') order by created_at desc
    );

多级数组查找替换

select id                                as cart_id,
       cart_user_id                      as user_id,
       elem ->> 'id'           as file_id,
       (updated_at + interval '30 days') as expired_at
from cart,
     jsonb_array_elements(data::jsonb) as elem
limit 10;

update file f
set expired_at=t.expired_at
from (select id                                as cart_id,
       cart_user_id                      as user_id,
       (elem ->> 'id')::int           as file_id,
       (updated_at + interval '30 days') as expired_at
from cart,
     jsonb_array_elements(data::jsonb) as elem)
         as t
where f.id = t.file_id
  and f.expired_at < t.expired_at;

数据库究竟存0还是null或者”有标准答案

存0的情况

  • 数值型但是需要统计:比如余额,订单总额

存null的情况

  • 外键可以为空:比如admin_user_id)如果为系统操作,可以为null,这时候存0必须取消数据库约束,或者不建外键约束,都不如存null好

存”的情况

  • 非必填且填写的字符串可以为空字符串:比如外国人的middle name,存”可以方便的拼接full name,这个时候如果不需要拼接,你还是可以存null.
  • 框架不支持修改时自动将null呈现为”,且保存时自动”改为null

MySQL分段统计函数INTERVAL和ELT

需求

期末考试成绩出来了,结果需要分为不及格(0-59),合格(60-84),优秀(80+)三个段,老师需要统计每个段成绩的分布情况。

分数表score结构如下:

id int
user_id int
score int

使用INTERVAL函数对学生成绩分段

SELECT user_id,score,INTERVAL(score,60,85) as step from score

INTERVAL函数会根据score返回0(少于60),1(60-74),2(85以及以上)

INTERVAL返回的结果进行分组:

SELECT INTERVAL(score,60,85),COUNT(*) AS num as step from score GROUP BY INTERVAL(score,60,85)

得到:

0, 5
1, 80
2, 15

使用ELT函数为阶段命名

ETL方法会建立重命名,将1,2,3分别转化为不及格,合格,优秀,但是etl不能是从0开始的,所以我们可以在INTERVAL分组时加一个0,将0分以下的划分为0,0-59的划为1,依次类推

SELECT INTERVAL(score,0,60,85),COUNT(*) AS num as step from score GROUP BY INTERVAL(score,60,85)

得到:

1, 5
2, 80
3, 15

最终SQL:

SELECT ELT(INTERVAL(score,0,60,85),"不及格","合格","优秀"),COUNT(*) AS num as step from score GROUP BY ELT(INTERVAL(score,0,60,85),"不及格","合格","优秀")

得到:

不及格, 5
合格, 80
优秀, 15

Mysql必知必会

今天在v2ex看到有人问怎么建表,其实建表个人总结起来非常简单。

用面向对象方法建立表

class = table
object = row
property = field

如:(我习惯用单数,你可以用复数)

book:
id
title,
isbn

category:
id
name

book_category:
id
book_id
author_id

处理表关联关系

  • A1 对多 B:B 表加 a_id,比如 order_item 表加 order_id
  • A1 对 1B:附表加主表 id,比如 profile 表加 user_id
  • A 多对多 B:C 表关联 A,B,如 post_tag 表加 post_id 和 tag_id

如何建立索引(全文索引)

  • 通常常用的where条件增加索引,如select id,title from post where forum_id=3,这时候需要给forum_id加索引,ID是主键是不要加的,title也不要加
  • 数据库模糊查询(如搜索“Mysql 数据库”)需要建立全文索引(参考我的另外一篇文章:Mysql中文全文索引(含实例5分钟上手)
  • 排序可以加组合索引,如select id,title from post where forum_id=3 order by create_time desc,这时候可以加forum_id+create_time的组合索引,Mysql高版本组合索引可以设置排序
  • 组合索引单独查后部分不起作用,如上面的forum_id+create_time的组合索引,如果你只根据create_time查那么不起作用
  • 唯一字段可以加唯一索引

Mysql性能工具

  • explain可以分析sql性能,每个字段的含义都需要清楚
  • slow query log:Mysql的配置中开启慢速查询日志,慢速查询日志需要用慢查询分析工具来分析,不是用肉眼来看的
  • show full processlist可以查看当前数据库运行的查询
  • infomation_schema有一大堆有用东西

其他

  • null还是0:建议采用null表示没有,这样不会影响数据库的约束

有用的资源

Mysql 数据库磁盘空间优化

查看那张表占用空间大

USE information_schema;

SELECT
TABLE_SCHEMA,
TABLE_NAME,
DATA_LENGTH,
INDEX_LENGTH
FROM
`TABLES`
ORDER BY
DATA_LENGTH DESC;

修改表采用压缩

USE mydb;

ALTER TABLE mytable ROW_FORMAT = DYNAMIC;

OPTIMIZE TABLE mytable;

如果没有 DYNAMIC,那么采用 COMPRESSED 也可以

修改表采用 tokudb (需要安装 tokudb 引擎)

ALTER TABLE `mytable`
ENGINE=tokudb;

修改字符集

ALTER TABLE `mytable`
DEFAULT CHARACTER SET=gbk

删除日志文件

第一步:登陆进入mysql,并使用 show binary logs; 查看日志文件。

mysql> show binary logs;

第二步:查看正在使用的日志文件:show master status;

mysql> show master status;
+------------------+-----------+--------------+------------------+-------------------+
| File             | Position  | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+-----------+--------------+------------------+-------------------+
| mysql-bin.000005 | 425994852 |              |                  |                   |
+------------------+-----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

当前正在使用的日志文件是mysql-bin.000005,那么删除日志文件的时候应该排除掉该文件。

mysql> purge binary logs to 'mysql-bin.000005';

删除除mysql-bin.000005以外的日志文件。

mysql 定时清理日志文件

如果每次等到发现空间不足的时候才去手动删除日志文件,这种方式是很不理想的。

那么,我们就需要设置mysql,让它能自动清理日志文件。

编辑mysql的配置文件,设置expire_logs_days(mysql定时删除日志文件)

[root@sam ~]# vim /etc/my.cnf

在my.cnf中,添加或修改expire_logs_days的值 (这里设置的自动删除时间为10天, 默认为0不自动删除)

expire_logs_days=10

修改后,重启mysql就会生效。

但是,在生产环境中,重启mysql数据库往往会付出很高的代价。

于是,可以在不重启mysql的情况下,修改expire_logs_days值

如下:

mysql> show variables like '%log%';
mysql> set global expire_logs_days = 10;

设置完后,可以通过 show variables like ‘%log%’; 看到expire_logs_days的值已被修改成10。

注意:通过这种方式设置expire_logs_days虽然不需要重启mysql即可生效,但是该方式在重启mysql之后,值会被恢复。

于是,建议通过mysql命令设置expire_logs_days的同时,也修改/etc/my.cnf下的expire_logs_days=10配置,这样在下次重启mysql的时候,expire_logs_days也一样是10;

参考

族谱的数据设计

需求分析

  • 族谱有一个特点就是写入少,所以可以建立一个缓存表。
  • 维护关系尽量简单
  • 查询上下几代尽量快速,避免递归

数据库结构

people 表

id 
name 姓名

relation 表

id
family_id 族号 一个用户可能存在多个族谱中,比如外嫁后进入其他族谱
people_id 用户编号
level 代系
relate_people_id 关联人,
type 关系:如丈夫,妻子,儿子,孙子,情人
order 第几个孩子,第几任妻子

索引

alter table relation create index family_level(family_id,level);

查询(高性能将上下三代查出来)

select * from relation where family_id=3 and level between current_level-3,current_level+3;

分析

这两个表记录基本不用改(除非录错了),任何时候都是增加记录即可,所以维护也非常简单。

界面展示上,查询出上下几代,然后名字作为主要展示标签,关系用连线展示,关系和人是多对多,完美解决关系混乱的问题。

level 代表是族谱里面的第几代。

如果发现了新的祖先,然么当然自己代数要加一。

比如李渊原来 level 是 1 代,然后认了老子做祖宗,那么他就不 1 代了,老子是 1 代。

族谱草图

file

参考

Mysql update select同一张表

根据下级状态更新上级状态

原来语句

SET display = 1
WHERE
    id IN (
        SELECT
            a.pid
        FROM
            area a
        WHERE
            a.display = 1
        AND pid != 0
    );

提示:[Err] 1093 – You can’t specify target table ‘area’ for update in FROM clause

修改后

UPDATE area a1,(
        SELECT
            pid
        FROM
            area
        WHERE
            display = 1
        AND pid != 0
    ) a2
SET a1.display = 1
where a1.id=a2.pid;

Mysql中文全文索引(含实例5分钟上手)

数据库配置

[mysqld]
ngram_token_size=1 # 如果需要搜索一个字,需要改为1

innodb_ft_min_token_size

默认3,表示最小3个字符作为一个关键词,增大该值可减少全文索引的大小

innodb_ft_max_token_size

默认84,表示最大84个字符作为一个关键词,限制该值可减少全文索引的大小

ngram_token_size

默认2,表示2个字符作为内置分词解析器的一个关键词,如对“abcd”建立全文索引,关键词为’ab’,’bc’,’cd’

一般来说,查询正好等于ngram_token_size的词,速度会更快,但是查询比它更长的词或短语,则会变慢

如果需要搜索一个字,需要改为1

注意 这三个参数均不可动态修改,修改了这些参数,需重启MySQL服务,并重新建立全文索引

建立数据库

CREATE TABLE `article` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(200) DEFAULT NULL,
  `body` text,
  PRIMARY KEY (`id`),
  FULLTEXT KEY `ft_idx` (`title`,`body`) WITH PARSER ngram
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

INSERT INTO `test`.`article` (`id`, `title`, `body`) VALUES ('1', 'MySQL数据库权威指南', '非常不错的书籍,值得一看');
INSERT INTO `test`.`article` (`id`, `title`, `body`) VALUES ('2', 'Oracle数据库精选', '不妨看看');
INSERT INTO `test`.`article` (`id`, `title`, `body`) VALUES ('3', 'SQL Servr 数据库进阶', '不容错过');
INSERT INTO `test`.`article` (`id`, `title`, `body`) VALUES ('4', 'postgreq 数据库进阶', '知道了吗');

这里注意全文索引使用了WITH PARSER ngram

开始搜索

SELECT * FROM article WHERE MATCH(title,body) AGAINST ('精选 值得')
SELECT * FROM article WHERE MATCH(title,body) AGAINST ('精选 值得' IN NATURAL LANGUAGE MODE))
SELECT * FROM article WHERE MATCH(title,body) AGAINST ('精选 值得' IN BOOLEAN MODE))

1、自然语言模式(NATURAL LANGUAGE MODE)

自然语言模式是MySQL 默认的全文检索模式。自然语言模式不能使用操作符,不能指定关键词必须出现或者必须不能出现等复杂查询。

2、BOOLEAN模式(BOOLEAN MODE)

BOOLEAN模式可以使用操作符,可以支持指定关键词必须出现或者必须不能出现或者关键词的权重高还是低等复杂查询。

参考