2013-03-19 2 views
2

Для записи я использую Python и SQLlite. У меня есть работающая функция, которая генерирует SQL, который мне нужен, но это кажется неправильным.Динамическое предложение SQL WHERE

def daily(self, host=None, day=None): 
    sql = "SELECT * FROM daily WHERE 1" 
    if host: 
     sql += " AND host = '%s'" % (host,) 
    if day: 
     sql += " AND day = '%s'" % (day,) 
    return sql 

Возможно, мне понадобится добавить несколько столбцов и критериев позже.

Любые лучшие идеи?

Редактировать: Что не так, так это то, что я динамически строю SQL из строк. Это, как правило, не самый лучший подход. SQL-инъекции attacs, необходимо правильно избежать строк. Я не могу использовать заполнители, потому что некоторые из значений - «Нет» и не должны находиться в условии WHERE.

+0

Что именно вы думаете об этом неправильно? –

+0

Вот подсказка: используйте словарь; но это, вероятно, лучше всего для ['programers.stackexchange.com'] (http://programmers.stackexchange.com) –

+1

Хуже всего то, что вы передаете параметры запроса через заполнители python. Используйте выполнить sqlite для передачи параметров запроса в запрос. См. Http://docs.python.org/2/library/sqlite3.html#sqlite3.Cursor.execute. – alecxe

ответ

7

Вы действительно не хотите использовать форматирование строк для включения значений. Оставьте это в API базы данных через SQL-параметры.

Использование параметров вы:

  • дает к базе данных возможности подготовить заявление и повторно использовать план запроса для повышения производительности.
  • сберечь головную боль, чтобы избежать правильного значения (в том числе избегать экранов SQL и с этими атаками SQL-инъекций).

С SQLLite supports named SQL parameters, я бы вернуть как заявление и словарь с параметрами:

def daily(self, host=None, day=None): 
    sql = "SELECT * FROM daily" 
    where = [] 
    params = {} 
    if host is not None: 
     where.append("host = :host") 
     params['host'] = host 
    if day is not None: 
     where.append("day = :day") 
     params['day'] = day 
    if where: 
     sql = '{} WHERE {}'.format(sql, ' AND '.join(where)) 
    return sql, params 

затем передать как к cursor.execute():

cursor.execute(*daily(host, day)) 

поколения SQL становится сложным быстро, вы можете посмотреть на SQLAlchemy core, чтобы сделать поколение вместо этого.

Для примера, вы можете создать:

from sqlalchemy import Table, Column, Integer, String, Date, MetaData 

metadata = MetaData() 
daily = Table('daily', metadata, 
    Column('id', Integer, primary_key=True), 
    Column('host', String), 
    Column('day', Date), 
) 
from sqlalchemy.sql import select 

def daily(self, host=None, day=None): 
    query = select([daily]) 
    if host is not None: 
     query = query.where(daily.c.host == host) 
    if day is not None: 
     query = query.where(daily.c.day == day) 
    return query 

Объект query может иметь дополнительные фильтры, применяемые к нему, приказал, сгруппированных, используемый в качестве подвыборки к другим запросам, присоединился и, наконец, отправляется выполняться в что указывает на то, что SQLAlchemy превратит это в SQL-подгонку для конкретной базы данных, к которой вы подключаетесь.

+0

Спасибо за ссылку SQLAlchemy. Но это выходит за рамки. Стандартное/встроенное ограничение модулей. – Ayman

+0

@Ayman: Тогда, по крайней мере, используйте параметры SQL. Почему существует такое произвольное ограничение? –

+0

Корпоративная политика с сторонними библиотеками. Я тоже это ненавижу, но ничего не могу поделать. – Ayman

Смежные вопросы