Como usar variáveis ​​na instrução SQL em Python?

91

Ok, então não tenho tanta experiência em Python.

Eu tenho o seguinte código Python:

cursor.execute("INSERT INTO table VALUES var1, var2, var3,")

onde var1é um inteiro var2e var3são strings.

Como posso escrever os nomes das variáveis ​​sem python incluindo-os como parte do texto da consulta?

user111606
fonte

Respostas:

103
cursor.execute("INSERT INTO table VALUES (%s, %s, %s)", (var1, var2, var3))

Observe que os parâmetros são passados ​​como uma tupla.

A API do banco de dados executa o escape e a cotação adequados de variáveis. Tenha cuidado para não usar o operador de formatação de string ( %), porque

  1. não faz nenhum escape ou citação.
  2. está sujeito a ataques de formato de string não controlado, por exemplo, injeção de SQL .
Ayman Hourieh
fonte
Interessante, por que funciona com os vars separadamente em vez de em um array (var1, var2, var3)?
Andomar
De acordo com as especificações da API do banco de dados, parece que pode ser das duas maneiras: python.org/dev/peps/pep-0249
Ayman Hourieh
9
@thekashyap Leia novamente com atenção. O que é inseguro é usar o operador de formatação de string %. Na verdade, digo isso na resposta.
Ayman Hourieh de
meu mal .. Eu imaginei um em % vez de ,entre a string e variáveis ​​.. não posso desfazer meu voto para baixo devido a vários motivos .. Eu pessoalmente gostaria de ver palavras como inseguro / ataque etc mencionadas na descrição onde você diz don 't use %..
Kashyap
1
@eric a resposta diz para não usar o % operador para formatar a string. Aqueles %na string estão sendo usados cursor.executediretamente pelo e, como ele sabe que está gerando SQL, pode fazer mais para protegê-lo.
Mark Ransom
66

Diferentes implementações da Python DB-API podem usar diferentes marcadores de posição, portanto, você precisará descobrir qual está usando - pode ser (por exemplo, com MySQLdb):

cursor.execute("INSERT INTO table VALUES (%s, %s, %s)", (var1, var2, var3))

ou (por exemplo, com sqlite3 da biblioteca padrão do Python):

cursor.execute("INSERT INTO table VALUES (?, ?, ?)", (var1, var2, var3))

ou outros ainda (depois de VALUESvocê ter (:1, :2, :3), ou "estilos nomeados" (:fee, :fie, :fo)ou (%(fee)s, %(fie)s, %(fo)s)onde você passa um dict em vez de um mapa como o segundo argumento execute). Verifique a paramstyleconstante de string no módulo da API do banco de dados que você está usando e procure por paramstyle em http://www.python.org/dev/peps/pep-0249/ para ver o que são todos os estilos de passagem de parâmetro!

Alex Martelli
fonte
É possível fazer a mesma coisa, mas com o script SQL externo?
Novitoll
46

Várias maneiras. NÃO use o mais óbvio ( %scom %) no código real, ele está aberto a ataques .

Aqui copie e cole do pydoc do sqlite3 :

# Never do this -- insecure!
symbol = 'RHAT'
c.execute("SELECT * FROM stocks WHERE symbol = '%s'" % symbol)

# Do this instead
t = ('RHAT',)
c.execute('SELECT * FROM stocks WHERE symbol=?', t)
print c.fetchone()

# Larger example that inserts many records at a time
purchases = [('2006-03-28', 'BUY', 'IBM', 1000, 45.00),
             ('2006-04-05', 'BUY', 'MSFT', 1000, 72.00),
             ('2006-04-06', 'SELL', 'IBM', 500, 53.00),
            ]
c.executemany('INSERT INTO stocks VALUES (?,?,?,?,?)', purchases)

Mais exemplos se você precisar:

# Multiple values single statement/execution
c.execute('SELECT * FROM stocks WHERE symbol=? OR symbol=?', ('RHAT', 'MSO'))
print c.fetchall()
c.execute('SELECT * FROM stocks WHERE symbol IN (?, ?)', ('RHAT', 'MSO'))
print c.fetchall()
# This also works, though ones above are better as a habit as it's inline with syntax of executemany().. but your choice.
c.execute('SELECT * FROM stocks WHERE symbol=? OR symbol=?', 'RHAT', 'MSO')
print c.fetchall()
# Insert a single item
c.execute('INSERT INTO stocks VALUES (?,?,?,?,?)', ('2006-03-28', 'BUY', 'IBM', 1000, 45.00))
Kashyap
fonte
4
Algumas das implementações do DB-API na verdade usam% s para suas variáveis ​​- mais notavelmente psycopg2 para PostgreSQL. Isso não deve ser confundido (embora seja facilmente) com o uso de% s com o operador% para substituição de string. Eu seria muito bom se, para portabilidade, pudéssemos ter apenas uma forma padrão definida de especificar os parâmetros SQL para DB-API.
ThatAintWorking
23

http://www.amk.ca/python/writing/DB-API.html

Tenha cuidado ao simplesmente anexar valores de variáveis ​​às suas instruções: Imagine um usuário nomeando a si mesmo ';DROP TABLE Users;'- É por isso que você precisa usar o escape sql, que o Python fornece quando você usa o cursor.execute de maneira decente. O exemplo no url é:

cursor.execute("insert into Attendees values (?, ?, ?)", (name,
seminar, paid) )
Num Lock
fonte
13
Na verdade, não é um escape de SQL. É ligação variável, que é muito mais simples e direta. Os valores são vinculados à instrução SQL após a análise, tornando-a imune a qualquer ataque de injeção.
S.Lott
bem, se é escape de SQL ou ligação de variável depende de quão bom ou ruim é seu servidor de banco de dados / driver de API de banco de dados. Eu vi alguns bancos de dados de produção do mundo real amplamente implantados que têm seu driver DB-API apenas escapando, em vez de manter os dados e o código fora da banda na transmissão. Nem preciso dizer que não tenho muito respeito por esses chamados "bancos de dados".
Charles Duffy