Publicado em

O que é SQL? Entendendo seu propósito, suas origens e como usá-lo

Autores

    O que é SQL? Entendendo seu propósito, suas origens e como usá-lo

    A maioria das aplicações modernas, de blogs simples a enormes redes sociais, armazena e recupera dados usando uma linguagem chamada SQL.

    Neste artigo, você aprenderá:

    • O que é SQL e por que foi criado
    • Como os bancos de dados relacionais organizam informações
    • Um exemplo concreto com duas tabelas
    • Como executar consultas SQL reais, incluindo JOINs
    • Por que SQL continua sendo o padrão depois de mais de 50 anos

    Vamos começar.

    O que é SQL?

    SQL (Structured Query Language, ou linguagem de consulta estruturada) é uma linguagem de programação declarativa criada para gerenciar bancos de dados relacionais, bancos de dados construídos em torno de tabelas, linhas e relacionamentos entre elas.

    SQL permite:

    • Criar tabelas
    • Inserir e modificar dados
    • Consultar dados usando filtros
    • Unir dados relacionados de várias tabelas
    • Impor restrições, como e-mails únicos ou chaves estrangeiras

    SQL é usado em muitos sistemas populares de bancos de dados relacionais. Veja alguns exemplos:

    • PostgreSQL: banco de dados relacional avançado de código aberto com amplo suporte aos padrões SQL.
    • MySQL: amplamente usado em aplicações web, conhecido pela velocidade e simplicidade.
    • MariaDB: versão derivada do MySQL mantida pela comunidade, com recursos aprimorados de desempenho.
    • SQLite: banco de dados leve baseado em arquivos, usado em aplicações móveis, navegadores e sistemas embarcados.
    • Microsoft SQL Server: banco de dados comercial de nível empresarial com ferramentas robustas em ambientes Windows e de nuvem.
    • Serviços SQL na nuvem totalmente gerenciados, como:
      • Amazon RDS
      • Google Cloud SQL
      • PlanetScale
        Essas plataformas (Amazon RDS, Google Cloud SQL, PlanetScale) cuidam de backups, escalabilidade, atualizações e manutenção, mantendo o poder do SQL à sua disposição.

    Por que SQL foi criado

    Na década de 1970, na IBM, os pesquisadores Edgar F. Codd e Donald Chamberlin trabalhavam em uma maneira melhor de armazenar e recuperar dados. Na época, os bancos de dados eram hierárquicos ou baseados em redes, e interagir com eles era:

    • Complexo
    • Sujeito a erros
    • Fortemente ligado ao armazenamento físico
    • Difícil de consultar de forma flexível

    Para resolver isso, eles introduziram o modelo relacional, que organiza dados em tabelas com relacionamentos bem definidos.

    SQL foi criado porque:

    1. Os desenvolvedores precisavam de uma linguagem simples e legível para consultar dados.
    2. As empresas precisavam de regras consistentes para armazenar relacionamentos.
    3. Os dados precisavam ser acessados sem conhecer os detalhes internos do armazenamento.

    O resultado?
    SQL se tornou o padrão em 1986 (ANSI SQL), e quase todos os principais bancos de dados atuais continuam sendo construídos em torno dele.

    Entendendo tabelas, colunas e relacionamentos

    Em um banco de dados relacional:

    • Uma tabela é como uma planilha (linhas + colunas)
    • Uma linha representa um único registro
    • Uma coluna representa uma propriedade
    • Uma chave primária identifica cada linha de forma única
    • Uma chave estrangeira liga uma tabela a outra

    Vamos analisar um exemplo simples com usuários e pedidos.

    Exemplo de esquema: usuários e pedidos

    Tabela 1: users

    Tabela 2: orders

    iduser_idproductprice
    11Coffee Mug12.99
    21Hoodie39.99
    32Stickers4.99

    Relacionamento

    • Cada pedido pertence a um usuário (orders.user_id → users.id).
    • Um usuário pode ter vários pedidos (relacionamento um para muitos).

    Criando as tabelas (exemplo SQL)

    CREATE TABLE users (
      id SERIAL PRIMARY KEY,
      name TEXT NOT NULL,
      email TEXT UNIQUE NOT NULL
    );
    
    CREATE TABLE orders (
      id SERIAL PRIMARY KEY,
      user_id INTEGER NOT NULL REFERENCES users(id),
      product TEXT NOT NULL,
      price NUMERIC(10,2) NOT NULL
    );
    

    Isso cria duas tabelas relacionais fortemente tipadas com uma restrição de chave estrangeira. Mas o que significa "fortemente tipadas"? Em bancos de dados SQL como PostgreSQL e MySQL, cada coluna tem um tipo de dado estrito:

    • Strings precisam ser strings
    • Números precisam ser números
    • Datas precisam ser datas
    • Tipos incorretos causam erros

    Isso evita erros como:

    • Salvar "vinte dólares" em uma coluna numérica
    • Salvar 123 em um campo de e-mail
    • Salvar datas inválidas

    SQLite é diferente: ele é flexível quanto aos tipos (“tipagem dinâmica”), o que significa que impõe os tipos de forma menos estrita. PostgreSQL é o mais fortemente tipado entre os principais bancos de dados.

    Criação das tabelas explicada linha por linha

    Tabela 1: users

    CREATE TABLE users (
    

    Explicação: Isso inicia a criação de uma nova tabela chamada users.

    • Funciona em: PostgreSQL, MySQL, MariaDB, SQLite, SQL Server
    • “Tabela” = conjunto estruturado de linhas e colunas no banco de dados.
      id SERIAL PRIMARY KEY,
    

    Explicação:

    • id → o nome da coluna.

    • SERIAL → inteiro com incremento automático. Específico do PostgreSQL.

      • Equivalentes em outros mecanismos:

        • MySQL: INT AUTO_INCREMENT
        • SQLite: INTEGER PRIMARY KEY AUTOINCREMENT
    • PRIMARY KEY → garante unicidade e identifica cada linha.

      name TEXT NOT NULL,
    

    Explicação:

    • name → coluna para armazenar texto.
    • TEXT → texto de comprimento variável (string).
    • NOT NULL → não pode ficar vazio.

    Funciona em: PostgreSQL, MySQL, MariaDB, SQLite (mas SQL Server usa VARCHAR(MAX) em vez de TEXT).

      email TEXT UNIQUE NOT NULL
    

    Explicação:

    • email → armazena o endereço de e-mail do usuário.
    • TEXT → tipo de texto.
    • UNIQUE → impede e-mails duplicados.
    • NOT NULL → campo obrigatório.

    Funciona em: PostgreSQL, MySQL, MariaDB, SQLite (considerando diferenças internas do tipo TEXT).

    );
    

    Explicação: Encerra a definição da tabela.

    Tabela 2: orders

    CREATE TABLE orders (
    

    Inicia uma nova tabela chamada orders. Funciona em todos os principais mecanismos.

      id SERIAL PRIMARY KEY,
    

    Mesmo significado que na tabela users:

    • Chave primária com incremento automático
    • Tipo específico do PostgreSQL (SERIAL)
    • MySQL usa INT AUTO_INCREMENT
    • SQLite usa INTEGER PRIMARY KEY AUTOINCREMENT
      user_id INTEGER NOT NULL REFERENCES users(id),
    

    Explicação:

    • user_id → um número que liga o pedido a uma linha em users.
    • INTEGER → número inteiro.
    • NOT NULL → todo pedido precisa pertencer a um usuário.
    • REFERENCES users(id) → restrição de chave estrangeira.

    Significado de chave estrangeira:

    • Garante que user_id corresponda a um id real na tabela users.
    • Se um usuário for excluído, o banco de dados fará cumprir o relacionamento de acordo com as regras de chave estrangeira.

    Compatibilidade dos mecanismos:

    • PostgreSQL: suporte completo.
    • MySQL/MariaDB: funciona, mas exige o mecanismo de armazenamento InnoDB.
    • SQLite: compatível somente quando o suporte a chaves estrangeiras está ativado (PRAGMA foreign_keys = ON;).
      product TEXT NOT NULL,
    

    Armazena o nome do produto comprado.

    • Tipo TEXT → funciona de forma diferente entre mecanismos, mas sempre armazena strings.
    • Obrigatório (NOT NULL).
      price NUMERIC(10,2) NOT NULL
    

    Explicação:

    • price → o custo do produto.

    • NUMERIC(10,2) → número de precisão fixa com:

      • até 10 dígitos no total
      • 2 dígitos após o separador decimal

    Isso é ideal para valores monetários porque evita erros de ponto flutuante.

    Compatibilidade dos mecanismos:

    • PostgreSQL: suporte perfeito.
    • MySQL: DECIMAL(10,2) é equivalente.
    • SQLite: aceita, mas armazena internamente como TEXT, INT ou REAL (tipagem dinâmica).
    );
    

    Encerra a tabela.

    Inserindo os dados do exemplo

    Agora que nossas tabelas foram criadas, vamos inserir os dados de exemplo apresentados anteriormente. Não forneceremos manualmente nenhum valor de id, o banco de dados os gerará automaticamente usando o tipo SERIAL (incremento automático).

    Inserindo os dados de users

    INSERT INTO users (name, email)
    VALUES
      ('Alice', 'alice@example.com'),
      ('Bob', 'bob@example.com'),
      ('Caroline', 'caroline@example.com');
    

    Inserção em users explicada linha por linha

    INSERT INTO users (name, email)
    
    • INSERT INTO users Estamos adicionando novas linhas à tabela users.

    • (name, email) Fornecemos apenas name e email, não id.

      Por que não fornecer o ID? Como a coluna id usa um tipo serial/com incremento automático (SERIAL do PostgreSQL), o banco de dados criará os IDs automaticamente:

    • Primeiro usuário → id = 1

    • Segundo usuário → id = 2

    • Terceiro usuário → id = 3

    Isso é previsível no PostgreSQL, MySQL, MariaDB e SQLite (usando seus respectivos mecanismos de incremento automático).

    VALUES
      ('Alice', 'alice@example.com'),
      ('Bob', 'bob@example.com'),
      ('Caroline', 'caroline@example.com');
    
    • VALUES introduz uma lista de linhas a inserir.
    • Cada grupo ( ... ) corresponde a uma nova linha.
    • O banco de dados atribuirá IDs automaticamente na ordem em que essas linhas aparecem.

    Tabela users resultante após a inserção:

    Embora não tenhamos escrito nenhum ID manualmente, o mecanismo de incremento automático garante essa ordem.

    Inserindo os dados de orders

    Para inserir orders, precisamos, sim, fornecer user_id, porque um pedido deve pertencer a um usuário específico.

    Embora não tenhamos definido os IDs dos usuários manualmente, ainda sabemos quais são:

    • Alice → ID 1
    • Bob → ID 2
    • Caroline → ID 3

    Eles foram gerados automaticamente na ordem de inserção, e o incremento automático sempre atribui IDs sequencialmente, a menos que linhas sejam excluídas.

    Aqui está a inserção:

    INSERT INTO orders (user_id, product, price)
    VALUES
      (1, 'Coffee Mug', 12.99),
      (1, 'Hoodie', 39.99),
      (2, 'Stickers', 4.99);
    

    Inserção em orders explicada linha por linha

    INSERT INTO orders (user_id, product, price)
    
    • Estamos inserindo novas linhas na tabela orders.

    • Especificamos:

      • user_id → a qual usuário o pedido pertence
      • product → nome
      • price → custo

    Observe novamente: não fornecemos id (orders.id), porque ele é incrementado automaticamente.

    VALUES
      (1, 'Coffee Mug', 12.99),
      (1, 'Hoodie', 39.99),
      (2, 'Stickers', 4.99);
    
    • (1, 'Coffee Mug', 12.99)

      • Este pedido pertence ao usuário 1 (Alice).
    • (1, 'Hoodie', 39.99)

      • Outro pedido para Alice.
    • (2, 'Stickers', 4.99)

      • Pedido para o usuário 2 (Bob).

      Como sabemos qual usuário tem qual ID? Porque:

    1. Os usuários foram inseridos primeiro.

    2. IDs com incremento automático sempre atribuem inteiros sequenciais.

    3. Portanto:

      • Primeiro usuário inserido → ID 1
      • Segundo usuário inserido → ID 2
      • Terceiro usuário inserido → ID 3

    Isso torna totalmente previsível referenciar usuários pelo ID.

    Tabelas finais resultantes

    users

    orders

    iduser_idproductprice
    11Coffee Mug12.99
    21Hoodie39.99
    32Stickers4.99

    Consultando dados com SQL

    Aqui estão consultas SQL que podemos executar nas duas tabelas definidas anteriormente.

    1. Selecionar todos os usuários

    SELECT * FROM users;
    

    Resultado:

    2. Selecionar usuários com um filtro

    SELECT * FROM users
    WHERE email LIKE '%alice@example.com';
    

    O que isso significa:

    • LIKE é um operador SQL usado para comparar texto usando padrões.
    • % é um curinga que corresponde a qualquer quantidade de caracteres, incluindo zero caracteres.

    Portanto:

    • '%alice@example.com' significa: “Corresponder a qualquer texto que termine com alice@example.com.”

    Isso retornará a linha em que o e-mail do usuário é exatamente:

    alice@example.com
    

    mas também corresponderia a valores como:

    • test-alice@example.com
    • hello.alice@example.com
    • anythingalice@example.com

    se existissem.

    Resultado:

    idnameemail
    1Alicealice@example.com

    Esta consulta retorna apenas o usuário cujo e-mail corresponde ao filtro.

    3. Inserir um novo pedido

    INSERT INTO orders (user_id, product, price)
    VALUES (2, 'T-Shirt', 29.99);
    

    Tabela orders resultante após a inserção:

    iduser_idproductprice
    11Coffee Mug12.99
    21Hoodie39.99
    32Stickers4.99
    42T-Shirt29.99
    • A nova linha tem id = 4, gerado automaticamente.
    • user_id = 2 vincula esse pedido a Bob.

    JOINs: uma parte poderosa do SQL

    Os JOINs permitem consultar dados relacionados entre várias tabelas.

    Vamos explorá-los usando nossas tabelas users e orders.

    Exemplo 1: INNER JOIN (o mais comum)

    Obtenha cada pedido com o usuário que o fez.

    SELECT users.name, orders.product, orders.price
    FROM orders
    INNER JOIN users
      ON orders.user_id = users.id;
    

    Resultado:

    nameproductprice
    AliceCoffee Mug12.99
    AliceHoodie39.99
    BobStickers4.99
    BobT-Shirt29.99

    Bob agora aparece duas vezes, por causa do novo pedido inserido anteriormente:

    INSERT INTO orders (user_id, product, price)
    VALUES (2, 'T-Shirt', 29.99);
    

    Exemplo 2: LEFT JOIN

    Obtenha todos os usuários, mesmo aqueles sem pedidos.

    SELECT users.name, orders.product
    FROM users
    LEFT JOIN orders
      ON users.id = orders.user_id;
    

    Resultado:

    nameproduct
    AliceCoffee Mug
    AliceHoodie
    BobStickers
    BobT-Shirt
    Carolinenull

    Caroline aparece com null porque não tem pedidos.

    Exemplo 3: consulta de agregação

    Total gasto por cada usuário:

    SELECT users.name, SUM(orders.price) AS total_spent
    FROM users
    LEFT JOIN orders ON users.id = orders.user_id
    GROUP BY users.name;
    

    Resultado:

    nametotal_spent
    Alice52.98
    Bob34.98
    Carolinenull

    Explicação

    • Alice: 12.99 + 39.99 = 52.98
    • Bob: 4.99 + 29.99 = 34.98
    • Caroline: sem pedidos → NULL

    Onde aprender mais

    Considerações finais

    SQL existe há mais de 50 anos, mas ainda sustenta as aplicações mais importantes do mundo, porque é:

    • Declarativo
    • Consistente
    • Previsível
    • Escalável
    • Apoiado por décadas de pesquisa

    Quando você entende como tabelas e junções funcionam, SQL se torna uma das ferramentas mais poderosas do seu conjunto de ferramentas de programação.