2010-01-15 6 views
2

Я использую MySQL Connector/Net, и я хочу написать запрос к таблице, имя которой будет указано во время выполнения.Как выбрать динамическое имя таблицы во время выполнения?

Этот пример с верхней части моей головы (не тестировался):

public class DataAccess 
{ 
    public enum LookupTable 
    { 
     Table1, 
     Table2, 
     Table3 
    } 

    public int GetLookupTableRowCount(LookupTable table) 
    { 
     string tableName = string.Empty; 

     switch (table) 
     { 
      case LookupTable.Table1: 
       tableName = "table_1"; 
       break; 
      case LookupTable.Table2: 
       tableName = "table_2"; 
       break; 
      case LookupTable.Table3: 
       tableName = "table_3"; 
       break; 
      default: 
       throw new ApplicationException("Invalid lookup table specified."); 
     } 

     string commandText = string.Concat("SELECT COUNT(*) FROM ", tableName); 

    // Query gets executed and function returns a value here... 
    } 
} 

Так как я не думаю, что вы можете параметризовать имя таблицы в запросе, я использовал перечисление, а не строки в параметр функции для ограничения возможности SQL injection.

Это похоже на хороший подход? Есть ли способ лучше?

+0

Does .net не поддерживает добавление дополнительных свойств в тип перечисления? – Jherico

+0

Вы имеете в виду Table1 = "table_1" и т. Д. Если это так, то вы можете использовать только такие интегральные типы, как int и long. –

ответ

4

Вы не можете параметрировать identifier (имя таблицы или имя поля) в MySQL, однако вы можете избежать их с помощью обратных ссылок.

Следующий запрос будет работать безопасно, но приводит к ошибке, так как таблица не существует (если по какой-то странной случайности вы на самом деле есть таблица с именем, как это):

SELECT * FROM `users; DROP TABLE users;`; 

В принципе, вы можете использовать динамический имена или поля, если они заключены в обратные ссылки. Для того, чтобы предотвратить инъекции SQL таким образом, все, что вам нужно сделать, это вырезать любую кавычку первой:

tableName = tableName.Replace("`", ""); 
string commandText = "SELECT COUNT(*) FROM `" + tableName + "`"; 
+0

Я вижу, что вы говорите, но я думаю, что я безопаснее изолировать текст запроса от любых входов свободной формы. Вот почему я не хотел использовать строковый параметр в моем методе доступа к данным. –

+0

Ну, я не могу заставить вас доверять семантике языка. Вы либо доверяете методам backtick и escape-кода MySQL, либо нет.Альтернативой является привязка структуры базы данных к вашему коду через какую-то таблицу поиска, как другие предложили, что я лично считаю очень неприятным. – zombat

+0

Я бы посоветовал вам внимательно прочитать эту страницу по идентификаторам, поскольку она определяет, что такое действительный идентификатор. Если вам нужна дополнительная защита, вы всегда можете использовать буквенно-цифровые символы или даже запрос, чтобы увидеть, существовала ли таблица заранее. Однако MySQL будет обрабатывать любые недопустимые символы в идентификаторе, вызывая ошибку в запросе. – zombat

1

Правильно, вы не можете использовать параметр запроса для имени таблицы, имени столбца, ключевого слова или выражения SQL и т. Д. Вы можете использовать параметр запроса только для одного значения.

Я согласен с тем, что выполнение какого-либо сопоставления ввода с именем таблицы literal является хорошим способом защиты от SQL-инъекции.

Я не программирую в .NET, я обычно использую динамические языки, такие как PHP, Python или Perl. Поэтому я использую хэш-массив. Вы можете пропустить switch(), если вы можете просто использовать переменную перечисления для индексации в массив хэшей.

$tableName = $tableNameHash[ $table ]; 

Поддерживает ли .NET структуру данных хэш-карты? Это то, что я бы искал.


Похоже есть hash_map класса в стандартной библиотеке C++.

+0

Да, в .NET есть hashtables и словари и множество других классов стиля коллекции. Мне нравится ваше предложение. –

2

имен Динамической таблицы никогда не хороший подход (если вы не разрабатываете PHPMyAdmin или что-то подобное).

Если имена таблиц ограничены, почему бы вам просто не сделать хранимую процедуру и назвать ее параметром?

DECLARE _which INT 
BEGIN 
     SELECT COUNT(*) 
     FROM table_1 
     WHERE _which = 1 
     UNION ALL 
     SELECT COUNT(*) 
     FROM table_2 
     WHERE _which = 2 
     UNION ALL 
     SELECT COUNT(*) 
     FROM table_3 
     WHERE _which = 3 
END 
+3

Я не согласен с «никогда хорошим подходом». Крупные корпоративные приложения почти наверняка будут использовать какую-то динамическую схему именования, чтобы разбить данные на управляемые множества. Методы очертания базы данных будут использовать даже динамические имена * базы данных *, а не только таблицы. – zombat