A conversão implícita do MySQL deve ser conhecida e compreendida

No ambiente de produção, muitas vezes há algumas conversões de tipo implícitas que fazem com que os índices SQL falhem e tenham um desempenho extremamente ruim, o que, por sua vez, afeta a carga e os negócios do cluster. Este artigo resume cenários comuns de conversão implícita. Tente evitar a conversão implícita de SQL na produção.

Autor: Zhang Luodan é apaixonado por tecnologia de banco de dados e está em constante exploração. Ele espera escrever artigos mais aprofundados e produzir conteúdo mais valioso no futuro!

Produzido pela comunidade de código aberto Aikeson, o conteúdo original não pode ser utilizado sem autorização. Entre em contato com o editor e indique a fonte para reimpressão.

Este artigo tem cerca de 3.000 palavras e espera-se que leve 10 minutos para ser lido.

Os cenários comuns em que o SQL gera conversões implícitas incluem:

  1. Conversão implícita de tipos de dados
  2. Conversão implícita de conjuntos de caracteres

Entre eles, a conversão de conjuntos de caracteres, especialmente em cenários de conexão de tabelas e procedimentos armazenados, é facilmente esquecida.

Nota: O conjunto de caracteres é a regra de codificação para dados de tipo de caracteres. Para tipos numéricos, não há necessidade de converter o conjunto de caracteres.

Conversão implícita de tipos de dados

Estrutura da tabela de teste

t1Os campos da tabela asão do tipo VARCHAR e t2os campos da tabela asão do tipo 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)

Exemplo de tabela única

O que precisa ser observado aqui é que existem os dois tipos de conversões a seguir:

  1. Quando o tipo de campo for do tipo string e o parâmetro for um número inteiro, o índice falhará.
  2. Quando o tipo de campo é um número inteiro e o parâmetro de entrada é um tipo de string, isso não causará falha no índice.

Isso ocorre porque ao comparar strings com números, o MySQL irá converter o tipo de string em um número para comparação. Portanto, quando o tipo de campo for uma string, uma função será adicionada ao campo, causando falha no índice.

A documentação oficial explica : Strings são automaticamente convertidas em números e números em strings conforme necessário.

-- 字段类型为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)

E por que você não pode converter números em strings para comparação?

Resultados de comparação abaixo:

  • A comparação de strings consiste em comparar o tamanho das strings uma por uma até que diferentes caracteres sejam encontrados. Os resultados da comparação desta comparação são diferentes dos resultados da comparação de números.
mysql> select '2000' <'250';
+---------------+
| '2000' <'250' |
+---------------+
|             1 |
+---------------+
1 row in set (0.00 sec)

Conversão de tipo de dados em junções de tabelas

Quando os tipos de campo de conexão das duas tabelas são inconsistentes, isso levará à conversão implícita ( cast()a função interna do MySQL é adicionada), e o índice do campo de conexão não pode ser alcançado e a ordem ideal de conexão da tabela não pode ser usada.

A tabela que era originalmente uma tabela controlada pode ser usada como tabela controladora porque o índice não pode ser usado.

Exemplo:

  • A seguir, em circunstâncias normais, t2a tabela será selecionada como tabela de controle, mas devido aos diferentes tipos de dados, o SQL real executado é:select * from t1 join t2 on cast(t1.a as unsigned)=t2.a where t2.id<1000
  • Se t1usada como tabela controlada, não há como ir para t1.ao índice de , então t1a tabela é selecionada como tabela controladora
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)

Conversão implícita de conjuntos de caracteres

Quando o conjunto de caracteres do parâmetro e o conjunto de caracteres do campo são diferentes, a comparação direta não pode ser feita e a conversão do conjunto de caracteres é necessária. Talvez seja necessário adicionar convert()uma função ao campo de conversão para converter o conjunto de caracteres, resultando em falha do índice.

Estrutura da tabela de teste

  • O conjunto de caracteres do banco de dados é UTF8MB4
  • t1O conjunto de caracteres da tabela é UTF8
  • t2O conjunto de caracteres da tabela é 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)

Exemplo de tabela única

-- 正常执行时,匹配字段的字符集(没有单独指定时继承表的字符集)
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)

Além disso, deve-se notar que:

O MySQL dará prioridade internamente à conversão de conjuntos de caracteres de baixo nível em conjuntos de caracteres de nível superior, como a conversão de UTF8 em UTF8MB4.

No exemplo anterior, convert()a função foi adicionada t1.aa , mas no exemplo a seguir, convert()a função foi adicionada ao parâmetro em vez t2.ado campo. Essa situação não resultou em desempenho ruim:

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)

Resumindo:

  • Quando o conjunto de caracteres do campo da tabela é um conjunto de caracteres de nível inferior (como UTF8) e o valor de entrada é um conjunto de caracteres de nível superior (como UTF8MB4), o conjunto de caracteres do campo da tabela será convertido neste momento, o que equivale a usar a Função, índice inválido.
  • Quando o campo da tabela é um conjunto de caracteres de nível superior (como UTF8MB4) e o valor de entrada é um conjunto de caracteres de nível inferior (como UTF8), o valor de entrada será convertido no conjunto de caracteres e não causará falha no índice. .

No entanto, geralmente não usamos convert()a função para converter o conjunto de caracteres de parâmetros manualmente. Nos dois cenários a seguir, pode haver conversões implícitas de tipo que são fáceis de ignorar, causando problemas de produção.

Conversão de conjunto de caracteres em junções de tabelas

Quando os conjuntos de caracteres dos campos de conexão das duas tabelas são inconsistentes, isso levará à conversão implícita ( convert()função adicionada dentro do MySQL), e o índice do campo de conexão não poderá ser alcançado e a sequência ideal de conexão da tabela não poderá ser usada.

A tabela que era originalmente uma tabela controlada pode ser usada como tabela controladora porque o índice não pode ser usado.

Exemplo:

  • Em circunstâncias normais, o MySQL dará prioridade a tabelas com pequenos conjuntos de resultados como tabelas de controle. Neste exemplo, elas são t2a tabela de controle e t1a tabela controlada.
  • No entanto, devido aos diferentes conjuntos de caracteres, o SQL realmente executado é mostrado show warningsna figura. Se uma função t1.afor adicionada ao campo convert()para converter o conjunto de caracteres, t1.ao índice do campo não poderá ser alcançado e a ordem de conexão deverá ser alterada. .
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)

Conversão de conjunto de caracteres em procedimentos armazenados

Este também é um cenário relativamente fácil de ignorar. O problema foi descoberto quando a chave primária foi atualizada durante o processo de armazenamento no ambiente de produção, mas demorou mais de 10s para ser executado.

O conjunto de caracteres de variáveis ​​​​no procedimento armazenado é herdado do databaseconjunto de caracteres de (também pode ser especificado durante a criação. Quando o conjunto de caracteres do campo da tabela databaseé diferente do conjunto de caracteres de (), a conversão implícita do tipo de conjunto de caracteres é semelhante à anterior ). um ocorrerá.

Exemplo:

  • databaseO conjunto de caracteres é UTF8MB4
  • character_set_cliente são os valores da sessão e collation_connectionao criar o procedimento armazenadocharacter_set_clientcollation_connection
  • Foi testado que o conjunto de caracteres das variáveis ​​no procedimento armazenado é consistente com o conjunto de caracteres no nível do banco de dados.
-- 存储过程信息: 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 instrução atualizada com base no campo de índice ana verdade se torna a seguinte, usando uma varredura completa da tabela (tipo: índice, chave: primária).

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)

O tempo de atualização também mudou de 0,00seg para 0,60seg . Quando a quantidade de dados da tabela é grande, uma varredura completa da tabela terá um impacto maior na produção.

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

Como evitar conversões implícitas

Para conversão implícita de tipos de dados:

  1. Seleção de tipo de dados padrão
  2. SQL passa correspondência de tipo de dados de campo participante

Para conversão implícita de conjuntos de caracteres: o conjunto de caracteres do cliente, o conjunto de caracteres do lado do servidor, o conjunto de caracteres do banco de dados, o conjunto de caracteres da tabela e o conjunto de caracteres do campo permanecem consistentes.

Para mais artigos técnicos, visite: https://opensource.actionsky.com/

Sobre SQLE

SQLE é uma plataforma abrangente de gerenciamento de qualidade de SQL que cobre auditoria e gerenciamento de SQL desde o desenvolvimento até ambientes de produção. Ele oferece suporte aos principais bancos de dados de código aberto, comerciais e domésticos, fornece recursos de automação de processos para desenvolvimento, operação e manutenção, melhora a eficiência on-line e melhora a qualidade dos dados.

Obter SQLE

tipo endereço
Repositório https://github.com/actiontech/sqle
documento https://actiontech.github.io/sqle-docs/
lançar notícias https://github.com/actiontech/sqle/releases
Documentação de desenvolvimento de plug-in de auditoria de dados https://actiontech.github.io/sqle-docs/docs/dev-manual/plugins/howtouse
Os recursos piratas de "Qing Yu Nian 2" foram carregados no npm, fazendo com que o npmmirror suspendesse o serviço unpkg. Zhou Hongyi: Não resta muito tempo para o Google. Sugiro que todos os produtos sejam de código aberto . time.sleep(6) aqui desempenha um papel. Linus é o mais ativo em “comer comida de cachorro”! O novo iPad Pro usa 12 GB de chips de memória, mas afirma ter 8 GB de memória. O People’s Daily Online analisa o carregamento estilo matryoshka do software de escritório: Somente resolvendo ativamente o “conjunto” poderemos ter um futuro . novo paradigma de desenvolvimento para Vue3, sem a necessidade de `ref/reactive `, sem necessidade de `ref.value` MySQL 8.4 LTS Manual chinês lançado: Ajuda você a dominar o novo domínio de gerenciamento de banco de dados Tongyi Qianwen nível GPT-4 modelo principal preço reduzido em 97%, 1 yuan e 2 milhões de tokens
{{o.nome}}
{{m.nome}}

Acho que você gosta

Origin my.oschina.net/actiontechoss/blog/11183928
Recomendado
Clasificación