Um projeto SQLite


SQLite

SQLite é um mecanismo de banco de dados relacional de código aberto, escrito em C por D. Richard Hipp, em 2000. Ele não necessita da instalação de um servidor para o seu funcionamento, como ocorre com a maioria dos demais bancos de dados, acessando diretamente um arquivo em disco. O estado completo de uma base de dados fica armazenado nesse arquivo, geralmente com extensão arquivo.db, .sqlite ou .db3. que pode ser utilizado diretamente em diversas plataformas. Ele não é um aplicativo independente e sim uma biblioteca que é incorporada junto com os aplicativos, sendo o mecanismo de banco de dados mais usado, servindo para armazenamento e manipulação de dados em navegadores da Web, sistemas operacionais, telefones celulares e outros sistemas integrados.

O SQLite usa uma sintaxe de SQL parecida com a do PostgreSQL, mas não impõe a verificação de tipo, tornando possível, por exemplo, se inserir uma string em uma coluna definida como inteiro. Como em outras versões de bancos de dados relacionais, o SQLite armazena dados em tabelas que podem conter campos de vários tipos de dados, como texto ou números inteiros. Essas tabelas podem ser acessadas (construídas, modificadas e consultadas) por meio de consultas SQL que realizam operações “CRUD” (criar, ler, atualizar, excluir). Um banco de dados SQLite pode ser criptografado usando o SQLite Encryption Extension (SEE) ou outra tecnologia, de forma a proteger dados de extrações não autorizadas.

Diversos Sistemas Gerenciadores de Bancos de Dados, SGBD, (em inglês RDBMS, Relational Database Management System) estão disponíveis para o SQLite, entre eles o SQLiteStudio, DB Browser, DBeaver. Também existem diversos plugins para navegadores e IDEs (como o VSCode).

SQLite no VSCode

O VSCode possui um plugin muito interessante para auxiliar no desenvolvimento com SQLite. Para usá-lo instale o plugin (extension) vscode-sqlite. Os seguintes recursos ficam disponíveis:

  • consulta a bancos de dados SQLite, visualização de tabelas, exportações de resultados para json, csv e html.
  • uso da barra lateral (explorer) para listar bancos de dados, tabelas, colunas e views.
  • preenchimento automático de palavras-chave, nomes de tabelas e colunas. Para isso deve-se vincular um banco de dados a um documento SQL, usando o comando: USE DATABASE.
  • emissão de mensagens de erros amigáveis apontando o local do erro.

Existem comandos para criar novos arquivos sqlite, novas consultas, editar e executar scripts de consulta, visualização rápida de BD, fechar e removar um BD, atualizar, exibir resultados de consultas.

Para iniciar o uso crie um documento, por exemplo meuBD.db. Clique nele (com botão direito) e gere um nova consulta. O arquivo é transformado automaticamente em um banco do SQLite. Também clicando com botão direito selecione “Open Database” para visualizar as tabelas e ter acesso a vários outros comandos. Consultas digitadas no editor podem ser executadas com as opções “Run Query” ou “Run Selected Query”.

Comandos SQLite

Existem diversas maneiras de inserir comandos SQL em um banco SQLite. Em um gerenciador de bancos de dados podemos abrir uma área de inserção de consultas e digitar, interativamente esses comandos. Alternativamente, se temos uma sequência de consultas válidas gravadas no arquivo consultas.sqljunto podemos executar no prompt de comando:

cat consultas.sql | sqlite3 test1.db
# ou
sqlite3 test2.db ".read consultas.sql"
# ou
sqlite3 test3.db < definicoes.sql

# no windows
sqlite3.exe test4.db ".read definicoes.sql"

Esses comandos produzem respectivamente os arquivos test_n.db após as execuções das consultas em definicoes.sql.

Projeto: vendas

Como uma ilustração de uso do SQLite vamos construir uma banco de dados para controle de uma loja, com tabelas para seus clientes, funcionários, fornecedores e itens vendidos. Todas as consultas SQL podem estar em um único arquivo .sql, mas nós as listaremos por partes para comentar os seus efeitos.

Consultas de definição das tabelas

O comando CREATE TABLE serve para inserir uma nova tabela no banco de dados. IF NOT EXISTS instrui a consulta a ser executada somente se a tabela ainda não foi criada. As definições de campo seguem dentro dos parênteses. O campo id INTEGER PRIMARY KEY AUTOINCREMENT é um índice inteiro, usado como chave primária e de autoincremento. Campos definidos como NOT NULL não podem ser deixados nulos.

Primeiro criamos uma tabela no prompt de comando:

>> sqlite3 vendas.db

Em seguida criamos as tabelas. Para usar esse arquivo podemos usar os comandos listados acima ou abrir um gerenciador de bancos de dados e inserir as consultas nele. O uso do VSCode ou diretamente no código com o sqlite3 do python são exemplos desse uso.

-- criando a tabela clientes
CREATE TABLE IF NOT EXISTS clientes (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
   nome VARCHAR(100) NOT NULL,
   sobrenome TEXT NOT NULL,
   email VARCHAR(100) NOT NULL,
   cidade TEXT NOT NULL,
   estado VARCHAR(2) NOT NULL,
   cep INTEGER NOT NULL
);

-- criando a tabela de funcionarios
CREATE TABLE IF NOT EXISTS funcionarios (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
   nome VARCHAR(100) NOT NULL,
   sobrenome TEXT NOT NULL,
   email TEXT NOT NULL,
   senha VARCHAR(20) NOT NULL,
   cargo VARCHAR(133),
   endereco TEXT NOT NULL,
   cidade VARCHAR(150) NOT NULL,
   estado VARCHAR(2) NOT NULL,
   cep INTEGER NOT NULL
);

-- Criando a tabela fornecedores
CREATE TABLE IF NOT EXISTS fornecedores (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
   nome VARCHAR(100) NOT NULL,
   cnpj VARCHAR(100) NOT NULL,
   email VARCHAR(100) NOT NULL,
   endereco TEXT NOT NULL,
   cidade VARCHAR(150) NOT NULL,
   estado VARCHAR(2) NOT NULL,
   cep INTEGER NOT NULL
);

Após a execução desses consultas temos as tabelas clientes, funcionarios e fornecedores. Digamos que queremos alterar uma tabela para acrescentar, renomear ou excluir algum campo. ALTER TABLE é a consulta usada.

-- para alterar uma tabela (alterando clientes)
ALTER TABLE clientes ADD data_aniversario DATE;
ALTER TABLE clientes ADD endereco TEXT NOT NULL;

-- para renomear um campo
ALTER TABLE clientes RENAME data_aniversario TO aniversario;
-- para excluir um campo
ALTER TABLE clientes DROP COLUMN aniversario;

Primeiro inserimos data_aniversario e endereco, depois renomeamos data_aniversario para aniversario, depois excluímos o campo aniversario. Da mesma forma tabelas inteiras podem ser excluídas com DROP TABLE.

-- criar uma tabela para ser apagada depois
CREATE TABLE IF NOT EXISTS usuarios (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
   nome VARCHAR(100) NOT NULL,
   codigo INTEGER NOT NULL
);
-- excluir a tabela recém criada
DROP TABLE usuarios;

Operações de apagamento devem ser executadas com cuidado pois os dados não podem ser recuperados!

Observe que no modelo adotado acima os cargos dos funcionários são inseridos como textos que devem ser repetidos para diversos funcionários com o mesmo cargo. Outra possibilidade consiste em ter uma tabela com os cargos em separado, referenciados na tabela funcionarios por meio de um id. Vamos fazer essas alterações.

-- criar a tabela cargos   
CREATE TABLE IF NOT EXISTS cargos (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
   descricao VARCHAR(200) NOT NULL
);   
-- exclui a coluna cargo de funcionarios   
ALTER TABLE funcionarios DROP COLUMN cargo;
-- insere cargo_id 
ALTER TABLE funcionarios ADD COLUMN cargo_id INTEGER REFERENCES cargos(id);

O último comando falaria se não existisse a tabela referenciada cargos. O campo cargo_id é uma chave estrangeira (foreign key), ligado à tabela cargos, pelo seu campo id. Vamos aplicar o mesmo conceito na criação de chaves estrangeiras para produtos vendidos e vendas.

-- tabela produtos
CREATE TABLE IF NOT EXISTS produtos (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
   fornecedor_id INTEGER,
   descricao VARCHAR(100),
   preco DECIMAL(10,2),

   FOREIGN KEY (fornecedor_id) REFERENCES fornecedores (id)
);

-- tabela vendas
CREATE TABLE IF NOT EXISTS vendas (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
   cliente_id INTEGER NOT NULL,
   funcionario_id INTEGER NOT NULL,
   data_venda DATETIME NOT NULL,
   total DECIMAL (10,2) NOT NULL,
   descricao TEXT,

   FOREIGN KEY (cliente_id) REFERENCES clientes (id),
   FOREIGN KEY (funcionario_id) REFERENCES funcionarios (id)
);

-- tabela itens_vendas
CREATE TABLE IF NOT EXISTS itens_vendas (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
   venda_id INTEGER NOT NULL,
   produto_id INTEGER NOT NULL,
   quantidade INTEGER NOT NULL,
   subtotal DECIMAL (10,2) NOT NULL,

   FOREIGN KEY (venda_id) REFERENCES vendas (id),
   FOREIGN KEY (produto_id) REFERENCES produto (id)
);

Os relacionamentos entre as tabelas expressam o fato de que um funcionário pode fazer várias vendas, cada venda pode conter vários ítens de produtos, um único fornecedor pode ser responsável por vários produtos e mais de um funcionário podem ter o mesmo cargo.

Vemos que as, após todos esses passos, as tabelas possuem os seguintes relacionamentos:

Consultas de inserção e leitura de dados

Uma vez definidas as tabelas passamos a inserir dados. Faremos também consultas para verificar os dados inseridos. Dados são inseridos com INSERT INTO. O nome da tabela e os campos são fornecidos e os valores a inserir. SELECT * significa “selecione todos os campos”.

INSERT INTO clientes (nome, sobrenome, email, cidade, estado, cep, endereco)
VALUES
('Caio', 'Zuretta', 'cz@hotmail.com', 'Seattle', 'WA', 123456789,'23rd Street AWE');
SELECT * FROM clientes;

id  nome  sobrenome   email            cidade   estado cep         endereco
1   Caio  Zuretta     cz@hotmail.com   Seattle  WA     123456789   23rd Street AWE

-- inserindo outro cliente
INSERT INTO clientes (nome, sobrenome, email, endereco, cidade, estado, cep )
    VALUES ('Polka', 'Brita', 'pbrita@gmail.com', 'Av. Contorno 432', 'Belo Horizonte', 'MG', 30876786);

SELECT id, nome, sobrenome FROM clientes;
id  nome   sobrenome
1   Caio   Zuretta
2   Polka  Brita

-- várias linhas podem ser inseridas na mesma consulta
INSERT INTO clientes (nome, sobrenome, email, endereco, cidade, estado, cep)
    VALUES
 ('Antonio', 'Tony', 'tmatador@gigamail.com.br', 'R. Pedro II, 34', 'Rio de Janeiro', 'RJ', 21654897),
 ('Martha', 'Maertis', 'marthis@onlymail.com', 'R. Joinha, 654', 'Goiania', 'GO', 41546546),
 ('Orlando', 'Orlandis', 'orlas@gmail.com', 'Av, Só que Não, 34', 'Itabira', 'MG', 35654654),
 ('Mirtes', 'Mello', 'mellom@gmail.com', 'SQL 123', 'Brasília', 'DF', 145428214);

-- uma operação sobre todos as linhas de um campo UPDATE clientes
SET email = UPPER(email);
SELECT email FROM clientes WHERE nome="Mirtes";
email
MELLOM@GMAIL.COM

-- vamos retornar as minúsculas no email
UPDATE clientes SET email = LOWER(email);
SELECT email FROM clientes WHERE nome="Mirtes";
email
mellom@gmail.com

-- inserindo dados na tabela funcionarios
INSERT INTO funcionarios (nome, sobrenome, email, senha, cargo_id, endereco, cidade, estado, cep)
VALUES
('Pedro', 'Altusser', 'pedroalt@email.com', '123456', 1, 'R. João Paulo, 534', 'Rio de Janeiro', 'RJ', 21654897),
('Levindo', 'Lopes', 'lopest@gmail.com', '234456', 1, 'R. Paulo II, 534', 'Ardósias', 'RJ', 21115114),
('Silvana', 'Gomes', 'silvana@gmail.com', '344456', 2, 'R. Paulo I, 4', 'Ardósias', 'RJ', 21651145),
('Lucas', 'Sêtte', 'lucas@gmail.com', '3er456', 3, 'R. Bahia, 1355', 'Belo Horizonte', 'MG', 31454232);

SELECT nome, email, cidade, estado, cep, cargo_id FROM funcionarios WHERE estado = "MG";
nome    email            cidade            estado   cep          cargo_id
Lucas   lucas@gmail.com  Belo Horizonte    MG       31454232     3

-- inserindo cargos
INSERT INTO cargos (descricao) VALUES ('Gerente'), ('Vendedor'), ('Desenvolvedor');

SELECT * FROM cargos;
id   descricao
1    Gerente
2    Vendedor
3    Desenvolvedor

-- alterar o valor de um campo já inserido
UPDATE funcionarios SET cargo_id=1 WHERE id=1;

-- agora a tabela funcionarios está no estado
SELECT * FROM funcionarios;
id nome    sobrenome  email               senha   endereco            cidade         estado cep       cargo_id
1  Pedro   Altusser   pedroalt@email.com  123456  R. João Paulo, 534  Rio de Janeiro RJ     21654897  3
2  Levindo Lopes      lopest@gmail.com    234456  R. Paulo II, 534    Ardósias       RJ     21115114  1
3  Silvana Gomes      silvana@gmail.com   344456  R. Paulo I, 4       Ardósias       RJ     21651145  2
4  Lucas   Sêtte      lucas@gmail.com     3er456  R. Bahia, 1355      Belo Horizonte MG     31454232  3


Uma vez preenchidas as tabelas podemos fazer consultas de todos os tipos. Para ler dados da tabela funcionarios com a descrição dos cargos em cargos usamos INNER JOIN. As duas consultas abaixo são equivalentes:

-- INNER JOIN
SELECT f.nome, c.descricao FROM funcionarios f
  INNER JOIN cargos c WHERE f.cargo_id = c.id;

SELECT f.nome, c.descricao FROM funcionarios f
  INNER JOIN cargos c ON (f.cargo_id = c.id);

nome    descricao
Pedro    Desenvolvedor
Levindo    Gerente
Silvana    Vendedor
Lucas    Desenvolvedor

-- aliases podem ser dados para qualquer campo. Strings são concatenados com ||
SELECT nome || " " || sobrenome  as 'Funcionário' FROM funcionarios;
Funcionário
Pedro Altusser
Levindo Lopes
Silvana Gomes
Lucas Sêtte

Aliases foram usados acima para atribuir nomes às tabelas (como em FROM funcionarios f INNER JOIN cargos c) ou a compos resultados da consultas (como em nome || " " || sobrenome as 'Funcionário').

Consultas podem ser modificadas pelas condições em WHERE, e partes dos campos podem ser encontrados com LIKE. % representa qualquer grupo de caracteres, _ (underline) significa um caracter.

-- Uma consulta simples com dupla condição
SELECT id, nome || " " || sobrenome  as 'Funcionário', estado
    FROM clientes  WHERE estado = "MG" and id=2;
id   Funcionário   estado
2   Polka Brita   MG

-- para apagar um ou mais registros (o registro listado acima)
DELETE FROM clientes  WHERE estado = "MG" and id=2;

-- nome iniciado com "Ma" e a letra "a" no sobrenome
SELECT id, nome, sobrenome FROM clientes WHERE nome LIKE 'Ma%' AND sobrenome LIKE '%a%';
id   nome   sobrenome
4   Martha   Maertis

SELECT id, nome, sobrenome FROM clientes WHERE nome LIKE '_a%';
id   nome    sobrenome
1   Caio    Zuretta
4   Martha    Maertis

SELECT id, nome, sobrenome FROM clientes WHERE nome LIKE '_a___a';
id   nome    sobrenome
4   Martha    Maertis

Para fazer outras consultas cruzadas, em mais de uma tabela, vamos entrar dados nas tabelas fornecedores e produtos.

-- inserindo fornecedores e produtos
INSERT INTO fornecedores (nome, cnpj, email, endereco, cidade, estado, cep)
    VALUES
    ('Microsoft', '234.456-098', 'ms@ms.com', 'R. Pedro Alves, 34', 'Sorocaba', 'SP', 1234567),
    ('Apple', '212.1226-128', 'apps@apps.com', 'R. Gerino Silva, 456', 'Brasília', 'DF', 61256767), 
    ('Lenovo', '2456.1567-676', 'lenovo@lenovo.com', 'R. Power Guido, 786', 'Manaus', 'AM', 23452345),
    ('Dell', '222.453-444', 'del@del.com', 'R. Vaga Errante, 13', 'Sorocaba', 'SP', 1234567),
    ('Logitec', '666.7777-888', 'logi@log.com', 'R. Ulva Gods Silva, 90', 'Brasília', 'DF', 61256767),
    ('Multilaser', '1111.9999-888', 'miltila@multi.com', 'R. Volvo Zona, 76', 'Itabira', 'MG', 3114045);
    
INSERT INTO produtos (fornecedor_id, descricao, preco)
    VALUES
    (2, 'iPAD', 12345.80),
    (1, 'Windows 11', 67.90),
    (5, 'Teclado sem fio', 99.00),
    (3, 'Notebook Intel', 1560.00),
    (15, 'Mouse Chines', 13.33),
    (16, 'Chingling Roteador', 59.89);

SELECT * FROM fornecedores;
SELECT * FROM produtos;
-- as tabelas resultado estão nas imagens abaixo


Com essas definições de valores vamos fazer algumas consultas para exibir a sintaxe de consultas JOIN. JOIN e INNER JOIN são idênticos.

SELECT p.id, f.nome, p.descricao, p.preco FROM produtos p
    JOIN fornecedores f ON  (p.fornecedor_id = f.id);
id  nome         descricao         preco
1   Apple        iPAD              12345.8
2   Microsoft    Windows 11        67.9
3   Logitec      Teclado sem fio   99
4   Lenovo       Notebook Intel    1560


SELECT p.id 'COD.', f.nome 'Vendedor', p.descricao 'Produto' , p.preco 'Preço'
    FROM produtos p RIGHT JOIN fornecedores f ON  (p.fornecedor_id = f.id);
COD.    Vendedor      Produto           Preço
1       Apple         iPAD              12345.8
2       Microsoft     Windows 11        67.9
3       Logitec       Teclado sem fio   99
4       Lenovo        Notebook Intel    1560
NULL    Dell          NULL              NULL
NULL    Multilaser    NULL              NULL

Suponha que desejamos fazer um relatório com todas as vendas para o cliente de sobrenome “Arbinger”. Podemos primeiro encontrar o id do cliente e, daí, todas as vendas para ele. Fazemos um JOIN para encontrar o nome dos funcionários que fizeram as vendas.

-- o id do cliente é
SELECT id FROM clientes WHERE  sobrenome="Arbinger";
3

-- as vendas desse cliente
SELECT * FROM vendas
    WHERE  cliente_id = (SELECT id FROM clientes WHERE  sobrenome="Arbinger");
    
--  as vendas associadas ao funcionário vendedor
SELECT v.data_venda, v.total, v.descricao, f.nome, f.sobrenome FROM vendas v
    JOIN funcionarios f 
    WHERE  f.id = v.funcionario_id 
    and v.cliente_id = (SELECT id FROM clientes WHERE  sobrenome="Arbinger");

data_venda   total   descricao   nome   sobrenome
10/12/2020   200   Filial 1   Levindo   Lopes
11/12/2020   100   Filial 1   Levindo   Lopes
13/12/2020   280   Filial 2   Pedro   Altusser
17/12/2020   290   Filial 4   Levindo   Lopes

Se, além disso quisermos saber quais os itens foram vendidos fazemos:

SELECT v.data_venda, v.total, v.descricao, f.nome, f.sobrenome, p.descricao as "Produto"
    FROM vendas v
    JOIN funcionarios f
    JOIN itens_vendas i
    JOIN produtos p 
    WHERE  f.id = v.funcionario_id and v.id = i.venda_id and i.produto_id = p.id
    and v.cliente_id = (SELECT id FROM clientes WHERE  sobrenome="Arbinger");

data_venda   total   descricao   nome       sobrenome    Produto
10/12/2020   200     Filial 1    Levindo    Lopes        Windows 11
10/12/2020   200     Filial 1    Levindo    Lopes        Teclado sem fio
11/12/2020   100     Filial 1    Levindo    Lopes        iPAD

Observe que dois itens foram listados relativos à venda do dia 10/12/2020 e um item para o dia 11/12. Nenhum item foi listado para as vendas dos dias 13 e 17/12.

sqlite3 e Python

O python traz um módulo instalado com a biblioteca padrão para integrar o SQLite com o código. Para usá-lo basta importar esse módulo chamado sqlite3.

import sqlite3
connection = sqlite3.connect("escola.db")
cursor = connection.cursor()

sql = """
      CREATE TABLE IF NOT EXISTS  alunos (
        id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
        nome TEXT,
        idade INTEGER
        );
      """
cursor.execute(sql)

sql = """
    INSERT INTO alunos (nome, idade) VALUES
    ('Alice', 21),
    ('Letícia', 22),
    ('Vinicius', 23),
    ('Guilherme', 12),
    ('Marcos', 37),
    ('Pedro', 6),
    ('Wanda', 20),
    ('Aluísio', 10);
    """
cursor.execute(sql)
connection.commit()

dados = cursor.execute("SELECT * FROM alunos WHERE idade <=' 22")
for linha in dados:
    print(linha[0], linha[1], linha[2])

1 Alice 21
4 Guilherme 12
6 Pedro 6
7 Wanda 20
8 Aluísio 10

Com o comando connection = sqlite3.connect("escola.db") o banco de dados é criado, se já não existe. Um cursor é um objeto usado para fazer a relação entre o código (e as queries) com o banco. O objeto dados é um iterável que retorna tuplas com os resultados da query.

Digamos que queremos fazer uma consulta ao banco de dados pre-existente phylos.db, usado nos exercícios anteriores. Para isso estabelecemos uma nova conecção com esse banco de dados.

connection = sqlite3.connect("vendas.db")
sql = """
   SELECT v.data_venda, v.total, v.descricao, f.nome, f.sobrenome, p.descricao as "Produto"
      FROM vendas v
      JOIN funcionarios f
      JOIN itens_vendas i
      JOIN produtos p 
      WHERE  f.id = v.funcionario_id and v.id = i.venda_id and i.produto_id = p.id
      and v.cliente_id = (SELECT id FROM clientes WHERE  sobrenome="Arbinger");
"""
cursor = connection.cursor()
data = cursor.execute(sql)
for row in data:
    print(row)
    
# o resultado é:
('10/12/2020', 200, 'Filial 1', 'Levindo', 'Lopes', 'Windows 11')
('10/12/2020', 200, 'Filial 1', 'Levindo', 'Lopes', 'Teclado sem fio')
('11/12/2020', 100, 'Filial 1', 'Levindo', 'Lopes', 'iPAD')

sqlite3 e pandas

Veja o artigo Pandas e Dataframes e artigos subsequentes.

Uma outra possibilidade interessante é a de integrar os dataframes do pandas com o SQLite. Para isso importamos o sqlite3 e o pandas e estabelecemos uma conexão com o banco de dados. Um dataframe pode ser carregado diretamente com o resultado da consulta com pandas.read_sql(sql, connection).

import sqlite3
import pandas as pd

connection = sqlite3.connect("vendas.db")
sql = """
   SELECT v.data_venda, v.total, v.descricao, f.nome, f.sobrenome
      FROM vendas v JOIN funcionarios f
      WHERE f.id = v.funcionario_id;
"""
cursor = connection.cursor()
# apenas para ver o resultado da consulta fazemos:
data = cursor.execute(sql)
for row in data:
    print(row)

# o resultado:
('10/12/2020', 200, 'Filial 1', 'Levindo', 'Lopes')
('11/12/2020', 100, 'Filial 1', 'Levindo', 'Lopes')
('12/12/2020', 120, 'Filial 2', 'Levindo', 'Lopes')
('13/12/2020', 280, 'Filial 2', 'Pedro', 'Altusser')
('17/12/2020', 290, 'Filial 4', 'Levindo', 'Lopes')

df = pd.read_sql(sql, connection)
print(df)

# o resultado:
   data_venda  total descricao     nome sobrenome
0  10/12/2020    200  Filial 1  Levindo     Lopes
1  11/12/2020    100  Filial 1  Levindo     Lopes
2  12/12/2020    120  Filial 2  Levindo     Lopes
3  13/12/2020    280  Filial 2    Pedro  Altusser
4  17/12/2020    290  Filial 4  Levindo     Lopes

df.to_sql("tabela_2", connection)

O último comando insere uma tabela tabela_2 no banco de dados vendas.db com os campos e valores desse dataframe.

Bibliografia

Sobre SQLite

Sobre o SQLite3, no Python