SQL Inserindo Várias Linhas
Na aula INSERT INTO, a Ana cadastrou uma linha de cada vez. Mas um pedido da Loja Aurora quase nunca tem um item só, e quando a loja abre três seções novas, ninguém quer escrever três comandos iguais. O INSERT aceita várias linhas num comando só: basta pôr várias listas de valores depois do VALUES, separadas por vírgula. Nesta aula você vai usar isso para cadastrar categorias, clientes e um pedido completo, e vai ver uma propriedade importante: se uma das linhas tem erro, nenhuma entra.
- A forma
VALUES (…), (…), (…): várias linhas numINSERT. - Como o banco numera as linhas novas, uma depois da outra.
- A diferença entre um
INSERTcom várias linhas e váriosINSERTseguidos. - O "tudo ou nada": por que uma linha com erro barra o comando inteiro.
- Como cadastrar um pedido novo e os seus itens, em duas tabelas.
RETURNING: ver na hora osidque o banco escolheu.- Os erros mais comuns: vírgula faltando, vírgula sobrando, linhas de tamanhos diferentes.
Como sempre no editor do guia, cada Rodar começa do banco limpo: pode inserir à vontade. Por isso cada exemplo termina com um SELECT para você ver as linhas novas.
A ideia: uma pilha de fichas de uma vez
Em vez de ir ao fichário três vezes, uma ficha por viagem, a Ana preenche três fichas na mesa e guarda a pilha inteira de uma vez. Se uma das fichas estiver rasurada, ela não guarda a pilha: corrige primeiro e guarda tudo junto. É assim que o INSERT com várias linhas funciona.
| Uma linha por vez | Várias linhas de uma vez |
|---|---|
INSERT INTO t (a) VALUES (1);INSERT INTO t (a) VALUES (2);INSERT INTO t (a) VALUES (3); | INSERT INTO t (a) VALUES (1), (2), (3); |
| Três comandos: cada um entra (ou falha) sozinho | Um comando: as três entram juntas, ou nenhuma entra |
1. Três categorias num INSERT só
A Aurora vai abrir três seções: Pet Shop, Automotivo e Beleza.
INSERT INTO categorias (nome, descricao) VALUES
('Pet Shop', 'Ração, brinquedos e acessórios para animais'),
('Automotivo', 'Acessórios para o carro'),
('Beleza', 'Cosméticos e cuidados pessoais');
SELECT * FROM categorias WHERE id > 6;
- INSERT INTO categorias (nome, descricao)
- Igual ao
INSERTde uma linha: a tabela e a lista de colunas, escrita uma vez só. Ela vale para todas as linhas. - VALUES
- Também aparece uma vez só.
- ('Pet Shop', '…'),
- A primeira linha: uma lista de valores entre parênteses, na ordem das colunas. A vírgula depois do parêntese avisa que vem mais uma linha.
- ('Automotivo', '…'),
- A segunda linha, também seguida de vírgula.
- ('Beleza', '…');
- A última linha termina com
;e sem vírgula: depois dela não vem mais nada.
O quadro mostra "3 linhas afetadas": um comando, três linhas. E os id saíram em sequência, 9, 10 e 11, na ordem em que as linhas foram escritas: cada uma ganhou "o maior id que existe, mais 1", no momento em que entrou.
Os espaços a mais depois de 'Pet Shop', são só para alinhar as colunas e facilitar a leitura; o SQL ignora espaços e quebras de linha fora das aspas. Uma linha por registro, com os valores alinhados, é o jeito mais fácil de conferir um cadastro grande.
Experimente: o INSERT abaixo cadastra só a Pet Shop. Acrescente, no mesmo comando, mais duas categorias: Automotivo e Beleza (a descrição você escolhe). Lembre da vírgula entre as linhas.
INSERT INTO categorias (nome, descricao) VALUES
('Pet Shop', 'Ração, brinquedos e acessórios para animais');
SELECT * FROM categorias;
INSERT INTO categorias (nome, descricao) VALUES
('Pet Shop', 'Ração, brinquedos e acessórios para animais'),
('Automotivo', 'Acessórios para o carro'),
('Beleza', 'Cosméticos e cuidados pessoais');
SELECT * FROM categorias;
2. Várias linhas, com brancos
As regras de uma linha valem para cada linha da lista. Três clientes novos, um deles sem email:
INSERT INTO clientes (nome, email, cidade, estado) VALUES
('Lívia Rocha', 'livia.rocha@exemplo.com.br', 'Teresina', 'PI'),
('Joaquim Leite', NULL, 'Picos', 'PI'),
('Bianca Moraes', 'bianca.moraes@exemplo.com.br', 'Parnaíba', 'PI');
SELECT * FROM clientes WHERE id > 30;
- Todas as linhas têm quatro valores, porque a lista de colunas tem quatro. Para deixar o email do Joaquim em branco, a linha dele usa
NULLno lugar do email; não dá para simplesmente "pular" o valor. - As colunas que não estão na lista (
nascimentoevip) ficaram com NULL e com o padrão0nas três linhas.
3. Um pedido completo: duas tabelas
O cliente 27, Paulo Sérgio Andrade, que nunca tinha comprado na Aurora, fez o primeiro pedido: 2 cadernos, 5 lápis e 1 estojo, atendido pela Renata (funcionária 3). Na Aurora, um pedido mora em duas tabelas: o "cabeçalho" em pedidos e cada produto em itens_pedido (aula Tabelas que se Relacionam). Primeiro o cabeçalho, que vai ganhar o id 91; depois os três itens, que apontam para ele:
INSERT INTO pedidos (cliente_id, funcionario_id, data, frete) VALUES (27, 3, '2026-10-16', 12.5); INSERT INTO itens_pedido (pedido_id, produto_id, quantidade, preco_unitario) VALUES (91, 1, 2, 12.35), (91, 3, 5, 1.15), (91, 6, 1, 19.5); SELECT * FROM pedidos WHERE id = 91; SELECT * FROM itens_pedido WHERE pedido_id = 91;
- O primeiro
INSERTcria o pedido. Oentregue_emficou de fora, então o pedido nasce NULL nesse campo: ainda não foi entregue. - O segundo
INSERTgrava os três itens de uma vez. Cada linha diz o pedido (91), o produto (1 = caderno, 3 = lápis, 6 = estojo), a quantidade e o preço daquele dia. - Os dois
SELECTmostram o pedido e os itens. O editor mostra um quadro para cada comando, na ordem.
A ordem importa: os itens apontam para o pedido 91 (é uma chave estrangeira), então o pedido precisa existir antes. Com os itens primeiro, o SQLite do editor responderia FOREIGN KEY constraint failed.
Aqui a Ana sabe que o próximo pedido é o 91 porque o banco do editor é sempre o mesmo. Num sistema de verdade, com muita gente comprando ao mesmo tempo, ela pediria ao banco o número que ele acabou de escolher, com o RETURNING da seção 6, e usaria esse número nos itens. Os programas fazem isso com parâmetros (aula Consultas com Parâmetros).
4. Um INSERT com várias linhas × vários INSERT
As duas formas cadastram as mesmas linhas quando tudo dá certo. A diferença aparece quando alguma coisa dá errado.
Um comando: tudo ou nada
A Ana quer cadastrar duas categorias, escolhendo os números à mão, e erra o segundo: o 8 já existe (é a Jardim).
INSERT INTO categorias (id, nome) VALUES (9, 'Pet Shop'), (8, 'Automotivo');
A mensagem: UNIQUE constraint failed: categorias.id. E a Pet Shop, que estava certa, entrou? Não. Um comando SQL é atômico (de "átomo", o que não se divide): ou ele faz tudo, ou não faz nada. Como a segunda linha falhou, o SQLite desfez a primeira.
Vários comandos: cada um por si
Com dois INSERT separados, o primeiro já está gravado quando o segundo falha. No editor, a execução para no erro, então não dá para pôr um SELECT depois. No terminal, onde as mudanças ficam no arquivo, dá para ver a diferença com clareza:
$ sqlite3 loja.db SQLite version 3.50.4 2025-07-30 19:33:53 Enter ".help" for usage hints. sqlite> INSERT INTO categorias (id, nome) VALUES (9, 'Pet Shop'), (8, 'Automotivo'); Runtime error: UNIQUE constraint failed: categorias.id (19) sqlite> SELECT id, nome FROM categorias WHERE id > 7; 8|Jardim « um comando só: a Pet Shop também não entrou sqlite> INSERT INTO categorias (id, nome) VALUES (9, 'Pet Shop'); sqlite> INSERT INTO categorias (id, nome) VALUES (8, 'Automotivo'); Runtime error: UNIQUE constraint failed: categorias.id (19) sqlite> SELECT id, nome FROM categorias WHERE id > 7; 8|Jardim 9|Pet Shop « dois comandos: o primeiro ficou gravado sqlite> .quit
Qual é melhor? Depende do que você quer quando algo dá errado. Para um pedido e os seus itens, o "tudo ou nada" é o que você quer: um pedido com só metade dos itens é pior do que pedido nenhum. Para isso, junte as linhas num comando só. Quando são vários comandos que precisam dar certo juntos (como o pedido e os itens, que estão em tabelas diferentes), o SQL tem as transações, que fazem um grupo de comandos virar um "tudo ou nada": aula Transações (BEGIN e COMMIT).
| Um INSERT, várias linhas | Vários INSERT | |
|---|---|---|
| Uma linha com erro | Nenhuma linha entra | As anteriores ficam; a com erro e as seguintes não entram |
| Tabelas diferentes | Não dá: um INSERT só grava numa tabela | Dá |
| Tamanho do SQL | Curto: tabela e colunas escritas uma vez | Repete o começo em cada linha |
| Velocidade com muitas linhas | Mais rápido | Mais lento (cada comando é uma gravação; fica rápido dentro de uma transação) |
5. Vendo quantas linhas mudaram
O editor escreve "3 linhas afetadas". No terminal, o INSERT fica quieto quando dá certo; para ver a contagem, ligue o comando de ponto .changes on:
$ sqlite3 loja.db
SQLite version 3.50.4 2025-07-30 19:33:53
Enter ".help" for usage hints.
sqlite> .changes on
sqlite> INSERT INTO categorias (nome) VALUES ('Automotivo'), ('Beleza'), ('Brinquedos Educativos');
changes: 3 total_changes: 3
sqlite> .quit
changes: 3 é quanto o último comando mudou; total_changes é a soma desde que você abriu o sqlite3. Com .changes on, o terminal repete essa linha depois de todo comando, até de um SELECT (que não muda nada e mostra os mesmos números). Desligue com .changes off.
6. RETURNING: ver os ids na hora
Depois do INSERT, você quer saber que id cada linha recebeu. Em vez de um SELECT separado, o SQLite aceita RETURNING ("devolvendo") no fim do INSERT: ele devolve as colunas que você pedir, das linhas que acabaram de entrar.
INSERT INTO clientes (nome, cidade, estado) VALUES
('Lívia Rocha', 'Teresina', 'PI'),
('Joaquim Leite', 'Picos', 'PI')
RETURNING id, nome;
- RETURNING id, nome
- Vem depois da última linha de valores, antes do
;. Funciona como a lista de colunas de umSELECT:RETURNING *devolve todas.
A resposta é uma tabela com as duas linhas novas: 31 e 32. É assim que um programa descobre o número do pedido recém-criado para gravar os itens. O RETURNING também funciona com UPDATE e DELETE, e é um ótimo aliado contra acidentes: a aula O Perigo do UPDATE e DELETE sem WHERE mostra como.
Formas diferentes de inserir várias linhas
| Forma | Quando usar |
|---|---|
INSERT INTO t (…) VALUES (…), (…), (…); | Várias linhas da mesma tabela que devem entrar juntas. A forma mais comum. |
Vários INSERT seguidos | Linhas em tabelas diferentes (o pedido e os itens), ou quando cada linha pode entrar sozinha. |
Vários INSERT dentro de uma transação | Vários comandos que precisam dar certo juntos: aula Transações. |
INSERT INTO t (…) SELECT … | As linhas novas vêm de uma consulta em outra tabela: aula INSERT INTO SELECT. |
.import arquivo.csv tabela (terminal) | Centenas ou milhares de linhas vindas de uma planilha: aula Importar e Exportar CSV. |
Quando dá errado
Em todos estes casos, o comando inteiro é recusado: nenhuma das linhas entra.
INSERT INTO categorias (nome, descricao) VALUES
('Pet Shop', 'Ração, brinquedos e acessórios para animais'),
('Automotivo');
A mensagem: all VALUES must have the same number of terms ("todos os VALUES precisam ter o mesmo número de termos"). A segunda linha tem um valor só, e a primeira tem dois.
Como consertar: todas as linhas com um valor para cada coluna da lista. Para deixar a descrição em branco, use NULL: ('Automotivo', NULL).
INSERT INTO categorias (nome) VALUES
('Pet Shop')
('Automotivo');
A mensagem: near "(": syntax error. Depois de ('Pet Shop'), o SQLite esperava uma vírgula (mais uma linha) ou o fim do comando, e encontrou outro parêntese.
Como consertar: vírgula depois de cada linha, menos a última.
INSERT INTO categorias (nome) VALUES
('Pet Shop'),
('Automotivo'),
;
A mensagem: near ";": syntax error. A vírgula prometeu mais uma linha, e chegou o fim do comando. Acontece muito quando você apaga a última linha de uma lista e esquece a vírgula da penúltima.
Como consertar: a última linha termina com ) e ;, sem vírgula.
INSERT INTO itens_pedido (pedido_id, produto_id, quantidade, preco_unitario) VALUES (90, 1, 1, 12.35), (90, 1, 2, 12.35);
A mensagem: UNIQUE constraint failed: itens_pedido.pedido_id, itens_pedido.produto_id. Na tabela itens_pedido, a chave primária é o par (pedido, produto): cada produto aparece uma vez por pedido. A mensagem cita as duas colunas porque é a combinação delas que se repetiu.
Como consertar: uma linha por produto, com a quantidade total: (90, 1, 3, 12.35).
INSERT INTO categorias (nome) VALUES
('Pet Shop'),
(NULL);
A mensagem: NOT NULL constraint failed: categorias.nome. Basta uma linha quebrar a regra para o comando todo ser recusado; a Pet Shop também não entra.
Missão: este cadastro de dois clientes não roda. Leia a mensagem, conserte e rode até os dois entrarem.
INSERT INTO clientes (nome, cidade, estado) VALUES
('Lívia Rocha', 'Teresina', 'PI')
('Joaquim Leite', 'Picos', 'PI'),
;
SELECT id, nome, cidade FROM clientes WHERE id > 29;
INSERT INTO clientes (nome, cidade, estado) VALUES
('Lívia Rocha', 'Teresina', 'PI'),
('Joaquim Leite', 'Picos', 'PI');
SELECT id, nome, cidade FROM clientes WHERE id > 29;
- O
VALUES (…), (…)com várias linhas é do SQL padrão e funciona no MySQL, no PostgreSQL e no SQL Server. No SQL Server, a documentação limita umINSERT … VALUESa 1.000 linhas; para mais, use vários comandos ou importação em massa. - O SQL Server (e o MySQL e o PostgreSQL) aceitam a palavra
DEFAULTno lugar de um valor, para pedir o valor padrão daquela coluna numa linha. O SQLite não aceita:VALUES ('Lívia Rocha', DEFAULT)dánear "DEFAULT": syntax error. No SQLite, para usar o padrão, deixe a coluna fora da lista. Trecho só ilustrativo:INSERT INTO clientes (nome, vip) VALUES ('Lívia Rocha', DEFAULT), ('Joaquim Leite', 1); - O
RETURNINGexiste também no PostgreSQL. No SQL Server, o equivalente é a cláusulaOUTPUT.
Referência rápida
| Escrita | O que faz |
|---|---|
INSERT INTO t (a, b) VALUES (1, 'x'), (2, 'y'); | Duas linhas num comando |
| Vírgula entre as listas | Separa as linhas; a última não leva vírgula |
NULL numa posição | Deixa aquele campo em branco naquela linha |
| Uma linha com erro | O comando inteiro é recusado (atômico) |
… RETURNING id, nome; | Devolve as linhas que acabaram de entrar |
.changes on (terminal) | Mostra quantas linhas cada comando mudou |
| Mensagem do SQLite (editor) | Quer dizer |
|---|---|
all VALUES must have the same number of terms | Linhas com quantidades diferentes de valores |
near "(": syntax error | Faltou a vírgula entre duas linhas |
near ";": syntax error | Sobrou vírgula depois da última linha |
UNIQUE constraint failed: itens_pedido.pedido_id, itens_pedido.produto_id | O mesmo par (pedido, produto) duas vezes |
near "DEFAULT": syntax error | O SQLite não aceita DEFAULT dentro do VALUES |
Exercícios
1. A vírgula que falta
Complete com o caractere que separa as duas linhas:
2. A maior categoria é a 8. Que id ganham as linhas de INSERT INTO categorias (nome) VALUES ('A'), ('B'), ('C');?
3. Um INSERT com três linhas tem um erro na terceira. Quantas linhas entram?
4. Por que o pedido novo precisa ser inserido antes dos seus itens?
Missão final: o primeiro pedido da Tainá
Missão: a cliente 28, Tainá Soares, fez o primeiro pedido, no dia 2026-10-17, atendida pelo funcionário 2, com frete de 9.9. O cabeçalho do pedido já está escrito abaixo (ele vai ganhar o número 91). Acrescente, num único INSERT em itens_pedido, os dois itens: 1 Garrafa Térmica 1L (produto 15, a 54.3) e 2 Cafés Torrados 500g (produto 35, a 23.85).
INSERT INTO pedidos (cliente_id, funcionario_id, data, frete) VALUES (28, 2, '2026-10-17', 9.9); SELECT * FROM itens_pedido WHERE pedido_id = 91;
INSERT INTO pedidos (cliente_id, funcionario_id, data, frete) VALUES (28, 2, '2026-10-17', 9.9); INSERT INTO itens_pedido (pedido_id, produto_id, quantidade, preco_unitario) VALUES (91, 15, 1, 54.3), (91, 35, 2, 23.85); SELECT * FROM itens_pedido WHERE pedido_id = 91;
Resumo
- Várias linhas num
INSERT:VALUES (…), (…), (…);, com a tabela e as colunas escritas uma vez. - Vírgula entre as linhas, nenhuma depois da última. Todas as linhas com o mesmo número de valores (use
NULLpara o branco). - Os
idautomáticos saem em sequência, na ordem das linhas. - Um comando é atômico: se uma linha falha, nenhuma entra. Vários
INSERTseguidos não são: os anteriores ao erro ficam. - Linhas em tabelas diferentes (pedido e itens) precisam de comandos separados, na ordem certa por causa das chaves estrangeiras. Para "tudo ou nada" entre vários comandos, use uma transação.
RETURNINGdevolve as linhas que acabaram de entrar, com osidescolhidos pelo banco.