SQL no MySQL e MariaDB
O MySQL é o banco de dados por trás de muitos sites que você usa: lojas virtuais, blogs, sistemas de escola. O MariaDB é o irmão dele, feito pelos mesmos criadores, e é o que o Debian instala quando alguém pede "o MySQL". Nesta aula a Ana instala o MariaDB no notebook dela, cria um usuário, leva a Loja Aurora para lá e descobre o que muda no SQL que ela aprendeu no SQLite: algumas coisas melhoram, outras pegam de surpresa (por exemplo, 'ana' é igual a 'ANA' lá).
- A diferença entre MySQL e MariaDB, e por que o Debian instala o MariaDB.
- Instalar, criar um banco e um usuário, e entrar com
mariadb -u ana -p loja. - Guardar a senha com segurança no
~/.my.cnfe rodar um arquivo.sql. - Os comandos do terminal:
SHOW TABLES,DESCRIBE,\G,SOURCE,quit(os "comandos de ponto" deles). - O que muda no SQL:
LIMIT, crase nos nomes,AUTO_INCREMENT,CONCAT, datas, booleanos, maiúsculas e acentos,ON DUPLICATE KEY UPDATEe o modo estrito. - As diferenças entre o próprio MySQL e o MariaDB, e os erros mais comuns com o número de cada um.
Todas as sessões de terminal são reais: rodaram num MariaDB 11.8 (a versão do Debian 13) na máquina da Ana, com a Loja Aurora. O MySQL da Oracle nós não rodamos: o que é só dele está marcado e foi conferido no manual oficial do MySQL 8.4 (dev.mysql.com). Os quadros "Resultado" continuam sendo o SQLite do seu navegador, para comparar.
A ideia: dois irmãos
O MySQL nasceu nos anos 1990 e virou um dos bancos mais usados na internet. Hoje ele pertence à Oracle. Os desenvolvedores originais do MySQL criaram o MariaDB, que o site oficial descreve como "feito pelos desenvolvedores originais do MySQL e garantido como código aberto" (licença GPLv2). No começo ele era uma troca direta pelo MySQL; com os anos, cada um ganhou recursos próprios.
Pense em dois irmãos gêmeos que foram morar em cidades diferentes: continuam falando igual, mas cada um pegou algumas gírias novas. Para quem está começando, 90% do que você faz é igual nos dois. Nesta aula, quando algo for diferente entre eles, vai aparecer escrito.
| MySQL | MariaDB | |
|---|---|---|
| Quem faz | Oracle | a comunidade MariaDB, com os desenvolvedores originais do MySQL |
| Programa de terminal | mysql, prompt mysql> | mariadb, prompt MariaDB [banco]> (no Debian, mysql também funciona: é um atalho para o mariadb) |
| No Debian 13 | não vem no Debian; a Oracle oferece um repositório APT próprio | vem no Debian: pacote mariadb-server, versão 11.8 |
1. Instalar
Diferente do SQLite, que é só um arquivo, o MySQL e o MariaDB são servidores: um programa fica rodando o tempo todo, guardando os bancos, e você conversa com ele por um programa cliente. Para entender bem essa diferença, veja Os Bancos Famosos.
No Debian ou no Ubuntu, o MariaDB vem do próprio apt (estes comandos são ilustrativos: não instalamos pacotes na máquina da Ana para esta aula, porque ela já tinha o MariaDB):
sudo apt update sudo apt install mariadb-server
O pacote mariadb-server traz o servidor e, junto, o cliente mariadb. Para o MySQL da Oracle, o manual do MySQL 8.4 diz que o jeito preferido no Debian é o repositório APT do MySQL (um repositório extra que você adiciona ao apt, baixado em dev.mysql.com); depois disso o pacote se chama mysql-server.
2. Criar o banco e o usuário
Num servidor, cada pessoa entra com um usuário e uma senha, e cada usuário só mexe no que tem permissão (aula Usuários e Permissões). Logo depois de instalar, só existe o administrador, o root. No MariaDB do Debian, o root entra pelo próprio usuário do sistema: a documentação do MariaDB diz que a conta 'root'@'localhost' usa por padrão o plugin unix_socket, que confere quem você é no Linux em vez de pedir senha. Então a Ana digitou sudo mariadb (o sudo pediu a senha dela, do Linux) e, já dentro, criou o banco e o usuário:
Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 32 Server version: 11.8.6-MariaDB-0+deb13u1 from Debian -- Please help get to 10k stars at https://github.com/MariaDB/Server Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MariaDB [(none)]> CREATE DATABASE loja; Query OK, 1 row affected (0,000 sec) MariaDB [(none)]> CREATE USER 'ana'@'localhost' IDENTIFIED BY 'Loja#Aurora26'; Query OK, 0 rows affected (0,005 sec) MariaDB [(none)]> GRANT ALL PRIVILEGES ON loja.* TO 'ana'@'localhost'; Query OK, 0 rows affected (0,002 sec) MariaDB [(none)]> SHOW DATABASES; +--------------------+ | Database | +--------------------+ | information_schema | | loja | | mysql | | performance_schema | | sys | +--------------------+ 5 rows in set (0,000 sec) MariaDB [(none)]> quit Bye
- MariaDB [(none)]>
- O prompt. Entre colchetes fica o banco em uso;
(none)quer dizer "nenhum banco escolhido ainda". - CREATE DATABASE loja;
- Cria um banco vazio chamado
loja. Num servidor cabem muitos bancos; no SQLite, cada banco é um arquivo. - CREATE USER 'ana'@'localhost' IDENTIFIED BY '…';
- Cria a usuária
ana, que só pode entrar desta mesma máquina (localhost), com essa senha. Escolha uma senha sua, forte. - GRANT ALL PRIVILEGES ON loja.* TO …;
- "Conceda todos os privilégios em todas as tabelas do banco
loja" para a Ana. Ela não ganha acesso aos outros bancos. - SHOW DATABASES;
- Lista os bancos. Os outros quatro são do próprio servidor: não mexa neles.
- Query OK, 1 row affected (0,000 sec)
- "Comando aceito, 1 linha afetada, em 0 segundos." A vírgula decimal aparece porque o sistema da Ana está em português.
3. Entrar como Ana
Agora a Ana entra com o usuário dela. O comando tem três partes importantes:
$ mariadb --version mariadb from 11.8.6-MariaDB, client 15.2 for debian-linux-gnu (x86_64) using EditLine wrapper $ mariadb -u ana -p loja Enter password: Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 35 Server version: 11.8.6-MariaDB-0+deb13u1 from Debian -- Please help get to 10k stars at https://github.com/MariaDB/Server Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MariaDB [loja]> SHOW TABLES; +----------------+ | Tables_in_loja | +----------------+ | categorias | | clientes | | fornecedores | | funcionarios | | itens_pedido | | pedidos | | produtos | +----------------+ 7 rows in set (0,000 sec) MariaDB [loja]> DESCRIBE clientes; +------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | nome | varchar(100) | NO | | NULL | | | email | varchar(100) | YES | | NULL | | | cidade | varchar(60) | YES | | NULL | | | estado | char(2) | YES | | NULL | | | nascimento | date | YES | | NULL | | | vip | tinyint(1) | NO | | 0 | | +------------+--------------+------+-----+---------+----------------+ 7 rows in set (0,001 sec) MariaDB [loja]> quit Bye
- -u ana
- user: entra como a usuária
anado banco (não é o usuário do Linux). - -p
- password: pergunte a senha. Ela aparece como
Enter password:("digite a senha") e não aparece na tela enquanto você digita. - loja
- O banco que já fica em uso ao entrar: por isso o prompt mostra
[loja]. - Reading table information…
- O cliente leu os nomes das tabelas e colunas para completar com a tecla Tab. Com
-Aele pula isso. - Commands end with ; or \g.
- "Os comandos terminam com
;ou\g": igual aosqlite3, sem o;o comando não roda.
Este banco já tem as tabelas da Loja Aurora: a Ana as importou de um arquivo, como você vai ver na seção 5. No MySQL da Oracle a sessão é igual, só trocando o comando para mysql -u ana -p loja e o prompt para mysql> (assim aparece no manual do MySQL 8.4); para sair, QUIT ou \q.
E se a senha estiver errada? O servidor recusa, com o erro 1045:
$ mariadb -u ana -p loja Enter password: ERROR 1045 (28000): Access denied for user 'ana'@'localhost' (using password: YES)
"Acesso negado para a usuária ana em localhost (usando senha: SIM)". O using password: YES só diz que uma senha foi enviada; não diz se o problema é a senha ou o usuário (de propósito, para não ajudar um invasor).
4. Guardar a senha no ~/.my.cnf
Digitar a senha toda vez cansa. Escrever a senha no próprio comando (-pLoja#Aurora26) é pior: ela fica no histórico do terminal e aparece para outros programas. O manual do MySQL recomenda guardá-la num arquivo de opções na sua pasta pessoal, o ~/.my.cnf, que o MySQL e o MariaDB leem sozinhos:
[client] user=ana password="Loja#Aurora26"
- [client]
- O grupo de opções que todos os programas clientes leem (
mariadb,mysql,mariadb-dump…), mas não o servidor. - user=ana
- Equivale a
-u ana. - password="…"
- A senha. As aspas são necessárias aqui porque a senha tem
#, e num arquivo de opções o#começa um comentário: sem as aspas, a senha lida seria sóLojae o servidor responderiaAccess denied.
Depois, deixe o arquivo legível só por você (o manual pede o modo 400 ou 600), e entre sem -u nem -p:
chmod 600 ~/.my.cnf mariadb loja
Daqui em diante, as sessões da aula usam mariadb loja.
5. Levar a Loja Aurora para o MariaDB
O arquivo aurora.sql do guia foi escrito para o SQLite. Para o MariaDB, a Ana fez o loja-mariadb.sql: os mesmos INSERT (eles funcionam igual), mas com o CREATE TABLE nos tipos do MariaDB. A tabela de clientes ficou assim:
CREATE TABLE clientes (
id INT AUTO_INCREMENT PRIMARY KEY,
nome VARCHAR(100) NOT NULL,
email VARCHAR(100),
cidade VARCHAR(60),
estado CHAR(2),
nascimento DATE,
vip BOOLEAN NOT NULL DEFAULT FALSE
);
Compare com a do SQLite (aula O Banco da Loja Aurora): TEXT virou VARCHAR(100) (texto de até 100 caracteres), a data virou DATE, o vip virou BOOLEAN e o id ganhou AUTO_INCREMENT. Para rodar um arquivo inteiro de fora do cliente, use o < do terminal; para um comando só, a opção -e (execute):
$ ls *.sql estoque-baixo.sql loja-mariadb.sql $ mariadb loja < loja-mariadb.sql $ mariadb loja -e "SELECT COUNT(*) AS clientes FROM clientes" +----------+ | clientes | +----------+ | 30 | +----------+
O < loja-mariadb.sql não mostra nada quando dá certo. Os 30 clientes chegaram.
6. Explorando o banco
Os "comandos de ponto" do sqlite3 (aula Comandos de Ponto) não existem aqui. No MySQL e no MariaDB, quase tudo é SQL mesmo (SHOW, DESCRIBE), mais alguns comandos do cliente que começam com \:
$ mariadb loja
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 43
Server version: 11.8.6-MariaDB-0+deb13u1 from Debian -- Please help get to 10k stars at https://github.com/MariaDB/Server
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [loja]> SELECT DATABASE();
+------------+
| DATABASE() |
+------------+
| loja |
+------------+
1 row in set (0,000 sec)
MariaDB [loja]> SELECT * FROM clientes WHERE id = 1\G
*************************** 1. row ***************************
id: 1
nome: José Ribamar Silva
email: jose.ribamar@exemplo.com.br
cidade: Teresina
estado: PI
nascimento: 1985-03-12
vip: 1
1 row in set (0,000 sec)
MariaDB [loja]> SOURCE estoque-baixo.sql
+-----------------------------------+---------+
| nome | estoque |
+-----------------------------------+---------+
| Jogo de Panelas 5 peças | 12 |
| Tesoura de Poda | 17 |
| Caixa de Som Portátil | 18 |
| Tapete de Yoga | 19 |
| Vaso de Cerâmica Médio | 20 |
| Jogo de Tabuleiro Trilha Mística | 21 |
| Faqueiro Inox 24 peças | 22 |
| Par de Halteres 2kg | 24 |
| Teclado Compacto | 25 |
| Atlas Geográfico | 26 |
| Boneca de Pano Maria Flor | 27 |
| Guia de Receitas do Nordeste | 29 |
+-----------------------------------+---------+
12 rows in set (0,000 sec)
- SELECT DATABASE();
- Mostra o banco em uso. Para trocar:
USE outro_banco;. - \G
- No lugar do
;, mostra cada linha "em pé": uma coluna por linha. Ótimo para tabelas largas. - SOURCE estoque-baixo.sql
- Roda os comandos de um arquivo, como o
.readdosqlite3. O arquivo tem umSELECT … WHERE estoque < 30 AND descontinuado = FALSE ORDER BY estoque;.
Se você esquecer o ;, o cliente espera mais texto e mostra ->, como o ...> do sqlite3. Basta digitar o ;:
MariaDB [loja]> SELECT nome FROM clientes WHERE estado = 'PI'
-> ;
+-----------------------+
| nome |
+-----------------------+
| José Ribamar Silva |
| Maria Clara Mendes |
| Conceição Prado |
| Luís Otávio Brito |
| Antônio Carlos Neves |
| Tiago Fontes |
| Camila Duarte |
| Larissa Macêdo |
| Ana Carvalho Lima |
| Sebastião Moura |
| Henrique Sales |
| Otávio Bezerra |
| Paulo Sérgio Andrade |
| Elias Furtado |
+-----------------------+
14 rows in set (0,000 sec)
No sqlite3 | No mariadb / mysql | O que faz |
|---|---|---|
sqlite3 loja.db | mariadb -u ana -p loja | abre o banco |
.databases | SHOW DATABASES; | lista os bancos |
.tables | SHOW TABLES; | lista as tabelas |
.schema clientes | DESCRIBE clientes; ou SHOW CREATE TABLE clientes; | mostra a estrutura da tabela |
.mode line | terminar com \G | uma coluna por linha |
.read arquivo.sql | SOURCE arquivo.sql | roda um arquivo |
sqlite3 loja.db "SELECT …" | mariadb loja -e "SELECT …" | um comando só, sem entrar |
.quit | quit, exit ou \q | sai |
7. O que muda no SQL
A Ana rodou as consultas que conhece. Quase tudo funcionou igual. As diferenças estão abaixo, cada uma comparada com o SQLite.
7.1 LIMIT: as duas formas
O MySQL e o MariaDB aceitam LIMIT 3 OFFSET 3 e a forma curta LIMIT 3, 3 (primeiro o deslocamento, depois a quantidade; o manual do MySQL explica que a forma com OFFSET existe "por compatibilidade com o PostgreSQL"). O MariaDB, desde a versão 10.6, também aceita a forma do padrão, com OFFSET … ROWS FETCH …; a sintaxe do SELECT do MySQL 8.4 só tem o LIMIT:
MariaDB [loja]> SELECT nome, preco FROM produtos ORDER BY preco DESC LIMIT 3, 3; +-------------------------+--------+ | nome | preco | +-------------------------+--------+ | Faqueiro Inox 24 peças | 119.40 | | Teclado Compacto | 99.00 | | Mochila Escolar Azul | 89.99 | +-------------------------+--------+ 3 rows in set (0,000 sec) MariaDB [loja]> SELECT nome, preco FROM produtos ORDER BY preco DESC OFFSET 3 ROWS FETCH NEXT 3 ROWS ONLY; +-------------------------+--------+ | nome | preco | +-------------------------+--------+ | Faqueiro Inox 24 peças | 119.40 | | Teclado Compacto | 99.00 | | Mochila Escolar Azul | 89.99 | +-------------------------+--------+ 3 rows in set (0,000 sec)
O SQLite também entende LIMIT 3, 3. A diferença que salta aos olhos é o preço: 119.40 lá, 119.4 aqui, porque a coluna no MariaDB é DECIMAL(10,2), que guarda sempre duas casas:
SELECT nome, preco FROM produtos ORDER BY preco DESC LIMIT 3, 3;
7.2 Crase nos nomes; aspas duplas são texto
Nomes de tabela e coluna que precisam de aspas vão entre crases: `nome`. As aspas duplas são texto, iguais às simples (a não ser que o modo ANSI_QUOTES esteja ligado). Por isso SELECT "nome" FROM clientes devolve a palavra "nome" em todas as linhas, sem erro. Isso foi mostrado em As Diferenças entre os Bancos. Outra pegadinha: no Linux, o nome da tabela diferencia maiúsculas (CLIENTES não é clientes), mas o da coluna não.
7.3 AUTO_INCREMENT, LAST_INSERT_ID e RETURNING
Com AUTO_INCREMENT, quem dá o número é o servidor. Para saber que número ele deu, use LAST_INSERT_ID() ("o último id inserido", na sua conexão). O MariaDB também tem o RETURNING, que devolve a linha inserida na hora, como no SQLite; o MySQL 8.4 não tem RETURNING no INSERT (o manual indica o LAST_INSERT_ID()):
MariaDB [loja]> INSERT INTO categorias (nome, descricao) VALUES ('Pet Shop', 'Rações e acessórios');
Query OK, 1 row affected (0,001 sec)
MariaDB [loja]> SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 9 |
+------------------+
1 row in set (0,000 sec)
MariaDB [loja]> INSERT INTO categorias (nome) VALUES ('Material de Construção') RETURNING id, nome;
+----+--------------------------+
| id | nome |
+----+--------------------------+
| 10 | Material de Construção |
+----+--------------------------+
1 row in set (0,001 sec)
No SQLite, o equivalente do LAST_INSERT_ID() é last_insert_rowid():
INSERT INTO categorias (nome, descricao) VALUES ('Pet Shop', 'Rações e acessórios');
SELECT last_insert_rowid();
7.4 Juntar textos: CONCAT, não ||
No MySQL e no MariaDB o || é "ou" lógico (o manual do MySQL 8.4 avisa que esse uso está descontinuado). Use CONCAT(a, b, …) ou CONCAT_WS(separador, a, b, …) (with separator, "com separador"). O CONCAT_WS pula os NULL; o CONCAT fica NULL se um pedaço for NULL:
MariaDB [loja]> SELECT CONCAT_WS(' - ', nome, cidade, estado) AS cliente FROM clientes WHERE id IN (1, 7);
+--------------------------------------+
| cliente |
+--------------------------------------+
| José Ribamar Silva - Teresina - PI |
| Luís Otávio Brito - Parnaíba - PI |
+--------------------------------------+
2 rows in set (0,000 sec)
O SQLite 3.50 tem as mesmas duas funções (desde a 3.44), então este SQL roda nos dois:
SELECT concat_ws(' - ', nome, cidade, estado) AS cliente
FROM clientes
WHERE id IN (1, 7);
7.5 Datas
No MySQL e no MariaDB a data é um tipo de verdade (DATE), com funções próprias: CURDATE() (hoje), NOW() (agora), DATE_ADD(d, INTERVAL 7 DAY), DATEDIFF(fim, início), YEAR(d), MONTH(d), DAY(d) e DATE_FORMAT(d, formato):
MariaDB [loja]> SELECT CURDATE() AS hoje; +------------+ | hoje | +------------+ | 2026-10-18 | +------------+ 1 row in set (0,000 sec) MariaDB [loja]> SELECT id, DATE_FORMAT(data, '%d/%m/%Y') AS data_br, YEAR(data) AS ano FROM pedidos WHERE id IN (1, 90); +----+------------+------+ | id | data_br | ano | +----+------------+------+ | 1 | 05/01/2024 | 2024 | | 90 | 14/10/2026 | 2026 | +----+------------+------+ 2 rows in set (0,000 sec) MariaDB [loja]> SELECT YEAR(data) AS ano, COUNT(*) AS pedidos FROM pedidos GROUP BY ano ORDER BY ano; +------+---------+ | ano | pedidos | +------+---------+ | 2024 | 32 | | 2025 | 32 | | 2026 | 26 | +------+---------+ 3 rows in set (0,001 sec)
Os códigos do DATE_FORMAT (%d, %m, %Y) são quase os mesmos do strftime do SQLite. A tabela completa de equivalências está em As Diferenças entre os Bancos.
Experimente: esta é a consulta do MariaDB acima, sem o ano. No SQLite ela dá erro, porque DATE_FORMAT não existe aqui. Troque por strftime com os mesmos códigos, mantendo o apelido data_br.
SELECT id, DATE_FORMAT(data, '%d/%m/%Y') AS data_br FROM pedidos WHERE id IN (1, 90);
SELECT id, strftime('%d/%m/%Y', data) AS data_br
FROM pedidos
WHERE id IN (1, 90);
7.6 Booleanos e divisão
BOOLEAN no MySQL/MariaDB é só outro nome para TINYINT(1), um número pequeno: o DESCRIBE da seção 3 mostrou vip | tinyint(1), e TRUE vale 1. Já a divisão é diferente do SQLite: 7 / 2 dá 3.5000; para a divisão inteira existe o DIV:
MariaDB [loja]> SELECT TRUE, FALSE, 7 / 2 AS divisao, 7 DIV 2 AS inteira, 7 % 2 AS resto; +------+-------+---------+---------+-------+ | TRUE | FALSE | divisao | inteira | resto | +------+-------+---------+---------+-------+ | 1 | 0 | 3.5000 | 3 | 1 | +------+-------+---------+---------+-------+ 1 row in set (0,000 sec)
No SQLite, inteiro dividido por inteiro dá inteiro (aula Funções Numéricas):
SELECT 7 / 2 AS divisao, 7 / 2.0 AS decimal, 7 % 2 AS resto;
7.7 Maiúsculas e acentos: 'ana' = 'ANA'
Esta é a diferença que mais surpreende quem vem do SQLite. Cada texto no MySQL/MariaDB tem uma collation (a regra de comparação e ordenação). A padrão do MySQL 8.4 é utf8mb4_0900_ai_ci; a do MariaDB 11.8 que a Ana usa é utf8mb4_uca1400_ai_ci. O final _ai_ci quer dizer, segundo o manual do MySQL, accent-insensitive e case-insensitive: ignora acentos e maiúsculas:
MariaDB [loja]> SELECT 'ana' = 'ANA' AS maiusculas, 'Jose' = 'José' AS acento; +------------+--------+ | maiusculas | acento | +------------+--------+ | 1 | 1 | +------------+--------+ 1 row in set (0,000 sec) MariaDB [loja]> SELECT @@character_set_server, @@collation_server; +------------------------+-----------------------+ | @@character_set_server | @@collation_server | +------------------------+-----------------------+ | utf8mb4 | utf8mb4_uca1400_ai_ci | +------------------------+-----------------------+ 1 row in set (0,000 sec) MariaDB [loja]> SELECT nome FROM clientes WHERE nome = 'jose ribamar silva'; +---------------------+ | nome | +---------------------+ | José Ribamar Silva | +---------------------+ 1 row in set (0,001 sec)
Uma busca por "jose ribamar silva", tudo minúsculo e sem acento, achou o "José Ribamar Silva". Para um campo de busca isso é ótimo; para um login, pode ser um problema. No SQLite, o = compara letra por letra; para ignorar maiúsculas (só nas letras sem acento) existe o COLLATE NOCASE:
SELECT 'ana' = 'ANA' AS maiusculas,
'ana' = 'ANA' COLLATE NOCASE AS com_nocase,
'Jose' = 'José' AS acento;
7.8 Conversões silenciosas e o modo estrito
Quando o MySQL/MariaDB não consegue converter um valor, às vezes ele não dá erro: troca o valor e deixa um aviso. 'dez' + 5 vira 5 (o texto vira 0):
MariaDB [loja]> SELECT 'dez' + 5 AS conta; +-------+ | conta | +-------+ | 5 | +-------+ 1 row in set, 1 warning (0,000 sec) MariaDB [loja]> SHOW WARNINGS; +---------+------+-----------------------------------------+ | Level | Code | Message | +---------+------+-----------------------------------------+ | Warning | 1292 | Truncated incorrect DOUBLE value: 'dez' | +---------+------+-----------------------------------------+ 1 row in set (0,000 sec)
Em gravações, porém, o modo estrito (STRICT_TRANS_TABLES, ligado por padrão no MySQL 8.4 e no MariaDB) transforma o problema em erro. A coluna estado é CHAR(2), e a Ana tentou gravar "Piauí":
MariaDB [loja]> INSERT INTO clientes (nome, estado) VALUES ('Cliente Teste', 'Piauí');
ERROR 1406 (22001): Data too long for column 'estado' at row 1
MariaDB [loja]> SELECT @@sql_mode;
+-------------------------------------------------------------------------------------------+
| @@sql_mode |
+-------------------------------------------------------------------------------------------+
| STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+-------------------------------------------------------------------------------------------+
1 row in set (0,000 sec)
"Dado longo demais para a coluna estado, na linha 1". O SQLite, nesse caso, gravaria "Piauí" sem reclamar, porque não confere o tamanho (aula Os Tipos no SQLite). O @@sql_mode mostra os modos ligados; o manual do MySQL 8.4 explica que, sem o modo estrito, o valor seria cortado com um aviso.
7.9 Inserir ou atualizar: ON DUPLICATE KEY UPDATE
O UPSERT do MySQL/MariaDB é o ON DUPLICATE KEY UPDATE ("em chave duplicada, atualize"). A Ana quer renomear a categoria 1:
MariaDB [loja]> INSERT INTO categorias (id, nome) VALUES (1, 'Papelaria e Escritório'); ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY' MariaDB [loja]> INSERT INTO categorias (id, nome) VALUES (1, 'Papelaria e Escritório') ON DUPLICATE KEY UPDATE nome = VALUE(nome); Query OK, 2 rows affected (0,001 sec) MariaDB [loja]> INSERT INTO categorias (id, nome) VALUES (1, 'Papelaria e Escritório') ON DUPLICATE KEY UPDATE nome = VALUE(nome); Query OK, 0 rows affected (0,000 sec)
- ERROR 1062 … Duplicate entry '1' for key 'PRIMARY'
- Sem o UPSERT, o
INSERTfalha: "entrada duplicada '1' na chave PRIMARY" (a chave primária). - ON DUPLICATE KEY UPDATE
- Se a chave já existe, em vez de falhar, atualize aquela linha.
- nome = VALUE(nome)
VALUE(nome)é "onomeque eu tentei inserir". No MariaDB a função se chamaVALUE()(oVALUES()antigo também funciona nesse lugar).- 2 rows affected / 0 rows affected
- A convenção do MySQL, segundo o manual: 1 = inseriu uma linha nova, 2 = atualizou uma existente, 0 = a linha já tinha esses valores. Na segunda vez nada mudou, por isso 0.
No MySQL 8.4, o manual marca o VALUES() desse lugar como descontinuado e recomenda dar um apelido à linha nova (ilustrativo, sintaxe do manual):
INSERT INTO categorias (id, nome) VALUES (1, 'Papelaria e Escritório') AS nova ON DUPLICATE KEY UPDATE nome = nova.nome;
O MariaDB 11.8 não aceita esse apelido; é uma das diferenças entre os irmãos:
MariaDB [loja]> INSERT INTO categorias (id, nome) VALUES (8, 'Jardim e Quintal') AS nova ON DUPLICATE KEY UPDATE nome = nova.nome; ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'AS nova ON DUPLICATE KEY UPDATE nome = nova.nome' at line 1
7.10 GROUP BY com coluna solta
No MariaDB, esta consulta roda: ela agrupa por estado, mas pede também o nome, que não está no GROUP BY nem numa função agregada. O banco escolhe um nome qualquer do grupo:
MariaDB [loja]> SELECT estado, COUNT(*) AS clientes, nome FROM clientes GROUP BY estado; +--------+----------+----------------------+ | estado | clientes | nome | +--------+----------+----------------------+ | CE | 7 | Carlos Eduardo Lima | | PE | 2 | Juliana Paes Barbosa | | PI | 14 | José Ribamar Silva | | SP | 7 | Ana Beatriz Sousa | +--------+----------+----------------------+ 4 rows in set (0,001 sec)
O SQLite também aceita (veja o quadro abaixo). O MySQL 8.4, não: o modo ONLY_FULL_GROUP_BY está ligado por padrão lá e, segundo o manual, rejeita consultas que pedem colunas "não agregadas" fora do GROUP BY (erro 1055). O @@sql_mode do MariaDB, na seção 7.8, não tem esse modo. Escreva sempre como o MySQL exige: toda coluna do SELECT ou está no GROUP BY, ou dentro de uma função como MIN(nome) (aula GROUP BY).
SELECT estado, COUNT(*) AS clientes, nome FROM clientes GROUP BY estado;
Formas diferentes: inserir sem duplicar
Quando a linha pode já existir, há três jeitos de escrever o INSERT no MariaDB, com equivalentes no SQLite:
MariaDB [loja]> INSERT IGNORE INTO categorias (id, nome) VALUES (2, 'Eletrônicos e Informática'); Query OK, 0 rows affected, 1 warning (0,000 sec) MariaDB [loja]> SHOW WARNINGS; +---------+------+---------------------------------------+ | Level | Code | Message | +---------+------+---------------------------------------+ | Warning | 1062 | Duplicate entry '2' for key 'PRIMARY' | +---------+------+---------------------------------------+ 1 row in set (0,000 sec)
O INSERT IGNORE transformou o erro 1062 num aviso e não mudou nada. No SQLite, o mesmo se escreve INSERT OR IGNORE:
INSERT OR IGNORE INTO categorias (id, nome) VALUES (2, 'Eletrônicos e Informática'); SELECT id, nome FROM categorias WHERE id = 2;
| Você quer… | MySQL / MariaDB | SQLite |
|---|---|---|
| falhar se já existe (o normal) | INSERT … → erro 1062 | INSERT … → UNIQUE constraint failed |
| ignorar se já existe | INSERT IGNORE … | INSERT OR IGNORE … ou … ON CONFLICT DO NOTHING |
| atualizar se já existe | … ON DUPLICATE KEY UPDATE col = VALUE(col) (MySQL: AS nova … nova.col) | … ON CONFLICT (id) DO UPDATE SET col = excluded.col |
Qual usar? Atualizar quando o dado novo deve valer (um preço novo). Ignorar só quando você tem certeza de que a linha antiga está certa: o IGNORE engole o erro, e erros engolidos escondem problemas.
MySQL × MariaDB: o que é diferente entre os irmãos
| Assunto | MySQL 8.4 (manual) | MariaDB 11.8 (rodado) |
|---|---|---|
| Cliente | mysql, prompt mysql> | mariadb, prompt MariaDB [loja]> |
OFFSET … FETCH … | não (só LIMIT) | sim (desde a 10.6) |
INSERT … RETURNING | não (use LAST_INSERT_ID()) | sim |
| UPSERT | VALUES (…) AS nova ON DUPLICATE KEY UPDATE c = nova.c | ON DUPLICATE KEY UPDATE c = VALUE(c) |
ONLY_FULL_GROUP_BY por padrão | sim | não |
| Collation padrão | utf8mb4_0900_ai_ci | utf8mb4_uca1400_ai_ci |
Quando dá errado
Os erros do MySQL e do MariaDB têm sempre o mesmo formato: ERROR número (SQLSTATE): mensagem. O número é o mesmo nos dois bancos e é ótimo para pesquisar; o SQLSTATE, entre parênteses, é um código de 5 caracteres definido pelo padrão SQL. As mensagens saem em inglês.
MariaDB [loja]> SELECT nome FROM cliente; ERROR 1146 (42S02): Table 'loja.cliente' doesn't exist
"A tabela loja.cliente não existe": faltou o s. O banco mostra o nome completo, banco.tabela. No SQLite a mesma falha é no such table: cliente.
Como consertar: SHOW TABLES; para ver os nomes certos. Lembre que no Linux Clientes e clientes são tabelas diferentes.
MariaDB [loja]> SELECT nome, cidad FROM clientes; ERROR 1054 (42S22): Unknown column 'cidad' in 'SELECT'
"Coluna desconhecida 'cidad' no SELECT". O MySQL escreve o fim de outro jeito (o manual registra a mensagem como Unknown column '%s' in '%s', e o segundo pedaço varia), mas o número 1054 é o mesmo.
Como consertar: DESCRIBE clientes; e corrija para cidade.
MariaDB [loja]> SELECT TOP 3 nome FROM produtos; ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '3 nome FROM produtos' at line 1
"Você tem um erro na sintaxe SQL; confira no manual da sua versão a sintaxe certa para usar perto de '3 nome FROM produtos', na linha 1." O trecho depois do near começa exatamente onde o banco se perdeu: aqui, no 3 depois do TOP (que é do SQL Server). Leia o que vem logo antes desse trecho.
Como consertar: SELECT nome FROM produtos ORDER BY preco DESC LIMIT 3;.
Visto na seção 7.9: ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'. Você tentou inserir um id que já existe. Como consertar: não informe o id (deixe o AUTO_INCREMENT escolher) ou use ON DUPLICATE KEY UPDATE.
Visto na seção 7.8: ERROR 1406 (22001): Data too long for column 'estado' at row 1. Como consertar: grave o valor no tamanho da coluna ('PI') ou aumente a coluna com ALTER TABLE (aula ALTER TABLE).
Visto na seção 3: ERROR 1045 (28000): Access denied for user 'ana'@'localhost' (using password: YES). Confira a senha; se ela está no ~/.my.cnf e tem #, ponha-a entre aspas. Se aparecer using password: NO, nenhuma senha foi enviada: faltou o -p.
Os dois mais traiçoeiros não dão erro nenhum: WHERE "estado" = 'PI' compara o texto "estado" com "PI" (volta vazio), e nome || cidade devolve 0 com avisos. Ambos estão em As Diferenças entre os Bancos. Se aparecer warnings no fim do resultado, rode SHOW WARNINGS;.
Referência rápida
| Comando / SQL | O que faz |
|---|---|
sudo apt install mariadb-server | instala o MariaDB no Debian/Ubuntu |
sudo mariadb | entra como administrador (root) |
CREATE DATABASE loja; | cria um banco |
CREATE USER 'ana'@'localhost' IDENTIFIED BY '…'; | cria um usuário |
GRANT ALL PRIVILEGES ON loja.* TO 'ana'@'localhost'; | dá permissão no banco |
mariadb -u ana -p loja | entra (pede a senha) |
mariadb loja < arquivo.sql / -e "SQL" | roda um arquivo / um comando, sem entrar |
SHOW DATABASES; SHOW TABLES; DESCRIBE t; | lista bancos, tabelas, colunas |
\G · SOURCE f.sql · quit | uma coluna por linha · roda arquivo · sai |
SHOW WARNINGS; | lê os avisos do último comando |
`nome` | aspas de nome (identificador) |
AUTO_INCREMENT · LAST_INSERT_ID() | id automático · último id gerado |
CONCAT() · CONCAT_WS() | juntar textos |
CURDATE() · NOW() · DATE_ADD(d, INTERVAL n DAY) · DATEDIFF(fim, início) · DATE_FORMAT(d, '%d/%m/%Y') · YEAR(d) | datas |
7 DIV 2 | divisão inteira |
INSERT IGNORE · ON DUPLICATE KEY UPDATE | ignorar / atualizar se a chave já existe |
Exercícios
1. Entrar pedindo a senha
Complete o comando para a Ana entrar no banco loja com a usuária ana, pedindo a senha:
2. Listar as tabelas
Qual comando, dentro do mariadb, faz o mesmo que o .tables do sqlite3?
3. No MariaDB, a Ana rodou SELECT nome FROM clientes WHERE nome = 'ana beatriz sousa';. O que acontece?
4. O que quer dizer Query OK, 2 rows affected depois de um INSERT … ON DUPLICATE KEY UPDATE de uma linha só?
5. A senha da Ana no ~/.my.cnf é Sol#2026. Como ela deve escrever a linha?
6. Qual destes só funciona no MariaDB, e não no MySQL 8.4?
Missão final: o relatório por ano
Missão: esta consulta conta os pedidos por ano no MariaDB (você viu o resultado na seção 7.5: 32, 32 e 26). No SQLite, YEAR() não existe. Reescreva para o SQLite, mantendo os apelidos ano e pedidos.
SELECT YEAR(data) AS ano, COUNT(*) AS pedidos FROM pedidos GROUP BY ano ORDER BY ano;
SELECT strftime('%Y', data) AS ano, COUNT(*) AS pedidos
FROM pedidos
GROUP BY ano
ORDER BY ano;
Resumo
- MySQL (Oracle) e MariaDB (dos criadores originais do MySQL) são irmãos: o SQL do dia a dia é igual. O Debian instala o MariaDB (
sudo apt install mariadb-server). - São servidores: crie o banco e o usuário como
root(sudo mariadb, depoisCREATE DATABASE,CREATE USER,GRANT) e entre commariadb -u ana -p loja. A senha pode ficar no~/.my.cnf, comchmod 600(e aspas se tiver#). - No lugar dos comandos de ponto:
SHOW TABLES,DESCRIBE,\G,SOURCE,quit. - No SQL: crase para nomes (aspas duplas são texto),
AUTO_INCREMENT,CONCAT(não||), datas comDATE_ADD/DATEDIFF/DATE_FORMAT/YEAR,BOOLEAN=TINYINT(1),7 / 2 = 3.5000. - A collation padrão ignora maiúsculas e acentos:
'ana' = 'ANA'e'Jose' = 'José'são verdadeiros. - Leituras convertem em silêncio (com
warnings); gravações, com o modo estrito, dão erro (1406). UPSERT:ON DUPLICATE KEY UPDATE. - Erros têm número: 1045 (acesso), 1054 (coluna), 1062 (duplicado), 1064 (sintaxe), 1146 (tabela), 1406 (grande demais).