博客链接: http://codeshold.me/2017/10/learning_mysql_mariadb_notes.html
- 本文是《MySQL与MariaDB学习指南》的简要学习笔记,方便日后查询和回忆!
- 原书容较为基础,但够用了,尤其常用函数和数据导入导出写的很不错! 适合初学者以及对mysql了解不用很深的人(仅会用)! 原书不涉及任何sql的优化!
- 发现了书中两处错误,已提交勘误!
- 进阶读物《深入理解MariaDB和MySQL》
- 作者网站mysqlresources.com有很多资料
docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root --name mariadb -d mariadb:latest
docker exec -it mariadb bash
docker start mariadb
登陆说明
root@b2659b21f321:/# mysql -uroot -proot # 默认用户名和密码为root
Welcome to the MariaDB monitor. Commands end with ; or \g. # 命令要以分号(;) 或斜线 +g(\g)结尾
Your MariaDB connection id is 10 # 本次连接标识符
Server version: 10.2.7-MariaDB-10.2.7+maria~jessie mariadb.org binary distribution
Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. # 可清除之前缓存的输入一半的mysql命令
MariaDB [(none)]>
help 帮助命令, 最有用的 help contents
help
help Data Manipulation
help SHOW DATABASES
prompt
可修改提示符SET @fav_site_total =
show tables from msyql;
显示mysql数据库中的所有表describe mysql.user;
CREATE SCHEMA swf
== CREATE DATABASE swf
CREATE DATABASE swf2 CHARACTER SET latin1 COLLATE latin1_bin;
# MySQL 数据的存储方式是二进制拉丁字符ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_bin
, 首先是表类型,或者说是该表使用的存储引擎的类型; 剩下两个是表的默认字符集(latin1)和默认校对方式(latin1_bin)mysqldump --user='russell' -p rookery birds > /tmp/birds.sql
mysql --user='russell' -p rookery < rookery-ch2-end.sql
CREATE TABLE test.birds_new LIKE birds;
仅复制表结构CREATE TABLE birds_new_alternative SELECT * FROM rookery.birds;
复制表结构和表数据ALTER TABLE birds ADD COLUMN test INT
ALTER TABLE birds_new ADD COLUMN wing_id CHAR(2);
ALTER TABLE birds_new ADD COLUMN wing_id CHAR(2) AFTER family_id;
# 在特定的列之后加入一列ALTER TABLE birds_new DROP COLUMN wing_id;
ALTER TABLE birds_new ADD COLUMN bill_id CHAR(2) AFTER body_id, ADD COLUMN endangered BIT DEFAULT b'1' AFTER bill_id;
可同时添加多列ALTER TABLE birds_new CHANGE COLUMN common_name common_name VARCHAR(255);
在使用CHANGE COLUMN时,就算只想修改该列的某一方面, 也需要完整地声明整个新列。MODIFY COLUMN子句的语法只要求输入一次列名, MODIFY COLUMN是不能用来 改列名的
ALTER TABLE birds_new
MODIFY COLUMN endangered
ENUM('Extinct',
'Extinct in Wild',
'Threatened - Critically Endangered',
'Threatened - Endangered',
'Threatened - Vulnerable',
'Lower Risk - Conservation Dependent',
'Lower Risk - Near Threatened',
'Lower Risk - Least Concern')
AFTER family_id;
SHOW COLUMNS FROM birds_new LIKE 'endangered' \G
结果仅显示 endangered 列的设定
SHOW FULL COLUMNS
显示更全的信息ALTER TABLE birds_new ALTER conservation_status_id DROP DEFAULT;
修改默认值的设定SELECT auto_increment FROM information_schema.tables WHERE table_name = 'birds';
ALTER TABLE birds AUTO_INCREMENT = 10;
RENAME TABLE table1_altered TO table1;
RENAME TABLE rookery.birds TO rookery.birds_old, test.birds_new TO rookery.birds;
SHOW TABLES FROM mysql LIKE 'plugin';
或者 SHOW TABLES IN rookery LIKE 'birds%';
SHOW INDEX FROM birdwatchers.humans \G
ALTER TABLE birdwatchers.humans ADD INDEX human_names (name_last, name_first);
添加索引EXPLAIN
语句返回的信息中, possible_keys域表示可能用到的键, key 域表示确实用到的键ALTER TABLE conservation_status DROP PRIMARY KEY, CHANGE status_id conservation_status_id INT PRIMARY KEY AUTO_INCREMENT;
SHOW WARNINGS \G
DELETE FROM bird_families WHERE family_id = 101;
INSERT INTO ...... VALUES ......
INSERT IGNORE INTO ......
指示服务器忽略所有错误,并插入那些没有产生错误的行, 接下来可以通过SHOW WARNINGS 显示那些有问题的行UPDATE ....... SET ...... WHERE ......
REPLACE INTO ...... VALUES ......
或 REPLACE INTO ...... SELECT ......
INSERT LOW_PRIORITY INTO bird_sightings
LOW_PRIORITY
和 HIGH_ PRIORITY
,因为 InnoDB 只锁定相关的行而不锁定整个表,所以这两个选项对它没有意义。WHERE common_name REGEXP 'Great|Least' AND common_name NOT REGEXP 'Hawk-Owl'
UPDATE humans SET formal_title = SUBSTRING(formal_title, 1, 2);
在MySQL使用UPDATE的多表语法时,不能带有ORDER BY或LIMIT——但在 UPDATE 单表时就可以
UPDATE prize_winners, humans
SET winner_date = CURDATE()
WHERE winner_date IS NULL
AND country_id = 'uk'
AND prize_winners.human_id = humans.human_id
ORDER BY RAND() # 随机
LIMIT 2; # 只更新两行
INSERT...ON DUPLICATE KEY UPDATE...
INSERT INTO humans
(formal_title, name_first, name_last, email_address, better_birders_site)
VALUES('Mr','Barry','Pilson', '[email protected]', 1),
('Ms','Lexi','Hollar', '[email protected]', 1),
('Mr','Ricky','Adams', '[email protected]', 1)
ON DUPLICATE KEY
UPDATE better_birders_site = 2; # 在重复的行上设置那个字段为2
多表删除操纵, DELETE FROM table[, table] USING table[, . . . ] [WHERE condition];
DELETE FROM humans, prize_winners
USING humans JOIN prize_winners
WHERE name_first = 'Elena'
AND name_last = 'Bokova'
AND email_address LIKE '%yahoo.com'
AND humans.human_id = prize_winners.human_id;
连接时在左表(humans)中找不到对应行,右表(prize_winners)的数据也会被删除
DELETE FROM prize_winners
USING humans RIGHT JOIN prize_winners
ON humans.human_id = prize_winners.human_id
WHERE humans.human_id IS NULL;
因为使用 UNION 时,MySQL 只会取第一个 SELECT 的域名作为最终结果集的 标题,而之后的 SELECT 的域名都会被忽略
不要把 USING...JOIN
和 JOIN...USING
搞混了
SELECT book_id, title, status_name
FROM books
JOIN status_names ON(status = status_id);
SELECT book_id, title, status_name
FROM books
JOIN status_names USING(status_id);
UPDATE birds
LEFT JOIN conservation_status USING(conservation_status_id)
JOIN bird_families USING(family_id)
SET birds.conservation_status_id = 9
WHERE bird_families.scientific_name = 'Ardeidae'
AND conservation_status.conservation_status_id IS NULL;
动态列
- 文本作为参数时,需要使用引号
- 列作为参数时,不要用引号——否则列名会被当成文本。如果列名是保留字或含有 可能引起问题的字符,可用
反引号
标示列名。- 如果字符串函数返回的值过长(即返回了太多的字符串),超出了系统限制(
max_allowed_packet
选项),MySQL 就会返回 NULL。- 有些参数用于指定字符串中字符的位置。字符串第一个字符的位置是1,不是0。 当需要从后往前数时(有些函数允许这么做),最后一个字符的位置是 -1。
- 有些参数用于表示字符串长度。如果用到小数,MySQL就会将其四舍五入为最接 近的整数。
字符串函数
CONCAT()
, CONCAT_WS('|', ...)
指定分隔符 mysql -p --skip-column-names -e "" > tmp.txt
, –skip-column-names 隐藏列名LCASE(common_name)
同 LOWER()
, UCASE(bird_families.scientific_name)
同 UPPER()
QUOTE(common_name)
接受字符串输入,然后将其用单引号包围, 会对某些字 符进行转换, 包括单引号、反斜杠、空(零)字节,以及 Ctrl-Z 字符RTRIM(name_first)
, LTRIM()
, TRIM()
RPAD(common_name, 20, '.' )
, LPAD()
, SPACE()
在 网页上显示的话,则要用
(不换行空格)来填充LEFT(prospect_name, 2)
, MID(prospect_name, 5, 25)
, RIGHT(prospect_name, 25)
SUBSTRING(prospect_name, 1, 2)
第三个参数表示个数SUBSTRING(prospect_name FROM 5 FOR 25)
, 其中25 代表抽取长度SUBSTRING(prospect_name, -25)
SUBSTRING_INDEX(prospect_name, '|', 1)
, SUBSTRING_INDEX(prospect_name, '|', -1)
第三个参数表示抽取个数LOCATE('Avocet', common_name)
返回Avocet子字符串第一次出现的地方日期和时间函数
NOW()
有几个同义词:CURRENT_TIMESTAMP()
、LOCALTIME()
和 LOCALTIMESTAMP()
NOW()
所返回的日期和时间是它所在的 SQL 语句开始执行时的日期和时间SYSDATE()
,它返回的是自身被执行时的那个时间点(不是整条语句的结束时间)SELECT NOW(), SLEEP(4) AS 'Zzz', SYSDATE(), SLEEP(2) AS 'Zzz', SYSDATE();
CURDATE( ), CURTIME( ), UNIX_TIMESTAMP()
HOUR(time_seen), MINUTE(time_seen), SECOND(time_seen)
EXTRACT(YEAR_MONTH FROM time_seen), EXTRACT(HOUR_MINUTE FROM time_seen), EXTRACT(MONTH FROM time_seen)
DATE_FORMAT(time_seen, '%W, %M %e, %Y')
TIME_FORMAT(time_seen, '%l:%i %p')
SELECT GET_FORMAT(DATETIME, 'ISO'), GET_FORMAT(DATE, 'ISO')
可以查看时间的标准格式SHOW VARIABLES LIKE 'time_zone';
CONVERT_TZ()
接受三个参数:日期和时间,来自哪个时区,想转换成哪个时区mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -p -u root mysql
安装时区文件SET GLOBAL time_zone = 'GMT';
, default-time-zone='GMT'
DATE_ADD()
和 DATE_SUB()
, 语法一样:第一个参数是要修改的日期,第二个参数是时间量, 时间量的写法是:在 INTERVAL 关键字后接上数字和单位(例如INTERVAL 1 DAY)DATE_ADD(membership_expiration, INTERVAL 3 MONTH)
, DATE_ADD(membership_expiration, INTERVAL -1 YEAR)
DATE_SUB(membership_expiration, INTERVAL 1 YEAR)
DATE_ADD(time_seen, INTERVAL '1 2' DAY_HOUR)
TIME_TO_SEC()
和 SEC_TO_TIME()
PERIOD_ADD()
, 接受两个参数,第一个是日期,第二个是想要增加的月的数量, 它接受的参数不是日期类型,而是字符串类 型,同时,返回值也是字符串PERIOD_ADD( EXTRACT(YEAR_MONTH FROM CURDATE()), -3)
DATE_ADD()
的别名 ADDDATE(), DATE_SUB()
的别名 SUBDATE())QUARTER(CURDATE())
DATEDIFF() 和 TIMEDIFF()
TIME_TO_SEC(), AVG(), MIN(avg_time), MAX(avg_time)
STDDEV() 和 VARIANCE()
标准差、方差GROUP_CONCAT()
, 可以把一个组所有的值拼接成一个以逗号分隔的串ROUND()
, FLOOR()
, CEILING()
TRUNCATE()
ABS()
SIGN()
POWER(2, 8)
会返回2的8次方,即256; PI()
会返回π,即3.141593用户账号: 用户名与主机的组合”
用户账户
SHOW GRANTS FOR 'lena_stankoska';
, SHOW GRANTS FOR 'lena_stankoska'@localhost \G
GRANT ALL ON rookery.* TO 'lena_stankoska'@'localhost';
DROP USER 'lena_stankoska'@'localhost';
, DROP USER 'lena_stankoska'@'%';
CREATE USER 'lena_stankoska'@'localhost' IDENTIFIED BY 'her_password_123';
GRANT USAGE ON *.* TO 'lena_stankoska'@'lena_stankoska_home' IDENTIFIED BY 'her_password_123';
SELECT PASSWORD('her_password_123');
, 从 MySQL 5.6 版本开始,包含 PASSWORD 的语句不会被记录GRANT SELECT (human_id, formal_title) ON birdwatchers.humans TO 'lena_stankoska'@'lena_stankoska_home';
备份和导入账号
LOAD DATA INFILE
语句需要 FILE 权限GRANT ALL PRIVILEGES ON rookery.* TO 'admin_granter'@'localhost' IDENTIFIED BY 'avocet_123' WITH GRANT OPTION;
REVOKE ALL PRIVILEGES ON rookery.* FROM 'michael_stone'@'localhost';
REVOKE ALTER ON rookery.* FROM 'admin_restore'@'localhost';
SELECT User, Host FROM mysql.user WHERE User LIKE '%michael%' OR User LIKE '%stone%';
SHOW PROCESSLIST;
获取会话标识符,如1482, 接着使用KILL 1482;
ALTER USER 'admin_granter'@'localhost' PASSWORD EXPIRE;
SET PASSWORD FOR 'admin_granter'@'localhost' = PASSWORD('some_pwd_123');
UPDATE mysql.user SET Password=PASSWORD('new_pwd') WHERE User='root';
FLUSH PRIVILEGES;
rt-reset.sql
,并放在受保护的目录中。然后用 --init-file
, 启动 MySQL,如下mysqld_safe --init-file=/root/rt-reset.sql &
CREATE ROLE 'admin_import_role';
GRANT FILE ON *.* TO 'admin_import_role'@localhost;
SET ROLE 'admin_import_role';
… , SET ROLE NONE;
mysqldump --user=admin_backup --password --lock-all-tables --all-databases > /data/backups/all-dbs.sql
-u
代表 --user
但是这些简短形式现在已不被推荐,甚至在未来的版本中可能会被取消。--single- transaction
选项。这能提高数据的一致性。--ignore-table=mysql.user, 可以忽略mysql.user数据库
将多个插入语句扩展成一个, P216 有详细字段说明
mysql --user=admin_restore --password < rookery.sql
SHOW BINARY LOGS;
log-bin
binlog-ignore-db=mysql
SHOW MASTER STATUS
查看日志是否生效SHOW VARIABLES WHERE Variable_Name LIKE 'datadir';
查看日志文件存放目录mysqlbinlog --database=rookery /data/mysql/mysqlresources-bin.000002 > recovery-research.txt
mysqlbinlog --database=rookery --start-position="1284889" --to-last-log ...
mysqlbinlog --database=rookery --stop-datetime="140916 13:10:24" ...
mysqlbinlog --database=rookery --start-datetime="140916 13:10:29" --to-last-log
导入 LOAD DATA INFILE
-- 统一导入
LOAD DATA INFILE '/tmp/Clements-Checklist-6.9-final.csv'
INTO TABLE rookery.clements_list_import
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' -- 指定域用的是双引号
IGNORE 1 LINES; -- 忽略指定的行数
-- 导入对应的域
LOAD DATA INFILE '/tmp/Clements-Checklist-6.9-final.csv' INTO TABLE rookery.clements_list_import
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
IGNORE 1 LINES
(id, change_type, @niente, @niente,
@niente, bird_order, family, @niente,
@niente, @niente, @niente, @niente); -- 那些不想要的域则被导入了临时变量 @niente
-- 设置列(family)
LOAD DATA INFILE '/tmp/Clements-Checklist-6.9-final.csv' INTO TABLE rookery.clements_list_import
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
IGNORE 1 LINES
(id, change_type, @niente, @niente,
@niente, bird_order, @family, @niente,
@niente, @niente, @niente, @niente, @niente)
SET family = SUBSTRING(@family, 1, LOCATE(' (', @family) );
-- 比较全的
LOAD DATA INFILE '/tmp/birdwatcher-prospects.csv'
INTO TABLE birdwatchers.birdwatcher_prospects_import
FIELDS TERMINATED BY '|' ENCLOSED BY '"' ESCAPED BY '\\'
LINES STARTING BY '[' TERMINATED BY ']\r\n' -- STARTING BY 行以左中括号开头; TERMINATED BY指定行由右中括号、回车和换行符结尾
IGNORE 1 LINES
(prospect_name, prospect_email, prospect_country);
忽略错误,LOAD DATA INFILE ... IGNORE INTO TABLE ...
LOAD DATA INFILE ... REPLACE INTO TABLE ...
使用mysqlimport, 所有的选项与LOAD DATA INFILE的一样,只是变成了小写,并以两个横杠开头, 但是没有--lines-starting-by
选项,所以支持不够
mysqlimport –user='marie_dyer' --password='sevenangels' \
--replace --low-priority --ignore-lines='1' \
--fields-enclosed-by='"' --fields-terminated-by='|' --fields-escaped-by='\\' \ --lines-terminated-by=']\r\n' \
--columns='prospect_name, prospect_email, prospect_country' \
birdwatchers '/tmp/birdwatcher_prospects_import.csv'
没有FILE权限也能导入数据 P253
P258
mysql.connector
sql注入 – 推荐使用占位符?
,而不是字符拼接,前者会对传入的参数转义
my $sql_stmnt = "SELECT human_id,
CONCAT(name_first, SPACE(1), name_last) AS full_name,
membership_expiration
FROM humans
WHERE name_last LIKE ?"; -- 问号是占位符
$sql_stmnt = "SELECT common_name, scientific_name
FROM birds
WHERE common_name LIKE '%$search_parameter%'" -- 字符拼接