Software engineering notes

Database - MySQL Command and Syntax

登入 mysql (使用 -p 密碼登入)

root@test:/# mysql -u webadmin -p
root@test:/# mysql -u webadmin --password=00000000 db_name

連入遠端 mysql

mysql -h devHostName -u test -p
mysql>    #按ENTER是換第二行輸入,
          #不是送出而是繼續輸入,
          #如果不要再輸入了加上分號(;)表示結束
          #quit 或 ctrl + d : 中斷

進入 local mysql server (in docker)

mysql -h localhost -P 3306 --protocol=tcp -u root

Console

看目前連到 MySQL 的 process

SHOW PROCESSLIST;
.. or ...
SHOW FULL PROCESSLIST;
+--------+----------+-------------------+-------+---------+------+----------+-----------------------+
| Id     | User     | Host              | db    | Command | Time | State    | Info                  |
+--------+----------+-------------------+-------+---------+------+----------+-----------------------+
| 530566 | api      | 10.0.21.49:43537  | my-log | Async commit | 1 | cleaned up | INSERT INTO `device_log` (`model`, `did`, `uid`, `mac`, `category`, `action`, `ip`, `log_date`, `occurred_at`, `created_at`, `timestamp_milli`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

KILL PROCESS: 如果對 MySQL 下的指令造成 LOCK TABLE 或其他效能上的影響, 想取消的話, 可以刪除這個 process

先找出 process id

SHOW PROCESSLIST;
| 284349 | root      | 10.0.1.224:52018  | my-log     | Query   | 2568 | copy to tmp table               | ALTER TABLE `user` CHANGE `name` `name` VARCHAR(100) CHARACTER SET utf8 COLLATE |

mysql> kill 284349;
Query OK, 0 rows affected (0.00 sec)

Show schema threads

SELECT * FROM performance_schema.threads;

其他系統指令

SHOW ENGINE INNODB STATUS;
SHOW GLOBAL STATUS;

Show running queries

SELECT * FROM INFORMATION_SCHEMA.PROCESSLIST WHERE COMMAND != 'Sleep';

Shows all queries running for 5 seconds or more:

SELECT * FROM INFORMATION_SCHEMA.PROCESSLIST WHERE COMMAND != 'Sleep' AND TIME >= 5;

Show all running UPDATEs:

SELECT * FROM INFORMATION_SCHEMA.PROCESSLIST WHERE COMMAND != 'Sleep' AND INFO LIKE '%UPDATE %';

目前連接 mysql 的數量

SHOW STATUS WHERE `variable_name` = 'Threads_connected';

最多可以同時幾個連線 (注意,如果超過這個數量,mysql 就會回 Too many connections 錯誤),如果使用 AWS RDS MySQL 要參考它不同 size 支援的 max_connections 量

SHOW VARIABLES LIKE 'max_connections';

MySQL version

SHOW VARIABLES LIKE "%version%";
或
select version();

binlog 是否開啟

SHOW VARIABLES LIKE 'log_bin';

slow query 是否開啟

SHOW VARIABLES LIKE '%slow%';

將執行過的 Query 寫到 log 檔

SET GLOBAL general_log = 'ON';

查詢指令使用index情況,在開頭加上EXPLAIN

XPLAIN SELECT *
FROM  `user_log`
WHERE uid =2

顯示系統狀態(詳細)

show status;

顯示系統狀態(簡單)

status;

查看自已的權限

mysql> show grants;

查看某個 user 的權限

show grants for test@'localhost';

列出 mysql 的 user

mysql> select user, host from mysql.user;

查看權限

select * from mysql.user where user='test';

查看 mysql 帳戶密碼及 host

select host, user, password from mysql.user;

mysql privileges

SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, EVENT, TRIGGER, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, EXECUTE

查看 mysql 帳號

desc mysql.user;

新增 User

use mysql;
INSERT INTO user(host,user,password) VALUES('127.0.0.1','test',password('qwer7890'));

限定權限

GRANT SELECT,INSERT,UPDATE,DELETE,CREATE ON testDB.* TO test@localhost IDENTIFIED BY 'qwer7890';
FLUSH PRIVILEGES;

所有權限

GRANT ALL PRIVILEGES ON *.* TO test@localhost IDENTIFIED BY 'qwer7890' WITH GRANT OPTION;
等於
GRANT ALL PRIVILEGES ON *.* TO test@localhost IDENTIFIED BY PASSWORD '*9B25933BE82583D0F86E4AB11A340DE6A3611ACD' WITH GRANT OPTION;

Show cache

show variables like '%cache%';

顯示資料庫清單

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| exam               |
| mysql              |
| package            |
| performance_schema |
| phpmyadmin         |
| test               |
+--------------------+
7 rows in set (0.02 sec)

選擇資料庫

mysql> use test
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed

顯示資料表清單

mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| test           |
+----------------+
1 row in set (0.00 sec)

顯示table的欄位資訊

mysql> show columns from test;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| name  | varchar(40) | NO   |     | NULL    |                |
| hobby | varchar(40) | NO   |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

或者:

mysql> desc test;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| name  | varchar(40) | NO   |     | NULL    |                |
| hobby | varchar(40) | NO   |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

顯示資料表的 TABLE Schema

mysql> show CREATE table test;
+-------+------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                   |
+-------+------------------------------------------------------------------------------------------------+
| test  | CREATE TABLE `test` (
          `name` varchar(80) DEFAULT NULL
        ) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

顯示 user 開頭的 table 列表

SHOW tables like 'user%';

Create database

CREATE DATABASE db_name

Use database

USE db_name

Drop database

DROP DATABASE db_name

DROP TABLE IF EXISTS `info`;

Create table

CREATE TABLE table_name (no char(4), name char(10));

CREATE TABLE `info` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `model` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
  `version` varchar(10) COLLATE utf8_unicode_ci NOT NULL,
  `url` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
  `create_time` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `version` (`model`,`version`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Drop table

DROP TABLE table_name;

Rename table name

RENAME TABLE table_name TO tt;

Display database list

SHOW DATABASES;

Display table list

SHOW TABLES;

Show 各 table 的筆數, 容量等等

SHOW TABLE STATUS;

Display table detail

DESC table_name;

Alter column

ALTER TABLE qq_tab CHANGE qq_col qq_col varchar(80) NOT NULL;

Add column

ALTER TABLE tt ADD phone varchar(40);

Primary key

ALTER TABLE gearman_queue ADD COLUMN `id` INT(10) UNSIGNED PRIMARY KEY AUTO_INCREMENT;

Replace

UPDATE table_name SET field=replace(field, "android", "iphone");
UPDATE `supe_spacenews` SET `message` = REPLACE(`message`,'要替換的文字','替換後的文字') WHERE `message` LIKE '%要替換的文字%'

將欄位改成 allow null

ALTER TABLE my_table MODIFY url varchar(100);

Show index

SHOW INDEX FROM my_table;

Foreign key

SET FOREIGN_KEY_CHECKS=0;
SET FOREIGN_KEY_CHECKS=1;

顯示 Table 的 Index

SHOW INDEX FROM users;

Before making an index unique, check first

SELECT column1, column2 FROM my_table GROUP BY column1, column2 HAVING COUNT(*) > 1;

Show index of a table

SHOW INDEX FROM table_name;

刪除資料表內的所有資料,並且重置AUTO_INCREMENT

TRUNCATE TableName

LIKE IN 語法怎麼辦? 使用 REGEXP

SELECT * FROM students WHERE name REGEXP 'bob|test|joyce';

設定 AUTO_INCREMENT

ALTER TABLE tbl AUTO_INCREMENT = 5;

更新與 users.uid 的關聯 parent_id

UPDATE parents set parent_id = (SELECT uid from users where users.username = parents.parent) where parents.parent != ''

匯入匯出資料庫

mysqldump 無法將密碼寫在指令上 e.g. --password=00000000, 所要要改用 config 的方式

/home/web-admin/db_backup/my.cnf:

    [mysqldump]
    password=00000000

mysqldump --defaults-file=/home/web-admin/db_backup/my.cnf --opt -u root my_db > my_db_backup.sql

匯出test資料庫, user為test, 密碼送出才輸入, 匯出到目前的目錄, 檔案名稱為test_backup.sql

mysqldump --opt -u test -p db_name > db_name_backup.sql

建立 test 使用者及建立 test 資料庫

mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, EVENT, TRIGGER ON *.* TO 'test'@'localhost' IDENTIFIED BY PASSWORD '*A8950BFBF9522A31DA227E7E1E751E2DC0691964' WITH GRANT OPTION;
mysql> create database db_name

匯入 test 資料庫 (不論是匯入 database 或是 table, 寫法都一樣)

mysql -u root -p db_name < db_name_backup.sql

匯出單筆 user 資料

mysqldump -u root my_db user -w "email='test_user@gmail.com'"

匯出 test DB, 無資料(但仍保留AUTO_INCREMENT)

mysqldump --opt -u test -p --no-data test > test_backup.sql

如果使用 innoDB 可能會有 lock 情況造成無法匯出, 加上 --lock-tables=false

匯出 test DB 裡的 qq table, 無資料(但仍保留AUTO_INCREMENT)

mysqldump --opt -u test -p --no-data test qq > test_qq_backup.sql

匯出 test DB 的 groups 資料表 uid=6 的資料

mysqldump -u root test groups -w "uid=6" > group_test.sql

匯出 test DB, 無資料, AUTO_INCREMENT重置

mysqldump -u test -p --no-data --skip-add-drop-table test | sed 's/AUTO_INCREMENT=[0-9]*\b//' > test_backup.sql

匯出遠端 mysql 資料

mysqldump -h sqlserver.com -u me -p123 me_test user -w "name='test'" > qq

Host : sqlserver.com
Account : me
Password : 123
DB : me_test
Table : user
WHERE 條件 : name='test'

程式裡會用到的語法

INSERT

INSERT INTO test2 (name, passwd) VALUES ('test', '123');

INSERT IF NOT EXISTS

INSERT INTO promote_grab (m_id)
SELECT (2)
FROM DUAL
WHERE NOT EXISTS(SELECT 1 FROM promote_grab WHERE m_id = 2)

插入 m_id = 2 到 promote_grab, 如果 promote_grab 不存在 m_id =2 :

INSERT IGNORE (if exists)

INSERT IGNORE INTO books (id, title, author, year_published) VALUES (1, 'Green Eggs and Ham', 'Dr. Seuss', 1960)

插入一樣的 index 如果已存在就會 ignore 不會噴 error

INSERT or UPDATE IF EXISTS

INSERT INTO table (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE name="A", age=19

INSERT Table to another one : 將 user 資料表的資料都 insert 到 user2 資料表 :

INSERT INTO `user2` SELECT * FROM `user`

Insert or Update

INSERT INTO student (id, name) VALUES('1', 'test') ON DUPLICATE KEY UPDATE name='Bob'

UPDATE

UPDATE question_options SET question_id = 20, optional_item = 5 WHERE id = 64;

UPDATE with EXISTS

UPDATE suppliers
SET supplier_name = (SELECT customers.customer_name
                     FROM customers
                     WHERE customers.customer_id = suppliers.supplier_id)
WHERE EXISTS (SELECT *
              FROM customers
              WHERE customers.customer_id = suppliers.supplier_id);

DELETE

DELETE FROM videos WHERE id=1;

DELETE with EXISTS

DELETE FROM suppliers
WHERE EXISTS (SELECT *
              FROM orders
              WHERE suppliers.supplier_id = orders.supplier_id);

將 time 欄位加30天

DATE_ADD(time, INTERVAL 30 DAY) AS after_time

select 特定時間

select * from device where date_format(update_time, '%Y-%m-%d') = '2014-10-23';

WHERE json 裡的欄位

SELECT * FROM `user` WHERE profile->"$.email" != ""                         // email 是欄位名稱
SELECT * FROM `user` WHERE profile->'$."example+11@example.com"' != ""      // 如果欄位名稱有符號要用 雙引號包起來

IFNULL(x, y): 如果 x 欄位的值是 NULl 就回傳 x, 否則是 y

GROUP BY IFNULL(ann_id, id)

FORCE INDEX 強制指定某個 index (寫在 from 前)

FORCE INDEX (uid+age+created_at)

LIMIT & OFFSET

// return only 10 records, start on record 16 (OFFSET 15)
SELECT * FROM Orders LIMIT 10 OFFSET 15
SELECT * FROM Orders LIMIT 15, 10

當資料筆數很大, 且 OFFSET 很大的話, 效能會很不好, 解決方式是不要用 OFFSET, 改用 PK id < 每次撈取的最後一個 id

UNION 將兩個不同表但欄位類似的 Query 合成一個結果

SELECT City FROM Customers
UNION
SELECT City FROM Suppliers
ORDER BY City;

如果只取一個欄位的話, UNION 預設會幫你去除重覆的, 如果要顯示重覆的話要用 UNION ALL

UNION + Unique 每個欄位

SELECT * FROM (
    SELECT City, Code FROM Customers
    UNION
    SELECT City, Code FROM Suppliers
    ORDER BY City;
) C
GROUP BY City

DISTINCT 沒有用, 要用 GROUP BY

JOIN

gui.JPG

A1

1 aaa
2 bbb

A2

1 bbb
2 ccc

Left join SELECT * FROM `A1` LEFT JOIN A2 ON A1.did = A2.did

id did id did
2 bbb 1 bbb
1 aaa NULL NULL

Right join SELECT * FROM `A1` RIGHT JOIN A2 ON A1.did = A2.did

id did id did
2 bbb 1 bbb
NULL NULL 2 ccc

Inner join SELECT * FROM `A1` INNER JOIN A2 ON A1.did = A2.did

id did id did
2 bbb 1 bbb

子查詢

限制

  1. 不可以傳回兩個欄位以上

  2. 不可以傳回兩筆以上的紀錄

    SELECT * FROM users WHERE uid = (SELECT parent_id FROM parents WHERE student_id=1109 LIMIT 1)

其他

3-tier Category

+--------+---------------+-----------+
| cat_id | name          | parent_id |
+--------+---------------+-----------+
|      1 | Electronics   |         0 |
|      2 | Appliances    |         0 |
|      3 | Cell phones   |         1 |
|      4 | Computers     |         1 |
|      5 | Tablets       |         1 |
|      6 | Smartphones   |         3 |
|      7 | Tablet Phones |         3 |
+--------+---------------+-----------+

mysql> SELECT
    -> a.name AS main_category,
    -> b.name AS second_level_category,
    -> c.name AS thrid_level_category
    ->  FROM categories AS a
    -> LEFT JOIN categories AS b ON (a.cat_id=b.parent_id)
    -> LEFT JOIN categories AS c ON (b.cat_id=c.parent_id)
    -> WHERE a.parent_id=0;
+---------------+-----------------------+----------------------+
| main_category | second_level_category | thrid_level_category |
+---------------+-----------------------+----------------------+
| Electronics   | Cell phones           | Smartphones          |
| Electronics   | Cell phones           | Tablet Phones        |
| Electronics   | Computers             | NULL                 |
| Electronics   | Tablets               | NULL                 |
| Appliances    | NULL                  | NULL                 |
+---------------+-----------------------+----------------------+

Rails ORM 版

Category.from('categories AS a').where('a.parent_id = ?', 0).
         joins('LEFT JOIN categories AS b ON (a.id = b.parent_id)').
         joins('LEFT JOIN categories AS c ON (b.id = c.parent_id)').
         select('a.name AS main, b.name AS second, c.name AS third')

ref : http://stackoverflow.com/questions/21277663/mysql-normalization-with-category-with-1st-tier-sub-category-and-2nd-tier-sub-ca

LIKE 語法關鍵字 _ %

How to prevent sql injection

支援儲存 unicode character(emoji,特殊字元)

utf8 vs utf8mb4

  1. 將欄位改成 utfmb4_unicode_ci

  2. 確認連線時的 Server charset, 如果預設是 UTF-8 Unicode (utf8), 那麼與 mysql 建立連線時的 dsn 就要加上 &charset=utf8mb4

    SHOW VARIABLES WHERE variable_name LIKE ‘character%’ OR variable_name LIKE ‘collation%’

如果連線的 client connection 沒有使用 utf8mb4, 在撈含有特殊字元的資料不會 error, 但字元會變成 ?