From SQL Server To MySQL
Table of Contents
Table
select into
同样不会复制表结构的索引,主键。
CREATE TABLE new_tbl [AS] SELECT * FROM orig_tbl;
all index
SELECT a.TABLE_SCHEMA,
a.TABLE_NAME,
a.index_name,
GROUP_CONCAT(column_name ORDER BY seq_in_index) AS `Columns`
FROM information_schema.statistics a
GROUP BY a.TABLE_SCHEMA,a.TABLE_NAME,a.index_name;
table size
SELECT
table_name AS `Table`,
round(((data_length + index_length) / 1024 / 1024), 2) `Size in MB`
FROM information_schema.TABLES
WHERE table_schema = "db"
AND table_name = "tbl";
table status
show table status like 'tbl';
show index from tbl;
show columns from tbl;
Procedure
获取SP的修改时间
SHOW PROCEDURE STATUS LIKE 'sp1'\G
View
视图修改时间
没法。视图只是一个定义,最多从show create view vw
中看到谁创建的。
Security
login permissions
show grants for `phoenix.zhong`;
SQL scripts
Outer apply
SQL Server里面有个OUTER APPLY,可以针对一条记录生成对应的记录(可以是TOP 1,也可以是COUNT(1)),而且性能还不错。
举个例子
SELECT pr.name,
pa.cnt as parameters_count
FROM sys.procedures pr
OUTER APPLY (SELECT COUNT(*) cnt
FROM sys.parameters pa
WHERE pa.object_id = pr.object_id
) pa
ORDER BY pr.name
在MySQL 5.7中没有这样的实现,只能够通过其他方式来实现。比如下面这种方式。
SELECT
ORD.ID
,ORD.NAME
,ORD.DATE
,ORD_HISTORY.VALUE
FROM
ORD
INNER JOIN
ORD_HISTORY
ON ORD_HISTORY.<PRIMARY_KEY>
=
(SELECT ORD_HISTORY.<PRIMARY_KEY>
FROM ORD_HISTORY
WHERE ORD.ID = ORD_HISTORY.ID
AND ORD.DATE <= ORD_HISTORY.DATE
ORDER BY ORD_HISTORY.DATE DESC
LIMIT 1
)
或者MySQL 8中的LATERAL
。
SELECT ORD.ID
,ORD.NAME
,ORD.DATE
,ORD_HIST.VALUE
FROM ORD,
LATERAL (
SELECT ORD_HISTORY.VALUE
FROM ORD_HISTORY
WHERE ORD.ID = ORD_HISTORY.ID
AND ORD.DATE <= ORD_HISTORY.DATE
ORDER BY ORD_HISTORY.DATE DESC
LIMIT 1
) ORD_HIST;
Row_number()
SQL Server里面的Row_number非常好用,不只是给每条记录赋一个行号,还可以根据某一列进行分区,单独计算每个区里面行号。
SELECT
col1, col2,
ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY col3 DESC) AS intRow
FROM Table1
又一次,这个在MySQL 8里面才实现了。在5.7里面,你可以用这种方式。
SELECT
t.*,
@r := CASE
WHEN col = @prevcol THEN @r + 1
WHEN (@prevcol := col) = null THEN null
ELSE 1 END AS rn
FROM
t,
(SELECT @r := 0, @prevcol := null) x
ORDER BY col
需要留意几点
- 针对col分区的第一条记录,第一个WHEN会返回false,第二个WHEN永远都是false,最后就返回1,同时更新@r和@prevcol
- 针对col分区的第二条记录,第一个WHEN会返回true,这个时候就@r就会增加1
- 最最关键的地方,就是第二个WHEN。当第一个WHEN返回false的时候,即用来分区的col发生了变化,这个时候会执行第二个WHEN。但是无论@prevcol是任何值,
= null
永远都是false。这里是SQL里面的一个语法点。null = null
是false
,而null is null
才是true
。所以,这里THEN后面的null永远不会出现。这行的作用就只是用来更新@prevcol
extend the group_concat()
length limitation
SET SESSION group_concat_max_len = 1000000;
select GROUP_CONCAT(COLUMN_NAME)
from information_schema.columns
where TABLE_NAME = 'tbl' AND TABLE_SCHEMA = 'db';
CAST AS VARCHAR
VARCHAR is not supported. Following is the supported data types.
- BINARY[(N)]
- CHAR[(N)]
- DATE
- DATETIME
- DECIMAL[(M[,D])]
- SIGNED [INTEGER]
- TIME
- UNSIGNED [INTEGER]
隐式转换的坑
隐式转换有些问题,如果对索引列用函数,就无法使用索引;如果对查询值用函数,就可以正确使用索引。字符集也会影响索引的使用,不同的字符集在底层存储上是不一样的,无论在JOIN还是WHERE都需要先统一转换类型,才可以进行下一步操作。
SQL SERVER 里面会有隐式转换的问题,在MySQL里面也有类似问题。
CREATE TABLE `tmp` (
`id` varchar(50) DEFAULT NULL
);
INSERT INTO tmp (ID) VALUES (10), (10000000000000), (18446744073709551616);
-- 这里不会有任何问题,数字会被转换成为varchar(50)
select * from tmp;
select * from tmp where id = 10;
select * from tmp where id = 10000000000000;
select * from tmp where id = 18446744073709551616;
以上也都可以正常查找。
但是,如果用>
,就会出问题了。也就是说,虽然比较字符串和数字的时候,会把字符串转换成数字,但还是会有这样或者那样的隐藏的坑。比如,下面4个select都不会返回结果。
select * from tmp where id > 18446744073709551616; -- no records
select * from tmp where id > 18446744073709551615; -- no records
select * from tmp where id > 18446744073709551614; -- no records
select * from tmp where id > 18446744073709551613; -- no records
直到你用小于18446744073709550592
的数字去比较的时候,才会有结果。有趣的是,18446744073709550592
的二进制是1111111111111111111111111111111111111111111111111111110000000000
,最后10位为0。
select * from tmp where id > 18446744073709550592; -- no records
select * from tmp where id > 18446744073709550591; -- one records
user defined variable @var will cause bad query plan
Per https://stackoverflow.com/a/53462860, the data type of @var
could be anything. Query optimizer will ignore the index on col id
in this scenario.
SET @id = "test1234567";
select @id;
EXPLAIN SELECT *
FROM TBL
WHERE id = @id;
You have two options. One is to use local variable in SP. Or you could explictly specify the data type for the user defined variable, like below.
SET @id = CONVERT(CAST("test1234567" AS CHAR(255)) USING ASCII);
select @id;
EXPLAIN SELECT *
FROM TBL
WHERE id = @id;