由於一早就接觸過SQL Server,因此對sql server的語法比較熟悉,後來轉學mysql,還有些不適應,因此大體總結了一些mysql和sql server語法不一樣的地方,其中借鑑了些此文中的內容。html
由於本文是SQL server和mysql語法差別性的總結,因此內容比較雜,沒什麼邏輯也不詳細,還請見諒。在後面的文章中將只說明MySQL的語法。mysql
MySQL和SQL Server同樣,對大小寫不敏感。但不一樣的是,在MySQL中對部分對象的引用是大小寫敏感的,如數據庫名、表名,但對字段、索引、函數、存儲過程等的引用不敏感。正則表達式
在MySQL中支持三種註釋方法:如下均可以是行內註釋。sql
#
做爲開頭,後面的全是註釋。--
做爲註釋開頭,但要注意,MySQL中這種註釋方法和SQL Server等其餘標準數據庫註釋語法稍有不一樣,MySQL要求第二個短線後面必須跟一個空白字符,如空格、製表符等。/**/
註釋符。MySQL中設置自增列(auto_increment)的列必須是有索引的列,且建立表時要顯式指定的種子值須要在建表語句以後。另外MySQL一張表只能有一個自增列。且MySQL中向自增列插入數據時必須使用null來表示插入的是自增列,除非顯式指定插入列表中不包含自增列,而SQL Server向自增列插入數據時能夠且必須無視該列,除非設置顯示插入模式。數據庫
-- SQL Server直接使用identity,但必須有非自增列以外的列才能插入,除非顯式開啓手動插入自增列
create table emp1(id int not null identity(1,2),name CHAR(20));
insert into emp1 VALUES('malongshuai');
insert into emp1 values('gaoxiaofang');
select * from emp1;
-- MySQL中自增列必須爲索引列,而且只能設置種子值而不能直接設置步長
create table emp1(id int not null primary key AUTO_INCREMENT);
create table emp2(id int not null primary key AUTO_INCREMENT) auto_increment=100;
insert into emp1 values(null);
insert into emp2 values(null);
設置自增列的步長,分爲全局級別和會話級別。但它們都是臨時生效的,重啓實例後效果就消失,要永久生效能夠將其寫入配置文件中。若是是會話級別,那麼當用戶新建一個會話的時候,那麼步長又回到了全局級別。express
mysql不能設置爲表級別的步長!!安全
設置和查看全局和會話級別的變量時,分別使用以下語句:session
set [session] auto_increment_increment=100; -- 會話級的步長設置
set global auto_increment_offset=12; -- 全局級的種子值
show [session] variables like 'auto_inc%';
show global variables like 'auto_inc%';
這兩個變量都有session級和global級。其中auto_increment_offset項爲起始計算項,auto_increment_increment項爲步長項。它們的處理模式和SQL server的處理方式相差甚遠。當同時設置了這兩個變量時,若是offsert設置的值大於increment的值,則offset將被忽略,且MySQL會以"offset+N\*increment"計算下一條插入的記錄值。例如,"offset=三、increment=5",當前表的最後一個自增列值爲13,則下一條插入的自增值爲18,由於"offset+N\*increment"將計算獲得[3,8,13,18,23,28...]序列,因此從序列中挑出大於且最接近當前最後一個值13的項,即18。併發
以上言論爲官方手冊上的解釋(原文:the next value inserted is the least value in the series that is greater than the maximum existing value in the AUTO_INCREMENT column),但實際上並不標準,更準確的說法是:根據當前offset和increment計算增加序列,並從中挑出大於或等於原序列的下一個值。例如上面offset=3,生成的序列爲[3,8,13,18,23...],下一個要插入的值爲18,但插入以前若是將offset改成4,則新的序列爲[4,9,14,19,24],那麼它將插入19,而不是14,儘管14大於當前最後一個記錄值13。同理,若是將offset改小,例如設置爲2,則序列爲[2,7,12,17,22],那麼下一個插入的值將是22。同理,修改increment也是同樣計算的。ide
問:若是有一張表,裏面有個字段爲id的自增主鍵,當已經向表裏面插入了10條數據以後,刪除了id爲八、九、10的數據,再把mysql重啓,以後再插入一條數據,那麼這條數據的id值應該是多少,是8仍是11?
答:是11。可是在老版本中,innodb存儲引擎的表會是8,這是innodb的bug,在後來修復了,只是在mysql5.6中沒有了。更簡單地說,在未修復以前,auto_increment的值來自於內存中的自增計數器,當中止服務後,內存中的計數器就消失了,在重啓時,auto_increment的值會根據表中已有的值進行初始化。當修復該功能以後,auto_increment計數器的值會持久化。對於MariaDB而言,則是從MariaDB 10.2.4開始持久化的。
查看當前自增值的方法:
show table status like "table_name_string"; -- 查看某個表的下一個自增值
select last_insert_id(); -- 查看當前環境下最後一次自增列的插入值
關於"last_insert_id"函數,在下一篇文章"內置函數"中再作介紹。
-- SQL Server使用存儲過程sp_help
exec sp_help emp;
-- MySQL使用desc描述或者使用show
mysql> desc emp1;
+-------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
+-------+---------+------+-----+---------+----------------+
mysql> show table status like 'emp1'\G
*************************** 1. row ***************************
Name: emp1
Engine: InnoDB
Version: 10
Row_format: Compact
Rows: 1
Avg_row_length: 16384
Data_length: 16384
Max_data_length: 0
Index_length: 0
Data_free: 0
Auto_increment: 2
Create_time: 2017-03-22 10:05:49
Update_time: NULL
Check_time: NULL
Collation: latin1_swedish_ci
Checksum: NULL
Create_options:
Comment:
1 row in set (0.00 sec)
-- SQL Server使用存儲過程sp_rename
EXEC sp_rename emp,emp2 [object]
-- mysql使用alter語句中的rename功能
alter table emp rename [to] emp3;
在刪除表方面,MySQL比SQL Server要方便不少,判斷起來也方便不少。
-- SQL Server刪除表,每次只能刪除一張表
if object_id('table_name') is not null drop table table_name;
if exists(select object_id('table_name')) drop table table_name;
-- MySQL能夠直接判斷,且一次能夠刪除多表
drop table if exists table_name1,table_name2...
-- SQL Server只能修改字段屬性(數據類型、空性),不能修改約束類屬性,
-- 約束類屬性須要使用"alter table … add constraint"
alter table emp2 alter column id int not null;
alter table emp2 add gender char(2);
alter table emp2 add CONSTRAINT def_key DEFAULT('男') FOR gender;
alter table emp2 add constraint pk_key primary key clustered(id);
-- mysql修改字段屬性有幾種方法
alter table table_name | ALTER [COLUMN] col_name {SET DEFAULT string | DROP DEFAULT} | CHANGE [COLUMN] old_col_name new_col_name column_definition [FIRST|AFTER col_name] | MODIFY [COLUMN] col_name column_definition [FIRST | AFTER col_name]
也就是說,change和modify均可以修改列的定義,包括約束類的屬性、字段的位置,且change比modify更多一個重命名列的功能。而alter column則只能設置默認值和刪除默認值。注意,重命名和修改字段時,須要從新定義字段屬性。
首先須要說明的是,MySQL中任何存儲引擎都不支持check約束,官方手冊上說明了會對check語句進行讀取檢查,可是不會生效,也就是即便是對的check約束也是被忽略的。要在MySQL中實現check約束能夠考慮使用觸發器或者經過數據類型來限制。
-- MySQL中添加、刪除字段和約束的語法
-- 添加字段
| ADD [COLUMN] col_name column_definition
[FIRST | AFTER col_name ]
| ADD [COLUMN] (col_name column_definition,...) -- 能夠一次性添加多個字段
-- 刪除字段
| DROP [COLUMN] col_name -- 添加約束 | ADD [CONSTRAINT [symbol]] PRIMARY KEY [index_type] (index_col_name,...) [index_option] ... | ADD [CONSTRAINT [symbol]] FOREIGN KEY [index_name] (index_col_name,...) reference_definition | ADD [CONSTRAINT [symbol]] UNIQUE [INDEX|KEY] [index_name] [index_type] (index_col_name,...) [index_option] ... -- 刪除約束 | DROP PRIMARY KEY | DROP FOREIGN KEY fk_symbol | DROP {INDEX|KEY} index_name | DISABLE KEYS | ENABLE KEYS -- 添加和刪除默認值約束 | ALTER [COLUMN] col_name {SET DEFAULT literal | DROP DEFAULT}
對於MariaDB,從10.2.1開始,其支持DROP CONSTRAINT子句,並引入了DEFAULT約束,還支持check約束。且在MariaDB 10.0.2版本以後,操做字段時甚至支持if exists和if not exists。包括以下狀況:
ADD COLUMN [IF NOT EXISTS]
ADD INDEX [IF NOT EXISTS]
ADD FOREIGN KEY [IF NOT EXISTS]
ADD PARTITION [IF NOT EXISTS]
CREATE INDEX [IF NOT EXISTS] DROP COLUMN [IF EXISTS] DROP INDEX [IF EXISTS] DROP FOREIGN KEY [IF EXISTS] DROP PARTITION [IF EXISTS] CHANGE COLUMN [IF EXISTS] MODIFY COLUMN [IF EXISTS] DROP INDEX [IF EXISTS]
對於SQL Server,不論是什麼約束,都能使用下面的語句進行刪除,但MySQL有些麻煩,見下文具體說明:
alter table table_name drop constraint constraint_name
添加/刪除字段
-- SQL Server添加字段,只能一個字段一個字段添加
alter table emp add name char(20) -- MySQL添加字段,能夠一次添加一個字段,也能夠一次添加多個字段 ALTER TABLE emp ADD NAME CHAR(20) NOT NULL;
ALTER TABLE emp ADD (gender CHAR(6) NOT NULL DEFAULT 'male',phone CHAR(11));
添加/刪除主鍵、外鍵約束
/*添加主鍵約束*/
-- SQL Server
alter table emp10 add constraint pk_id primary key clustered(id);
-- MySQL添加主鍵
ALTER TABLE emp10 ADD CONSTRAINT pk_id PRIMARY KEY (id);
-- 添加外鍵約束,SQL Server和MySQL相同
ALTER TABLE emp10 ADD CONSTRAINT fk_id FOREIGN KEY (id) REFERENCES emp20(id);
-- MySQL刪除主鍵、外鍵(須要先刪除外鍵)
alter table emp10 drop foreign key fk_id;
alter table emp10 drop primary key;
MySQL在外鍵上和SQL Server以及Oracle都不一樣,MySQL在建立外鍵的時候,會自動在外鍵列上建立一個索引,且這個索引沒法人爲刪除。在表聯接的過程當中由於會依賴性的對外表加上鎖,若是外鍵列上沒有索引,可能會加上表鎖下降併發且容易致使死鎖,若是有索引,將會進行範圍鎖定,加強併發性也減小了死鎖的出現概率。在這一點上,MySQL比SQL Server作的要好。
添加惟一性約束
--SQL Server
alter table emp10 add constraint uni_name unique nonclustered(name);
-- MySQL添加惟一性約束
ALTER TABLE emp10 ADD CONSTRAINT uni_name UNIQUE KEY(`name`);
-- MySQL刪除惟一性約束
alter table emp10 drop key uni_name
默認值的設置方法
SQL Server的默認值約束和MySQL的默認值約束設置方法相差很大,MySQL的默認值約束不能使用constraint來設置,只能經過修改列屬性來設置。另外,MySQL的default關鍵字後是不能加括號的,而SQL Server是無所謂的。
-- SQL Server設置默認值時可有可沒有括號
create table emp10(name int not null default(12));
create table emp10(name int not null default 12);
alter table emp10 add constraint def_name default 12 for name;
-- MySQL設置默認值時不能使用括號
create table emp(id int not null default 12);
alter table test.emp alter id set default 12; /*使用change和modify也行,可是要重定義列屬性*/
-- MySQL刪除默認值約束
alter table test.emp alter id drop default;
-- SQL Server
select * into table_name1 from table_name2; /*複製表結構和數據*/
select * into table_name1 from table_name2 where 1=0; /*只複製表結構*/
-- MySQL
create table table_name1 like table_name2; /*只複製表結構*/
create tbale table_name1 as select * from table_name2; /*複製表結構和數據*/
MySQL中複製表結構時不會複製主鍵、索引、自增列等任何屬性,僅僅只是簡單的創建一張表而後插入數據。但SQL Server複製表結構時會複製自增列屬性。
在MySQL中能夠給整數數據類型指定結果的顯式寬度,如int(4)表示將顯示4位整數,若是實際值的位數小於顯示值寬度,則使用空格填充。而結果位數超出時將不影響顯示結果。通常該功能都會配合zerofill屬性用0代替空格填充,可是使用了zerofill後,該列就會自動變成無符號字段。
zerofill屬性的聲明必須緊跟在整數數據類型的後面,而不能跟在如not null這樣的屬性後面。
SQL Server中沒有該功能。
要注意的是顯示寬度和數據類型限制的字段寬度是不同的。顯示寬度不會影響字段的限制寬度,只是起一個顯示做用。
CREATE TABLE test3(id INT(2) ZEROFILL NOT NULL);
ALTER TABLE test3 MODIFY id INT(2) ZEROFILL NOT NULL;
ALTER TABLE test3 CHANGE id id INT(2) ZEROFILL NOT NULL;
INSERT INTO test3 VALUES(1),(2),(11),(111);
SELECT id FROM test3;
+-----+
| id |
+-----+
| 01 |
| 02 |
| 11 |
| 111 |
+-----+
4 rows in set (0.00 sec)
數據類型的範圍是根據bit位的數量值來計算的。4字節的int佔用32bit,因此能夠表示的範圍爲0-2^32。
數值在存儲(或調入內存)時,以數值型方式存儲比字符型或日期時間類型更節省空間。在整數值存儲上,0-255之間的任意整數都只佔一個字節,256-65535之間的任意整數都佔2個字節,而佔用4個字節時即可以表明幾十億個整數之間的任意一個,這顯然比字符型存儲時每一個字符佔用一個字節節省空間的多。例如值"100"存儲爲字符型時佔用三個字節,而存儲爲數值型將只佔用一個字節。所以數據庫默認將不使用引號包圍的值看成數值型,若是明確要存儲爲字符型或日期時間型則應該使用引號包圍以免歧義。
值 CHAR(4) 存儲需求 VARCHAR(4) 存儲需求
-----------------------------------------------------------
'' ' ' 4個字節 '' 1個字節
'ab' 'ab ' 4個字節 'ab ' 3個字節
'abcd' 'abcd' 4個字節 'abcd' 5個字節
'abcdefgh' 'abcd' 4個字節 'abcd' 5個字節
MySQL在檢索或操做char時會刪除尾隨空格,也就是說在where語句中name='gaoxiaofang '
和name='gaoxiaofang'
的結果是同樣的;若name='gaoxiaofang '
,那麼concat(name,'x')
的結果將是gaoxiaofangx
。
而檢索或操做varchar時不會刪除尾隨空格。可是char類型的列和varchar類型的列進行比較會忽略尾隨空格,即 char:a
=varchar:a
。
mysql> create table test(a char(10),b varchar(10));
mysql> insert into test select 'a ','a ';
mysql> select concat(a,'x'),concat(b,'x'),a=b from test;
+---------------+---------------+-----+
| concat(a,'x') | concat(b,'x') | a=b |
+---------------+---------------+-----+
| ax | a x | 1 |
+---------------+---------------+-----+
1 row in set
關於char(M)和varchar(M),其長度是M個字符(MySQL早期版本是M字節),其字節數和字符集有關,例如latain1字符集下char(30)表示能存儲30個字符也就是30個字節,而utf8字符集下char(30)只能存儲30個字符(哪怕是英文字母),但該列將佔用30*3=90個字節的空間。
mysql> create table test9(a char(2) charset utf8mb4,b char(2)) charset=latain1;
mysql> insert into test9 values('我是','wo'),('wo','wo');
mysql> select length(a),char_length(a),length(b),char_length(b) from test9;
+-----------+----------------+-----------+----------------+
| length(a) | char_length(a) | length(b) | char_length(b) |
+-----------+----------------+-----------+----------------+
| 6 | 2 | 2 | 2 |
| 2 | 2 | 2 | 2 |
+-----------+----------------+-----------+----------------+
2 rows in set (0.00 sec)'
varchar(M)的字節數還和存儲的字節數有關,每2^8次方字節增長一字節結束符。
關於日期時間的輸入方式是很是寬鬆的,如下幾種方式都是被容許的:任意容許的分隔符,建議使用4位的年份。
2011-01-01 18:40:20 2011/01/01 18-40-20 20110101184020
對於ENUM,插入數據時忽略大小寫。若是enum列是容許NULL的,則NULL值也是有效值。
對於SET類型,和enum相似,不區分大小寫,存儲時刪除尾隨空格,null也是有效值。但不一樣的是能夠組合多個給出的值。如set('a','b','c','d')
能夠存儲'a,b','d,b'
等,多個成員之間使用逗號隔開。因此,使用多個成員的時候,成員自己的值中不能出現逗號。而且存儲數據時忽略重複成員並按照枚舉時的順序存儲,如set('d','b','a')
,存儲'a,b,a','b,a,b'
的結果都是'b,a'
。使用find_in_set(set_value,set_column_name)
能夠檢索出包含指定set值set_value的行。
SELECT * FROM test6 WHERE FIND_IN_SET('d',col)>0;
unsigned屬性就是讓數值類型的數據變得無符號化。使用unsigned屬性將會改變數值數據類型的範圍,例如tinyint類型帶符號的範圍是-128到127,而使用unsigned時範圍將變成0到255。同時unsigned也會限制該列不能插入負數值。
create table t(a int unsigned,b int unsigned);
insert into t select 1,2;
insert into t select -1,-2;
上面的語句中,在執行第二條語句準備插入負數時將會報錯,提示超出範圍。
使用unsigned在某些狀況下確有其做用,例如通常的ID主鍵列不會容許使用負數,它至關於實現了一個check約束。
可是使用unsigned有時候也會出現些不可預料的問題:在進行數值運算時若是獲得負數將會報錯。例如上面的表t中,字段a和b都是無符號的列,且有一行"a=1,b=2"。
mysql> select * from t;
+---+---+
| a | b |
+---+---+
| 1 | 2 |
+---+---+
1 row in set
此時若是計算"a-b"將會出錯,不只如此,只要是unsigned列參與計算並將獲得負數都會出錯。
mysql> select a-b from t;
1690 - BIGINT UNSIGNED value is out of range in '(`test`.`t`.`a` - `test`.`t`.`b`)'
mysql> select a-2 from t;
1690 - BIGINT UNSIGNED value is out of range in '(`test`.`t`.`a` - 2)'
而不是負數的結果將不會有影響。
mysql> select 2-a,a*3 from t;
+-----+-----+
| 2-a | a*3 |
+-----+-----+
| 1 | 3 |
+-----+-----+
1 row in set
這並非MySQL中的bug,在C語言中的unsigned也同樣有相似的問題。這個問題在MySQL中設置set sql_mode='no_unsigned_subtraction'
便可解決。
zerofill修飾字段後,不足字段顯示部分將使用0來代替空格填充,啓用zerofill後將自動設置unsigned。zerofill通常只在設置了列的顯示寬度後一塊兒使用。關於列的顯示寬度在上文已經介紹過了。
mysql> create table t1(id int(4) zerofill);
mysql> select * from t1;
+-------+
| id |
+-------+
| 0001 |
| 0002 |
| 0011 |
| 83838 |
+-------+
4 rows in set (0.00 sec)
zerofill只是修飾顯示結果,不會改變存儲的數據值。
只說明些SQL Server中沒有的運算符。詳細內容見官方手冊:函數和操做符。
這個符號和"="進行相同的運算,可是它多出的一個功能是能夠和NULL進行比較。
當比較的兩邊都是NULL時返回1而不是NULL,只有一邊是null時返回0而不是null,其他的時候和"="的結果同樣。
mysql> SELECT 1<=>NULL UNION ALL SELECT NULL<=>NULL UNION ALL SELECT 1=0 UNION ALL SELECT 1<=>0;
+----------+
| 1<=>NULL |
+----------+
| 0 |
| 1 |
| 0 |
| 0 |
+----------+
4 rows in set
在SQL Server中沒有正則表達式運算符,而MySQL中有。格式:expression regexp reg_pattern
若expression知足reg_pattern,則返回1,沒法匹配成功則返回0。若expression或reg_pattern任意一方爲null,則返回null。
MySQL實現的是擴展正則表達式。
mysql> SELECT 'basskd' REGEXP '^b','basskd' REGEXP 's.k','basskd' REGEXP NULL,NULL REGEXP '^b';
+----------------------+-----------------------+----------------------+------------------+
| 'basskd' REGEXP '^b' | 'basskd' REGEXP 's.k' | 'basskd' REGEXP NULL | NULL REGEXP '^b' |
+----------------------+-----------------------+----------------------+------------------+
| 1 | 1 | NULL | NULL |
+----------------------+-----------------------+----------------------+------------------+
1 row in set
在MySQL中,"+"不算是一種鏈接操做符。可是它的表達式是另有意義的:它會將兩邊的表達式嘗試轉換爲數值型進行數值相加運算,若是轉換失敗的則表示爲0。
mysql> select '12'+'34',12+'34','abc'+12,'a'+'abc';
+-----------+---------+----------+-----------+
| '12'+'34' | 12+'34' | 'abc'+12 | 'a'+'abc' |
+-----------+---------+----------+-----------+
| 46 | 46 | 12 | 0 |
+-----------+---------+----------+-----------+
1 row in set
要真正實現字符串鏈接,須要使用函數concat,參見字符串鏈接。
異或運算符是用於比較兩邊值是否相同的。相同則返回0,不一樣則返回1,若是存在null,則直接返回null。
mysql> select 1 xor 1,0 xor 0,1 xor 0,1 xor null,null xor null;
+---------+---------+---------+------------+---------------+
| 1 xor 1 | 0 xor 0 | 1 xor 0 | 1 xor null | null xor null |
+---------+---------+---------+------------+---------------+
| 0 | 0 | 1 | NULL | NULL |
+---------+---------+---------+------------+---------------+
1 row in set
XOR運算符能夠鏈接屢次,順序是從前向後依次進行運算。
mysql> select 1 xor 1 xor 0,1 xor 1 xor 1;
+---------------+---------------+
| 1 xor 1 xor 0 | 1 xor 1 xor 1 |
+---------------+---------------+
| 0 | 1 |
+---------------+---------------+
1 row in set
在MySQL中某些符號須要使用反斜槓"\"來轉義。包括單引號(')、雙引號(")、反斜線(\)。
另外,若是對象名使用了關鍵字或特殊符號,也須要進行轉義,如列名使用了int關鍵字,表名使用了char關鍵字等。可是此時的轉義符號不是反斜線,而是反引號``或引號。
而在SQL Server中則不須要轉義,有歧義的時候只需使用引號便可。
mysql> INSERT INTO test7 VALUES('\\'),('\\\\'),('\''); mysql> select * from test7; +-----+ | col | +-----+ | \ | | \\ | | ' |
+-----+
3 rows in set
對於對象名的轉義,參考show create table table_name
的結果便可看到,到處使用了反引號防止歧義。例如:
mysql> show create table test7;
+-------+----------------------------------------------------------------------------+
| Table | Create Table |
+-------+----------------------------------------------------------------------------+
| test7 | CREATE TABLE `test7` (
`col` char(6) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+----------------------------------------------------------------------------+
1 row in set
有時候,直接反斜線轉義單引號、雙引號是不可行的,這時能夠將雙引號放在單引號中保留雙引號,或者單引號放在雙引號中保留單引號。
insert into t values('Tun"er'); # 保留雙引號 insert into t values("Tun'er"); # 保留單引號
若是狀況還更復雜,則須要採用另外一種機制保留單引號、雙引號:寫兩次表示轉義一個引號。例如:
insert into t values("Tun"'"er"); # 錯誤 insert into t values('Tun''er'); # 正確-->Tun'er insert into t values("Tun""er"); # 正確-->Tun"er