SQL nas Linguagens

O SQL que você aprendeu é a língua do banco. Mas um site ou um aplicativo não é feito só de SQL: ele é feito numa linguagem de programação — Python, PHP, JavaScript, Java, C# — e essa linguagem fala com o banco para pedir e gravar dados. A boa notícia é que o SQL é quase o mesmo em todas; o que muda é só a "cola" em volta: como conectar, como mandar a consulta e como ler o resultado. Nesta aula você vê a mesma consulta na Loja Aurora saindo de cinco linguagens, e descobre que o passo a passo é sempre o mesmo.

Você vai aprender
  • O que é um driver (a biblioteca que liga a linguagem ao banco).
  • O fio condutor comum: conectar → preparar o SQL → ligar os parâmetros → ler as linhas → fechar.
  • A mesma consulta em Python, PHP, Node, Java e C#.
  • A marca de espaço reservado de cada uma (?, :nome, $1…).
  • Por onde começar em cada linguagem, com os guias de PHP, Java, C# e C.

Só o exemplo em Python roda aqui dentro (na máquina da Ana); os outros são ilustrativos, para você ver a forma. Todos usam parâmetros (aula Consultas com Parâmetros): nunca cole dados no SQL à mão, em linguagem nenhuma.


A ideia: o banco e o tradutor

O banco de dados é como um almoxarife que só entende SQL. A sua linguagem de programação é outra pessoa, que fala outra língua. Entre as duas fica um tradutor: uma biblioteca que recebe o SQL do seu programa, entrega ao banco e traz a resposta de volta num formato que o seu programa entende (uma lista, um objeto). Esse tradutor tem nome: é o driver (ou "conector") do banco.

Cada linguagem tem o seu driver para o SQLite (e outros para o MySQL, o PostgreSQL…). Troque o banco e você troca o driver; o seu SQL continua quase igual. É por isso que aprender SQL vale para qualquer linguagem.


O fio condutor (igual em todas)

Não importa a linguagem, rodar uma consulta tem sempre os mesmos passos:

  1. Conectar ao banco (abrir o arquivo loja.db, ou ligar no servidor).
  2. Preparar o SQL, com espaços reservados no lugar dos valores.
  3. Ligar os valores aos espaços reservados (o bind).
  4. Executar e ler as linhas que voltaram.
  5. Fechar a conexão no fim.

A consulta que vamos usar em todas é a mesma: os produtos mais baratos que um certo preço. Em SQL puro, no editor, ela é esta (aqui com o preço 5 já no lugar):

SELECT nome, preco FROM produtos WHERE preco < 5 ORDER BY preco;

Nos programas, o 5 vira um parâmetro. Vamos ver linguagem por linguagem.


1. Python — módulo sqlite3

O Python já vem com o driver do SQLite embutido: o módulo sqlite3. É o único exemplo que roda aqui de verdade, na máquina da Ana:

import sqlite3

con = sqlite3.connect("loja.db")        # 1. conectar
con.row_factory = sqlite3.Row            # ler colunas pelo nome

sql = "SELECT nome, preco FROM produtos WHERE preco < ? ORDER BY preco LIMIT 3"
for p in con.execute(sql, (5,)):         # 2-4. preparar, ligar o 5, ler
    print(f"{p['nome']:<28} R$ {p['preco']:.2f}")

con.close()                              # 5. fechar
$ python3 lista.py
Lápis Grafite HB             R$ 1.15
Caneta Esferográfica Azul    R$ 2.79
Marca-texto Amarelo          R$ 4.90
sqlite3.connect("loja.db")
Abre (ou cria) o arquivo do banco e devolve a conexão.
WHERE preco < ?
O espaço reservado posicional (aula Consultas com Parâmetros).
con.execute(sql, (5,))
Prepara, liga o 5 ao ?, executa e devolve as linhas, que o for percorre.
con.close()
Fecha a conexão. Sempre feche, ou use um with.

2. PHP — PDO

No PHP, o jeito moderno é o PDO (PHP Data Objects), um driver único que fala com SQLite, MySQL, PostgreSQL e outros trocando só o começo da conexão (o DSN). O espaço reservado é o mesmo ? (ou o :nome):

<?php
$pdo = new PDO("sqlite:loja.db");                 // 1. conectar
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$sql = "SELECT nome, preco FROM produtos WHERE preco < ? ORDER BY preco LIMIT 3";
$stmt = $pdo->prepare($sql);                       // 2. preparar
$stmt->execute([5]);                               // 3-4. ligar o 5 e executar

foreach ($stmt as $p) {                           // ler as linhas
    printf("%-28s R$ %.2f\n", $p["nome"], $p["preco"]);
}

(Ilustrativo: não roda no editor.) O prepare cria o comando preparado e o execute([5]) liga o valor ao ?. Para começar em PHP, veja o Guia PHP do mesmo autor: Conectar com PDO e Prepared Statements e SQL Injection.


3. Node.js — node:sqlite

O Node.js ganhou um driver de SQLite embutido, o módulo node:sqlite, a partir do Node 22.5 (ainda marcado como experimental). A forma lembra a dos outros: preparar e ligar o valor.

import { DatabaseSync } from "node:sqlite";

const db = new DatabaseSync("loja.db");           // 1. conectar
const stmt = db.prepare(                          // 2. preparar
  "SELECT nome, preco FROM produtos WHERE preco < ? ORDER BY preco LIMIT 3"
);
for (const p of stmt.all(5)) {                    // 3-4. ligar o 5, ler
  console.log(p.nome, p.preco);
}
db.close();                                       // 5. fechar
Node mais antigo

O node:sqlite só existe do Node 22.5 em diante. Em versões anteriores (como o Node 20), ele não está presente: require("node:sqlite") dá o erro ERR_UNKNOWN_BUILTIN_MODULE. Nesses casos, o costume é instalar um pacote da comunidade, como o better-sqlite3, que tem uma forma muito parecida. Por isso este exemplo fica ilustrativo.


4. Java — JDBC

Em Java, o padrão para falar com bancos é o JDBC (Java Database Connectivity). Você usa um PreparedStatement, liga os valores por posição (começando em 1, não em 0) e lê um ResultSet:

import java.sql.*;

public class Lista {
    public static void main(String[] args) throws SQLException {
        String url = "jdbc:sqlite:loja.db";                 // 1. conectar
        String sql = "SELECT nome, preco FROM produtos WHERE preco < ? ORDER BY preco LIMIT 3";
        try (Connection con = DriverManager.getConnection(url);
             PreparedStatement ps = con.prepareStatement(sql)) {   // 2. preparar
            ps.setDouble(1, 5);                             // 3. ligar o 5 (posição 1)
            try (ResultSet rs = ps.executeQuery()) {        // 4. executar e ler
                while (rs.next()) {
                    System.out.printf("%-28s R$ %.2f%n",
                        rs.getString("nome"), rs.getDouble("preco"));
                }
            }
        }                                                   // 5. o try fecha tudo
    }
}

(Ilustrativo.) O try (…) com os recursos entre parênteses fecha a conexão sozinho no fim. Para começar em Java, veja o Guia Java do mesmo autor.


5. C# — ADO.NET (Microsoft.Data.Sqlite)

No C# (.NET), o padrão é o ADO.NET. Com o pacote Microsoft.Data.Sqlite, você cria um comando, adiciona os parâmetros (aqui nomeados, com $) e lê com um DataReader:

using Microsoft.Data.Sqlite;

using var con = new SqliteConnection("Data Source=loja.db");  // 1. conectar
con.Open();

var cmd = con.CreateCommand();                                // 2. preparar
cmd.CommandText = "SELECT nome, preco FROM produtos WHERE preco < $preco ORDER BY preco LIMIT 3";
cmd.Parameters.AddWithValue("$preco", 5);                     // 3. ligar o 5

using var reader = cmd.ExecuteReader();                       // 4. executar e ler
while (reader.Read())
{
    Console.WriteLine($"{reader.GetString(0),-28} R$ {reader.GetDouble(1):F2}");
}

(Ilustrativo.) O $preco é um espaço reservado nomeado (o SQLite aceita :nome, @nome e $nome). Para começar em C#, veja o Guia C# do mesmo autor.

E em C puro?

O SQLite é escrito em C, e dá para usá-lo direto, sem driver, com funções como sqlite3_open, sqlite3_prepare_v2, sqlite3_bind_* e sqlite3_step. É mais trabalhoso (é a "cozinha" que as outras linguagens escondem), mas é a base de todas elas. O Guia C do mesmo autor ajuda com a linguagem.


Lado a lado

LinguagemDriver / bibliotecaConectar (SQLite)Espaço reservado
Pythonsqlite3 (embutido)sqlite3.connect("loja.db")? e :nome
PHPPDOnew PDO("sqlite:loja.db")? e :nome
Node.jsnode:sqlite (Node 22.5+)new DatabaseSync("loja.db")? e :nome
JavaJDBCjdbc:sqlite:loja.db? (posição começa em 1)
C#ADO.NET (Microsoft.Data.Sqlite)"Data Source=loja.db"$nome, :nome, @nome

Repare: fora o nome das funções, o SQL é idêntico e o passo a passo é o mesmo. Quem aprende SQL aprende a parte que não muda.

Nos bancos com servidor

Para MySQL, PostgreSQL e SQL Server, você troca o driver e o endereço da conexão (servidor, usuário, senha), mas o SQL e os passos continuam. O PostgreSQL usa a marca $1, $2 nos parâmetros; o MySQL e o SQL Server usam ? ou nomes, conforme o driver. Nesses bancos entram também os usuários e permissões: o programa conecta com um usuário que só pode o necessário.


Quando dá errado

Erro do banco: cada linguagem embrulha do seu jeito

Escreva o nome da tabela errado — produto em vez de produtos — e o banco devolve sempre a mesma mensagem crua. Veja no editor:

SELECT * FROM produto;

A mensagem do SQLite é no such table: produto ("não existe a tabela produto"). Num programa, essa mesma mensagem chega dentro de um erro da linguagem. Em Python, por exemplo:

$ python3 -c "import sqlite3; sqlite3.connect('loja.db').execute('SELECT * FROM produto')"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
    import sqlite3; sqlite3.connect('loja.db').execute('SELECT * FROM produto')
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
sqlite3.OperationalError: no such table: produto

Em PHP seria uma PDOException, em Java uma SQLException, em C# uma SqliteException — mas o texto do banco, no such table: produto, é o mesmo em todas. Por isso vale aprender a ler as mensagens do SQLite: elas aparecem igual em qualquer linguagem.

Colar dados no SQL em vez de usar parâmetro

Em qualquer dessas linguagens, montar o SQL juntando texto ("… WHERE nome = '" + digitado + "'") reabre a porta do SQL Injection e quebra com nomes que têm apóstrofo, como Loja D'Água. A regra vale para todas: valor vindo de fora entra por parâmetro, nunca colado.


Referência rápida

PassoO que é
DriverA biblioteca que liga a linguagem ao banco.
ConectarAbrir o arquivo (SQLite) ou ligar no servidor.
PrepararMontar o SQL com espaços reservados.
Ligar (bind)Encaixar os valores nos espaços reservados.
LerPercorrer as linhas devolvidas.
FecharEncerrar a conexão no fim.

Exercícios

1. O que é um "driver" de banco de dados?

2. Trocando de linguagem (de Python para Java, por exemplo), o que mais muda?

3. Em todas as linguagens desta aula, como o valor de busca deve entrar na consulta?

Experimente: a consulta que todas rodam

Missão: escreva, em SQL puro, a consulta que todos os programas rodaram: os produtos com preco menor que 5, ordenados por preco. (É o que sai quando o parâmetro recebe o valor 5.) Devem vir 3 produtos.

SELECT nome, preco FROM produtos;
SELECT nome, preco FROM produtos WHERE preco < 5 ORDER BY preco;

Missão final

Missão: provoque, de propósito, o erro que todas as linguagens embrulham. Troque a consulta para ler de uma tabela que não existe (por exemplo, cliente, no singular) e veja a mensagem do SQLite. (Esta caixa espera um erro.)

SELECT * FROM clientes LIMIT 1;
SELECT * FROM cliente LIMIT 1;

Resumo

  • Um programa fala com o banco por um driver: a biblioteca que traduz entre a linguagem e o SQL.
  • O passo a passo é sempre o mesmo: conectar → preparar → ligar os parâmetros → ler → fechar.
  • O SQL quase não muda de uma linguagem para outra; muda a "cola" em volta e a marca de espaço reservado (?, :nome, $1…).
  • Em todas, os valores entram por parâmetro (aula Consultas com Parâmetros), nunca colados — senão volta o SQL Injection.
  • O erro do banco (como no such table) é o mesmo texto em qualquer linguagem, só embrulhado num erro diferente.
  • Para aprofundar em cada linguagem: os guias de PHP, Java, C# e C do mesmo autor.