SQL Os Tipos no SQLite
Na aula Tipos de Dados você viu que, no MySQL, no PostgreSQL e no SQL Server, o tipo da coluna é lei: coluna de número só aceita número. O SQLite pensa diferente. Nele, o tipo da coluna é uma sugestão, e quem tem tipo de verdade é cada valor. Isso se chama tipagem dinâmica. Nesta aula você vai ver o que isso quer dizer na prática (sim, dá para guardar a palavra "cinco" numa coluna INTEGER), como descobrir o tipo de cada valor com typeof() e como ligar o modo rígido do SQLite, as tabelas STRICT.
- As cinco classes de armazenamento do SQLite:
NULL,INTEGER,REAL,TEXTeBLOB. - A função
typeof(), que diz o tipo de qualquer valor. - A afinidade: como o SQLite lê o tipo que você escreveu no
CREATE TABLEe o que ele faz com cada valor que chega. - O que acontece quando um texto vai parar numa coluna
INTEGER, e como isso estraga uma média sem dar erro. - As tabelas
STRICT: os seis tipos que elas aceitam e as mensagens de erro reais delas.
A ideia: a etiqueta na gaveta
Pense no estoque da Loja Aurora. Cada gaveta tem uma etiqueta: "Lápis", "Canetas", "Borrachas". Se alguém põe uma borracha na gaveta dos lápis, a gaveta não reclama: a etiqueta só sugere o que deveria estar ali. Um funcionário caprichoso até tenta ajudar ("isto aqui é um lápis embrulhado, vou desembrulhar"), mas, se não der, a borracha fica lá dentro mesmo.
Uma coluna do SQLite funciona assim. O tipo que você escreve no CREATE TABLE é a etiqueta. Quando chega um valor, o SQLite tenta convertê-lo para o tipo da etiqueta; se a conversão não der certo sem perder nada, ele guarda o valor do jeito que veio.
| MySQL, PostgreSQL, SQL Server | SQLite (normal) | SQLite com STRICT | |
|---|---|---|---|
| O tipo é… | da coluna | de cada valor | da coluna |
A coluna INTEGER recebe 'cinco' | Erro | Guarda o texto 'cinco' | Erro |
| Comparação | Uma fechadura | Uma etiqueta | Uma fechadura |
1. As cinco classes de armazenamento
Todo valor guardado no SQLite pertence a uma de cinco classes de armazenamento (em inglês, storage classes). São só cinco, e é com elas que o SQLite trabalha por dentro:
| Classe | O que é | Exemplo |
|---|---|---|
NULL | A ausência de valor (aula O Valor NULL) | NULL |
INTEGER | Número inteiro, com sinal, até 8 bytes | 42 |
REAL | Número com casas decimais (ponto flutuante de 8 bytes) | 4.2 |
TEXT | Texto | 'Ana' |
BLOB | Bytes, guardados exatamente como chegaram | x'CAFE' |
Repare no que não está na lista: não existe classe para data nem para verdadeiro/falso. Datas são guardadas como TEXT ('2026-10-14') e o verdadeiro/falso como INTEGER (1 e 0), como você viu na aula anterior.
A função typeof()
Para saber a classe de um valor, use typeof() ("tipo de"). Ela devolve o nome da classe, em minúsculas:
SELECT typeof(42), typeof(4.2), typeof('Ana'), typeof(NULL), typeof(x'CAFE');
- typeof(42)
integer: número sem ponto.- typeof(4.2)
real: número com ponto.- typeof('Ana')
text: o que está entre aspas simples é texto.- typeof(NULL)
null: o "vazio" também tem a sua classe.- typeof(x'CAFE')
blob: umxseguido de algarismos hexadecimais entre aspas escreve bytes.CAFEsão dois bytes.
A função também serve nas colunas de uma tabela, linha por linha. Nos produtos da Loja Aurora, o preço é real e o estoque é integer:
SELECT nome, preco, typeof(preco), estoque, typeof(estoque) FROM produtos LIMIT 3;
E, junto com o GROUP BY (aula GROUP BY), ela faz um "raio X" de uma coluna inteira: quantos valores de cada classe existem lá. Na coluna email dos clientes:
SELECT typeof(email), count(*) FROM clientes GROUP BY typeof(email);
São 27 emails de texto e 3 NULL: os clientes 7, 15 e 23, que não informaram email.
Experimente: faça o mesmo "raio X" na coluna nascimento dos clientes. Quantos clientes estão sem data de nascimento?
SELECT typeof(email), count(*) FROM clientes GROUP BY typeof(email);
SELECT typeof(nascimento), count(*) FROM clientes GROUP BY typeof(nascimento);
2. A afinidade: o que o SQLite faz com o tipo que você escreveu
Você pode escrever praticamente qualquer nome de tipo no CREATE TABLE do SQLite: VARCHAR(100), DECIMAL(10,2), BOOLEAN, DATE… Ele aceita todos. Mas, por dentro, ele resume cada nome a uma de cinco afinidades (em inglês, type affinity): o tipo que aquela coluna "prefere".
A regra está na documentação do SQLite e é aplicada nesta ordem, olhando as letras do nome do tipo:
- Se o nome contém
INT→ afinidade INTEGER. - Senão, se contém
CHAR,CLOBouTEXT→ afinidade TEXT. - Senão, se contém
BLOB, ou se a coluna não tem tipo → afinidade BLOB (nenhuma conversão). - Senão, se contém
REAL,FLOAouDOUB→ afinidade REAL. - Senão → afinidade NUMERIC.
Aplicando a regra aos nomes que você viu na aula anterior:
| Você escreve | Regra | Afinidade |
|---|---|---|
INTEGER, INT, SMALLINT, BIGINT | 1 (tem INT) | INTEGER |
TEXT, VARCHAR(100), CHAR(2) | 2 (tem CHAR ou TEXT) | TEXT |
BLOB, ou nenhum tipo | 3 | BLOB |
REAL, FLOAT, DOUBLE PRECISION | 4 | REAL |
NUMERIC, DECIMAL(10,2), BOOLEAN, DATE, DATETIME | 5 (nenhuma das anteriores) | NUMERIC |
FLOATING POINT (pegadinha!) | 1: "POINT" tem INT no meio | INTEGER |
STRING (pegadinha!) | 5: não tem nenhuma das palavras | NUMERIC |
Duas consequências importantes. Primeira: os números entre parênteses são ignorados. VARCHAR(2) é só "texto", sem limite nenhum. Segunda: DATE e BOOLEAN não criam nada especial; eles caem na afinidade NUMERIC, a mesma do DECIMAL.
O que cada afinidade faz com um valor
A afinidade age no momento em que o valor entra (no INSERT ou no UPDATE):
| Afinidade | Quando chega um texto que parece número | Quando chega um número |
|---|---|---|
| TEXT | Fica texto | Vira texto |
| INTEGER e NUMERIC | Vira número (inteiro, se der) | Fica número; 3.0 vira o inteiro 3 |
| REAL | Vira número com decimais | Vira número com decimais: 42 vira 42.0 |
| BLOB | Fica como chegou | Fica como chegou |
O exemplo clássico da documentação do SQLite: o mesmo texto '500.0' inserido em cinco colunas, uma de cada afinidade.
CREATE TABLE t1 (
t TEXT, -- afinidade TEXT
nu NUMERIC, -- afinidade NUMERIC
i INTEGER, -- afinidade INTEGER
r REAL, -- afinidade REAL
no BLOB -- afinidade BLOB (nenhuma)
);
INSERT INTO t1 VALUES ('500.0', '500.0', '500.0', '500.0', '500.0');
SELECT t, typeof(t), nu, typeof(nu), i, typeof(i), r, typeof(r), no, typeof(no)
FROM t1;
- t (TEXT)
- Continua o texto
500.0. - nu (NUMERIC)
- Virou o inteiro
500: o texto parecia um número, e um número sem parte decimal de verdade vira inteiro. - i (INTEGER)
- A mesma coisa: inteiro
500. - r (REAL)
- Virou o número com decimais
500.0(classereal). - no (BLOB)
- Nenhuma conversão: continua o texto
500.0.
3. Texto numa coluna INTEGER
Agora o caso que assusta quem vem de outros bancos. Uma coluna INTEGER recebe seis valores de tipos diferentes:
CREATE TABLE t (n INTEGER);
INSERT INTO t VALUES (10), ('20'), ('abc'), (3.0), (3.5), (NULL);
SELECT n, typeof(n) FROM t;
| Valor inserido | Guardado como | Por quê |
|---|---|---|
10 | 10, integer | Já era inteiro |
'20' | 20, integer | Texto que parece inteiro: a afinidade converteu |
'abc' | abc, text | Não dá para converter sem perder nada: ficou texto mesmo, e sem erro |
3.0 | 3, integer | Número com decimal "de mentira": vira inteiro sem perder nada |
3.5 | 3.5, real | Virar inteiro perderia o .5, então ficou real |
NULL | NULL, null | NULL nunca é convertido |
Numa coluna chamada "inteiro", três classes diferentes. Para o SQLite está tudo certo. Para quem consulta, nem tanto, como você vai ver agora.
O estrago: uma média que mente
A Ana montou uma tabela de notas dos produtos. Alguém digitou a nota por extenso:
CREATE TABLE notas (n INTEGER);
INSERT INTO notas VALUES (5), ('quatro'), (4);
SELECT avg(n) FROM notas;
A média deu 3.0. Mas as notas são 5 e 4, e a média delas é 4,5! O que aconteceu: o 'quatro' ficou guardado como texto, e na hora da conta o avg() tratou esse texto como 0. Resultado: (5 + 0 + 4) / 3 = 3. Nenhum erro, nenhum aviso. Só um relatório errado.
E na ordenação?
Quando uma coluna mistura classes, o ORDER BY segue uma ordem fixa: primeiro os NULL, depois os números (inteiros e reais juntos, pelo valor), depois os textos, depois os blobs.
CREATE TABLE t (n INTEGER);
INSERT INTO t VALUES (10), ('abc'), (9), (2.5), (NULL);
SELECT n, typeof(n) FROM t ORDER BY n;
Experimente: a afinidade também pode atrapalhar. Rode e veja: o CEP 04567000 perdeu o zero da frente, porque a coluna tem afinidade INTEGER e o texto foi convertido em número. Troque o tipo da coluna cep para que o CEP fique intacto.
CREATE TABLE enderecos (cliente_id INTEGER, cep INTEGER); INSERT INTO enderecos VALUES (5, '04567000'); SELECT cep, typeof(cep) FROM enderecos;
CREATE TABLE enderecos (cliente_id INTEGER, cep TEXT); INSERT INTO enderecos VALUES (5, '04567000'); SELECT cep, typeof(cep) FROM enderecos;
Cuidado com nomes de tipo inventados: STRING parece "texto", mas, pela regra 5, a afinidade dele é NUMERIC, e o zero da frente some do mesmo jeito:
CREATE TABLE t (codigo STRING);
INSERT INTO t VALUES ('0042');
SELECT codigo, typeof(codigo) FROM t;
4. Tabelas STRICT: o SQLite rígido
Desde a versão 3.37.0 (de 2021), o SQLite tem um modo rígido, por tabela: basta escrever STRICT ("estrito") depois do parêntese que fecha o CREATE TABLE. Numa tabela STRICT, o tipo da coluna vira fechadura: o valor que não pode ser convertido sem perda é recusado, com erro.
CREATE TABLE avaliacoes (
id INTEGER,
produto_id INTEGER,
nota INTEGER,
comentario TEXT
) STRICT;
INSERT INTO avaliacoes VALUES (1, 3, 'cinco', 'Chegou rápido');
- ) STRICT
- A palavra vai fora dos parênteses, logo depois do
)que fecha a lista de colunas, e antes do;. - cannot store TEXT value
- "Não é possível guardar um valor TEXT…"
- in INTEGER column
- "…numa coluna INTEGER…"
- avaliacoes.nota
- "…
avaliacoes.nota": a mensagem diz a tabela e a coluna, separadas por ponto. Ótimo para achar o problema numINSERTcom muitas colunas.
No terminal, a mesma mensagem vem com o prefixo Runtime error: ("erro durante a execução") e, no fim, o número (19), o código que o SQLite usa para "uma restrição foi violada":
$ sqlite3 loja.db SQLite version 3.50.4 2025-07-30 19:33:53 Enter ".help" for usage hints. sqlite> CREATE TABLE avaliacoes (id INTEGER, produto_id INTEGER, nota INTEGER, comentario TEXT) STRICT; sqlite> INSERT INTO avaliacoes VALUES (1, 3, 'cinco', 'Chegou rápido'); Runtime error: cannot store TEXT value in INTEGER column avaliacoes.nota (19) sqlite> .quit
STRICT ainda converte quando não perde nada
O modo rígido não é implicante: se o valor pode virar o tipo da coluna sem perder nada, ele converte, como antes. O texto '5' vira o inteiro 5 sem problema:
CREATE TABLE avaliacoes (
id INTEGER,
produto_id INTEGER,
nota INTEGER,
comentario TEXT
) STRICT;
INSERT INTO avaliacoes VALUES (1, 3, '5', 'Chegou rápido');
SELECT nota, typeof(nota) FROM avaliacoes;
Já 3.5 numa coluna INTEGER perderia o .5, então é recusado:
CREATE TABLE notas (n INTEGER) STRICT; INSERT INTO notas VALUES (3.5);
Os seis tipos de uma tabela STRICT
Numa tabela STRICT, toda coluna tem de ter tipo, e só valem estes seis nomes:
| Tipo | Aceita |
|---|---|
INT ou INTEGER | Inteiros (e o que vira inteiro sem perda, como '5' ou 3.0) |
REAL | Números (um inteiro vira real: 42 vira 42.0) |
TEXT | Texto (um número vira texto) |
BLOB | Bytes |
ANY | Qualquer coisa, guardada exatamente como chegou, sem conversão nenhuma |
Em todas, NULL é aceito, a menos que a coluna tenha a regra NOT NULL. Os nomes "dos outros bancos" não passam:
CREATE TABLE clientes_novos (
nome VARCHAR(100),
email TEXT
) STRICT;
"Tipo de dado desconhecido para clientes_novos.nome: VARCHAR(100)." Em tabela STRICT, escreva TEXT. Datas também: DATE dá o mesmo erro, e a data vai numa coluna TEXT no formato 'AAAA-MM-DD'.
Como saber se uma tabela é STRICT
O PRAGMA table_list lista as tabelas, e a última coluna, strict, vale 1 nas tabelas rígidas. As da Loja Aurora são todas normais (0); a avaliacoes criada com STRICT aparece com 1:
CREATE TABLE avaliacoes (id INTEGER, nota INTEGER) STRICT; SELECT name, strict FROM pragma_table_list WHERE schema = 'main' AND name NOT LIKE 'sqlite_%';
(pragma_table_list é o mesmo PRAGMA table_list, escrito como tabela, para poder usar SELECT e WHERE; o NOT LIKE 'sqlite_%' esconde a tabela interna sqlite_schema. Mais em PRAGMAs Úteis.)
Formas diferentes de proteger os tipos
| Forma | Exemplo | Quando usar |
|---|---|---|
Tabela STRICT | CREATE TABLE avaliacoes (…) STRICT; | Tabelas novas, quando o SQLite é 3.37.0 ou mais novo. A forma mais simples e completa |
CHECK com typeof() | nota INTEGER CHECK (typeof(nota) = 'integer') | Para proteger uma coluna, ou quando a tabela precisa de um tipo como DATE no nome (aula CHECK) |
| Converter na consulta | SELECT CAST(n AS INTEGER) FROM t; | Para ler dados que já estão bagunçados. Não protege nada, só remenda |
| Confiar no programa | O sistema da loja só manda números | É o que muita gente faz, e funciona até o dia em que alguém digita "quatro" |
O CHECK dá um erro diferente, que mostra a regra que falhou:
CREATE TABLE avaliacoes (
id INTEGER,
nota INTEGER CHECK (typeof(nota) = 'integer')
);
INSERT INTO avaliacoes VALUES (1, 'cinco');
Quando dá errado
CREATE TABLE avaliacoes (id INTEGER, nota INTEGER); INSERT INTO avaliacoes VALUES (1, 'cinco'); SELECT nota, typeof(nota) FROM avaliacoes;
O que acontece: o INSERT passa e o texto fica lá. O problema só aparece numa conta (a média que deu 3.0) ou numa ordenação. Como evitar: crie a tabela com STRICT, ou proteja a coluna com CHECK (typeof(nota) = 'integer'). Para achar o lixo numa tabela que já existe: SELECT * FROM avaliacoes WHERE typeof(nota) <> 'integer';.
STRICT com um tipo que ele não conhece
CREATE TABLE entregas (id INTEGER, saiu_em DATE) STRICT;
O que diz: unknown datatype for entregas.saiu_em: "DATE", "tipo de dado desconhecido". No terminal: Parse error: unknown datatype for entregas.saiu_em: "DATE". Como consertar: só INT, INTEGER, REAL, TEXT, BLOB ou ANY. A data vira TEXT.
STRICT com uma coluna sem tipo
CREATE TABLE avaliacoes (id INTEGER, comentario) STRICT;
O que diz: missing datatype for avaliacoes.comentario, "falta o tipo de dado". Na tabela normal, coluna sem tipo é permitida (afinidade BLOB); na STRICT, não. Como consertar: comentario TEXT.
INTEGER de tabela STRICT
Você já viu: cannot store REAL value in INTEGER column notas.n. Como consertar: se a coluna precisa de decimais, ela é REAL. Se não precisa, arredonde antes de guardar: round(3.5) ou CAST(3.5 AS INTEGER) (que corta: dá 3).
STRING, CARACTERES, NOME… qualquer nome inventado sem CHAR, CLOB ou TEXT cai na afinidade NUMERIC, e um código como '0042' vira o número 42. Como evitar: use os nomes de sempre (TEXT, VARCHAR(n)) e confira com typeof().
No MySQL (no modo estrito, que é o padrão do MySQL 8.4), no PostgreSQL e no SQL Server, o tipo da coluna é sempre lei: um texto que não é número, numa coluna de número, dá erro, como numa tabela STRICT do SQLite. Neles, todos os valores de uma coluna têm o tipo da coluna. Se um dia a Loja Aurora mudar para um desses bancos, os dados "bagunçados" de uma tabela SQLite normal são os que vão dar trabalho na mudança: mais um motivo para usar STRICT desde o começo.
Referência rápida
| Escrita | O que faz |
|---|---|
typeof(x) | A classe do valor: null, integer, real, text ou blob |
CAST(x AS INTEGER) | Converte para inteiro ('12abc' dá 12; 'abc' dá 0; 3.99 dá 3) |
CREATE TABLE t (…) STRICT; | Tabela rígida: recusa o valor que não cabe no tipo |
INT, INTEGER, REAL, TEXT, BLOB, ANY | Os únicos tipos de uma tabela STRICT |
SELECT name, strict FROM pragma_table_list; | Mostra quais tabelas são STRICT |
CHECK (typeof(col) = 'integer') | Protege uma coluna só, numa tabela normal |
| Mensagem do SQLite | Quer dizer |
|---|---|
cannot store TEXT value in INTEGER column t.c | Tabela STRICT: um texto que não é número numa coluna inteira |
cannot store REAL value in INTEGER column t.c | Tabela STRICT: um número com decimais numa coluna inteira |
unknown datatype for t.c: "DATE" | Tabela STRICT com um tipo fora dos seis |
missing datatype for t.c | Tabela STRICT com uma coluna sem tipo |
CHECK constraint failed: typeof(nota) = 'integer' | A regra CHECK recusou o valor |
Exercícios
1. Qual é a classe?
Complete com o que o SQLite responde para SELECT typeof(1.15);:
2. A tabela rígida
Complete para que a tabela recuse texto na coluna nota:
3. Numa tabela normal (sem STRICT), a coluna n INTEGER recebe 'abc'. O que acontece?
4. Qual é a afinidade de uma coluna declarada como VARCHAR(2)?
5. Qual destes CREATE TABLE dá erro?
6. Numa tabela STRICT, a coluna n INTEGER recebe o texto '7'. O que acontece?
Missão final: tranque a tabela
Missão: rode como está: o INSERT com a nota 'cinco' passou, e o texto foi parar na coluna INTEGER. Mude a tabela para que o SQLite recuse essa avaliação com o erro cannot store TEXT value in INTEGER column avaliacoes.nota. (Uma palavra resolve.)
CREATE TABLE avaliacoes (
id INTEGER,
produto_id INTEGER,
nota INTEGER,
comentario TEXT
);
INSERT INTO avaliacoes VALUES (1, 3, 'cinco', 'Chegou rápido');
SELECT nota, typeof(nota) FROM avaliacoes;
CREATE TABLE avaliacoes (
id INTEGER,
produto_id INTEGER,
nota INTEGER,
comentario TEXT
) STRICT;
INSERT INTO avaliacoes VALUES (1, 3, 'cinco', 'Chegou rápido');
SELECT nota, typeof(nota) FROM avaliacoes;
Aqui o erro é o resultado que você quer: ele prova que a tabela agora protege os dados. Troque 'cinco' por 5 e rode de novo para ver a avaliação entrar.
Resumo
- No SQLite, o tipo é de cada valor, não da coluna (tipagem dinâmica). Os valores são de cinco classes:
NULL,INTEGER,REAL,TEXT,BLOB. typeof(x)diz a classe de um valor; comGROUP BY typeof(col), você faz um raio X da coluna.- O tipo escrito no
CREATE TABLEvira uma afinidade (INTEGER, TEXT, BLOB, REAL ou NUMERIC), pelas letras do nome. Os números entre parênteses são ignorados. - A afinidade converte quando pode sem perda (
'20'vira 20,3.0vira 3); quando não pode, guarda como veio. Texto numa colunaINTEGERpassa sem erro e estraga médias e ordenações. - Tabelas
STRICT(desde o SQLite 3.37.0) recusam o valor errado:cannot store TEXT value in INTEGER column avaliacoes.nota. Elas só aceitamINT,INTEGER,REAL,TEXT,BLOBeANY.