프로덕션 환경에는 SQL 인덱스의 실패와 성능 저하를 유발하는 암시적 유형 변환이 종종 있으며, 이는 결국 클러스터 로드와 비즈니스에 영향을 미칩니다. 이 문서에는 암시적 변환의 일반적인 시나리오가 요약되어 있습니다. 프로덕션에서는 SQL 암시적 변환을 피하십시오.
저자: Zhang Luodan은 데이터베이스 기술에 열정을 갖고 있으며, 앞으로도 더 깊이 있는 기사를 작성하고 더 가치 있는 콘텐츠를 생산하기를 희망합니다.
Aikeson 오픈 소스 커뮤니티에서 제작되었습니다. 원본 콘텐츠는 승인 없이 사용할 수 없습니다. 편집자에게 연락하여 재인쇄할 출처를 명시해 주세요.
이 글은 3,000단어 정도이며 읽는 데 10분 정도 걸릴 것으로 예상됩니다.
SQL이 암시적 변환을 생성하는 일반적인 시나리오는 다음과 같습니다.
- 데이터 유형의 암시적 변환
- 문자 세트의 암시적 변환
그중에서도 특히 테이블 연결 시나리오 및 저장 프로시저에서 문자 집합 변환은 쉽게 간과됩니다.
참고: 문자 세트는 문자 유형 데이터의 인코딩 규칙입니다. 숫자 유형의 경우 문자 세트를 변환할 필요가 없습니다.
데이터 유형의 암시적 변환
테스트 테이블 구조
t1
테이블 필드 a
는 VARCHAR 유형이고 t2
테이블 필드 a
는 INT 유형입니다.
mysql> show create database test1\G
*************************** 1. row ***************************
Database: test1
Create Database: CREATE DATABASE `test1` /*!40100 DEFAULT CHARACTER SET utf8 */
1 row in set (0.00 sec)
mysql> show create table t1\G
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`id` int(11) NOT NULL,
`a` varchar(20) DEFAULT NULL,
`b` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `a` (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
mysql> show create table t2\G
*************************** 1. row ***************************
Table: t2
Create Table: CREATE TABLE `t2` (
`id` int(11) NOT NULL,
`a` int(11) DEFAULT NULL,
`b` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `a` (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
단일 테이블 예
여기서 주목해야 할 점은 다음과 같은 두 가지 유형의 변환이 있다는 것입니다.
- 필드 유형이 문자열 유형이고 매개변수가 정수인 경우 인덱스가 실패합니다.
- 필드 유형이 정수이고 수신 매개변수가 문자열 유형인 경우 인덱스가 실패하지 않습니다.
이는 문자열을 숫자와 비교할 때 MySQL이 비교를 위해 문자열 유형을 숫자로 변환하므로, 필드 유형이 문자열인 경우 해당 필드에 함수가 추가되어 인덱스가 실패하게 되기 때문입니다.
공식 문서에서는 다음과 같이 설명합니다 . 문자열은 필요에 따라 자동으로 숫자로 변환되고 숫자는 문자열로 변환됩니다.
-- 字段类型为varchar,传参为整数,无法走到索引
mysql> explain select * from t1 where a=1000;
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| 1 | SIMPLE | t1 | NULL | ALL | a | NULL | NULL | NULL | 498892 | 10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 3 warnings (0.00 sec)
mysql> show warnings;
+---------+------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+---------+------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| Warning | 1739 | Cannot use ref access on index 'a' due to type or collation conversion on field 'a' |
| Warning | 1739 | Cannot use range access on index 'a' due to type or collation conversion on field 'a' |
| Note | 1003 | /* select#1 */ select `test1`.`t1`.`id` AS `id`,`test1`.`t1`.`a` AS `a`,`test1`.`t1`.`b` AS `b` from `test1`.`t1` where (`test1`.`t1`.`a` = 1000) |
+---------+------+---------------------------------------------------------------------------------------------------------------------------------------------------+
3 rows in set (0.00 sec)
-- 字段类型为int,传参为字符串,可以走到索引
mysql> explain select * from t2 where a='1000';
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+
| 1 | SIMPLE | t2 | NULL | ref | a | a | 5 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
비교를 위해 숫자를 문자열로 변환할 수 없는 이유는 무엇입니까?
아래 비교 결과:
- 문자열 비교는 다른 문자를 찾을 때까지 문자열의 크기를 하나씩 비교하는 것입니다. 이 비교의 비교 결과는 숫자의 비교 결과와 다릅니다.
mysql> select '2000' <'250';
+---------------+
| '2000' <'250' |
+---------------+
| 1 |
+---------------+
1 row in set (0.00 sec)
테이블 조인의 데이터 유형 변환
두 테이블의 연결 필드 유형이 일치하지 않으면 암시적 변환(MySQL 내부 cast()
함수 추가)이 발생하여 연결 필드 인덱스에 도달할 수 없으며 최적의 테이블 연결 순서를 사용하지 못할 수 있습니다.
원래 구동 테이블이었던 테이블은 인덱스를 사용할 수 없기 때문에 구동 테이블로 사용할 수 있습니다.
예:
- 다음과 같이 일반적인 상황에서는
t2
테이블이 구동 테이블로 선택되지만 데이터 유형이 다르기 때문에 실제 실행되는 SQL은 다음과 같습니다.select * from t1 join t2 on cast(t1.a as unsigned)=t2.a where t2.id<1000
- 피동 테이블로 사용하는 경우 의 인덱스
t1
로 갈 수 없으므로 해당 테이블을 구동 테이블로 선택합니다.t1.a
t1
mysql> explain select * from t1 join t2 on t1.a=t2.a where t2.id<1000;
+----+-------------+-------+------------+------+---------------+------+---------+------------+--------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------------+--------+----------+-----------------------+
| 1 | SIMPLE | t1 | NULL | ALL | a | NULL | NULL | NULL | 498892 | 100.00 | Using where |
| 1 | SIMPLE | t2 | NULL | ref | PRIMARY,a | a | 5 | test1.t1.a | 1 | 5.00 | Using index condition |
+----+-------------+-------+------------+------+---------------+------+---------+------------+--------+----------+-----------------------+
2 rows in set, 2 warnings (0.00 sec)
mysql> show warnings;
+---------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+---------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Warning | 1739 | Cannot use ref access on index 'a' due to type or collation conversion on field 'a' |
| Note | 1003 | /* select#1 */ select `test1`.`t1`.`id` AS `id`,`test1`.`t1`.`a` AS `a`,`test1`.`t1`.`b` AS `b`,`test1`.`t2`.`id` AS `id`,`test1`.`t2`.`a` AS `a`,`test1`.`t2`.`b` AS `b` from `test1`.`t1` join `test1`.`t2` where ((`test1`.`t2`.`id` < 1000) and (`test1`.`t1`.`a` = `test1`.`t2`.`a`)) |
+---------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
2 rows in set (0.01 sec)
문자 세트의 암시적 변환
매개변수 문자셋과 필드 문자셋이 다를 경우 직접 비교가 불가능하며, 문자셋 convert()
변환을 위해 변환 필드에 함수를 추가해야 하므로 인덱스 실패가 발생할 수 있습니다.
테스트 테이블 구조
- 데이터베이스 문자 집합은 UTF8MB4입니다.
t1
테이블 문자 집합은 UTF8입니다.t2
테이블 문자 집합은 UTF8MB4입니다.
mysql> show create database test\G
*************************** 1. row ***************************
Database: test
Create Database: CREATE DATABASE `test` /*!40100 DEFAULT CHARACTER SET utf8mb4 */
mysql> show create table t1\G
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`id` int(11) NOT NULL,
`a` varchar(20) DEFAULT NULL,
`b` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `a` (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
mysql> show create table t2\G
*************************** 1. row ***************************
Table: t2
Create Table: CREATE TABLE `t2` (
`id` int(11) NOT NULL,
`a` varchar(20) DEFAULT NULL,
`b` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `a` (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
1 row in set (0.01 sec)
단일 테이블 예
-- 正常执行时,匹配字段的字符集(没有单独指定时继承表的字符集)
mysql> explain select * from t1 where a='1000';
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+
| 1 | SIMPLE | t1 | NULL | ref | a | a | 63 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
-- 将参数转换不同的字符集,无法走到索引,而是全表扫描
mysql> explain select * from t1 where a=convert('1000' using utf8mb4);
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 2000 | 100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
-- show warnings可以看到优化器进行了转换,在t1.a上加了convert函数,从而无法走到索引
mysql> show warnings;
+-------+------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select `test`.`t1`.`id` AS `id`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where (convert(`test`.`t1`.`a` using utf8mb4) = <cache>(convert('1000' using utf8mb4))) |
+-------+------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
또한 다음 사항에 유의해야 합니다.
MySQL은 내부적으로 UTF8을 UTF8MB4로 변환하는 것과 같이 낮은 수준의 문자 집합을 더 높은 수준의 문자 집합으로 변환하는 데 우선 순위를 부여합니다.
이전 예에서는 convert()
함수가 t1.a
에 추가되었지만 다음 예에서는 필드 convert()
대신 매개변수에 함수가 추가되었으므로 t2.a
성능이 저하되지 않았습니다.
mysql> show create table t2\G
*************************** 1. row ***************************
Table: t2
Create Table: CREATE TABLE `t2` (
`id` int(11) NOT NULL,
`a` varchar(20) DEFAULT NULL,
`b` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `a` (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)
mysql> explain select * from t2 where a=convert('1000' using utf8);
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+
| 1 | SIMPLE | t2 | NULL | ref | a | a | 83 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
mysql> show warnings;
+-------+------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select `test`.`t2`.`id` AS `id`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` where (`test`.`t2`.`a` = convert(convert('1000' using utf8) using utf8mb4)) |
+-------+------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
요약하자면:
- 테이블 필드 문자 세트가 하위 문자 세트(예: UTF8)이고 수신 값이 상위 레벨 문자 세트(예: UTF8MB4)인 경우, 이때 테이블 필드의 문자 세트가 변환됩니다. 이는 함수를 사용하는 것과 같습니다. 인덱스가 잘못되었습니다.
- 테이블 필드가 상위 수준 문자 집합(예: UTF8MB4)이고 들어오는 값이 하위 수준 문자 집합(예: UTF8)인 경우 들어오는 값은 문자 집합으로 변환되므로 인덱스 오류가 발생하지 않습니다. .
그러나 우리는 일반적으로 매개변수의 문자 집합을 수동으로 변환하는 함수를 사용하지 않습니다 convert()
. 다음 두 시나리오에서는 무시하기 쉬운 암시적 유형 변환이 있을 수 있으며 이로 인해 생산 문제가 발생합니다.
테이블 조인의 문자 집합 변환
두 테이블의 연결 필드의 문자 집합이 일치하지 않으면 암시적 변환( convert()
MySQL 내부에 함수 추가)이 발생하고 연결 필드 인덱스에 도달할 수 없으며 최적의 테이블 연결 순서가 사용되지 않을 수 있습니다.
원래 구동 테이블이었던 테이블은 인덱스를 사용할 수 없기 때문에 구동 테이블로 사용할 수 있습니다.
예:
- 일반적인 상황에서 MySQL은 작은 결과 집합이 있는 테이블을 구동 테이블로 우선순위로 지정합니다. 이 예에서는
t2
구동 테이블과t1
구동 테이블입니다. - 그러나 문자 집합이 다르기 때문에 실제로 실행되는 SQL은 그림과 같습니다
show warnings
. 문자 집합을 변환하는 함수를t1.a
필드에 추가 하면 해당 필드의 인덱스에 도달할 수 없으며 연결 순서를 변경해야 합니다. .convert()
t1.a
mysql> explain select * from t1 left join t2 on t1.a=t2.a where t2.id<1000;
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-----------------------+
| 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 498649 | 100.00 | NULL |
| 1 | SIMPLE | t2 | NULL | ref | PRIMARY,a | a | 83 | func | 1 | 4.79 | Using index condition |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-----------------------+
2 rows in set, 1 warning (0.00 sec)
mysql> show warnings;
+-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select `test`.`t1`.`id` AS `id`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`id` AS `id`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`id` < 1000) and (convert(`test`.`t1`.`a` using utf8mb4) = `test`.`t2`.`a`)) |
+-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
-- 在下面示例中,虽然也发生了类型转换,但是效率并没有变差,因为原本最优的连接顺序就是t1作为驱动表
mysql> explain select * from t1 left join t2 on t1.a=t2.a where t1.id<1000;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| 1 | SIMPLE | t1 | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 999 | 100.00 | Using where |
| 1 | SIMPLE | t2 | NULL | ref | a | a | 83 | func | 1 | 100.00 | Using where |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
mysql> show warnings;
+-------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select `test`.`t1`.`id` AS `id`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`id` AS `id`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t1` left join `test`.`t2` on((convert(`test`.`t1`.`a` using utf8mb4) = `test`.`t2`.`a`)) where (`test`.`t1`.`id` < 1000) |
+-------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
저장 프로시저의 문자 세트 변환
이는 비교적 무시하기 쉬운 시나리오이기도 합니다. 프로덕션 환경에서 저장 프로세스 중에 기본 키가 업데이트되었을 때 문제가 발견되었지만 실행하는 데 10초 이상이 걸렸습니다.
저장 프로시저의 변수 문자 세트는 database
(생성 시 지정 가능)의 문자 세트에서 상속됩니다. 테이블 필드 문자 세트가 database
()의 문자 세트와 다른 경우 암시적 문자 세트 유형 변환은 이전과 유사합니다. 하나가 발생합니다.
예:
database
문자 집합은 UTF8MB4입니다.character_set_client
세션의 값 이며collation_connection
저장 프로시저를 생성할 때의 값입니다character_set_client
.collation_connection
- 저장 프로시저의 변수 문자 집합이 데이터베이스 수준의 문자 집합과 일치하는지 테스트되었습니다.
-- 存储过程信息: Database Collation: utf8mb4_general_ci
mysql> show create procedure update_data\G
*************************** 1. row ***************************
Procedure: update_data
sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
Create Procedure: CREATE DEFINER=`root`@`%` PROCEDURE `update_data`()
begin
declare j int;
declare n varchar(100);
select charset(n);
set j=1;
while(j<=2000)do
set n = cast(j as char);
select 1,now();
update t1 set b=concat(b,'1') where a=n;
select 2,now();
select sleep(1);
set j=j+1;
end while;
end
character_set_client: utf8mb4
collation_connection: utf8mb4_general_ci
Database Collation: utf8mb4_general_ci
1 row in set (0.00 sec)
如下,在执行存储过程后,看到打印的变量n的字符集是utf8mb4
mysql> call update_data();
+------------+
| charset(n) |
+------------+
| utf8mb4 |
+------------+
1 row in set (0.00 sec)
인덱스 필드를 기반으로 업데이트된 명령문은 a
전체 테이블 스캔(유형: 인덱스, 키: 기본)을 사용하여 실제로 다음과 같습니다.
mysql> explain update t1 set b=concat(b,'1') where a=convert('1000' using utf8mb4);
+----+-------------+-------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
| 1 | UPDATE | t1 | NULL | index | NULL | PRIMARY | 4 | NULL | 498649 | 100.00 | Using where |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
1 row in set (0.00 sec)
-- 而正常情况下,执行计划为:
mysql> explain update t1 set b=concat(b,'1') where a='1000';
+----+-------------+-------+------------+-------+---------------+------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+------+---------+-------+------+----------+-------------+
| 1 | UPDATE | t1 | NULL | range | a | a | 63 | const | 1 | 100.00 | Using where |
+----+-------------+-------+------------+-------+---------------+------+---------+-------+------+----------+-------------+
1 row in set (0.00 sec)
업데이트 시간도 0.00초 에서 0.60초로 변경되었습니다 . 테이블 데이터의 양이 많을 경우 전체 테이블 스캔이 프로덕션에 더 큰 영향을 미칩니다.
mysql> update t1 set b=concat(b,'1') where a='1000';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> update t1 set b=concat(b,'1') where a=convert('1000' using utf8mb4);
Query OK, 1 row affected (0.60 sec)
Rows matched: 1 Changed: 1 Warnings: 0
암시적 변환을 피하는 방법
데이터 유형의 암시적 변환의 경우:
- 표준 데이터 유형 선택
- SQL 패스 참여 필드 데이터 유형 일치
문자 집합의 암시적 변환의 경우: 클라이언트 문자 집합, 서버측 문자 집합, 데이터베이스 문자 집합, 테이블 문자 집합 및 필드 문자 집합이 일관성을 유지합니다.
더 많은 기술 기사를 보려면 https://opensource.actionsky.com/을 방문하세요.
SQLE 소개
SQLE는 개발부터 프로덕션 환경까지 SQL 감사 및 관리를 포괄하는 포괄적인 SQL 품질 관리 플랫폼입니다. 주류 오픈소스, 상용 및 국내 데이터베이스를 지원하고 개발, 운영 및 유지 관리를 위한 프로세스 자동화 기능을 제공하고 온라인 효율성을 향상시키며 데이터 품질을 향상시킵니다.