Можно ли запросить указанное пользователем имя столбца, не прибегая к динамическому SQL? - PullRequest
2 голосов
/ 16 сентября 2011

Я пытаюсь реализовать API со следующей подписью:

public static List<string> SearchDatabase(
    string column,
    string value);

Для реализации API необходимо создать запрос SQL SELECT с предложением WHERE, в котором используется имя столбца, указанное в параметре column.

Запрос:

string query =
    string.Format(
        "SELECT Name, @Column " +
        "FROM Db1 " +
        "INNER JOIN Db2 ON Db1.Id = Db2.Id " +
        "WHERE @Column = @Value");

Команда и параметры SQL:

SqlCommand selectCmd =
    new SqlCommand(
        query,
        connection);
selectCmd.CommandTimeout = SqlCommandTimeout;
selectCmd.Parameters.AddRange(
    new SqlParameter[]
{
    new SqlParameter("@Column", column),
    new SqlParameter("@Value", value)
});

И я выполняю это так:

SqlDataReader sqlDataReader = selectCmd.ExecuteReader();
while (sqlDataReader.Read())
{
    // ...
}

Проблема в том, что sqlDataReader не возвращает никаких строк, поэтому я не захожу в цикл while выше.

Однако, если я изменю последнюю строку запроса выше с:

"WHERE @Column = @Value");

до

"WHERE Vendor = @Value");

(т. Е. Жестко закодировать имя столбца в 'Vendor'), затем оно работает.

Насколько я понимаю из проведенного мною исследования, невозможно передать имена столбцов в качестве параметров, а только те значения, к которым мы обращаемся. Но, похоже, он позволяет мне использовать параметр @Column в предложении SELECT, а не предложение WHERE.

Я не хочу прибегать к динамическому SQL из-за проблем с внедрением SQL. Есть ли другой способ обойти это?

Ответы [ 5 ]

7 голосов
/ 16 сентября 2011

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

"WHERE " + Sanitize(column) + " = @Value");
1 голос
/ 16 сентября 2011

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

 Select case 
         when @column = 'UserName' then username
         when @column = 'Email' then email 
         Else firstname End as Column
 From MyTable
 Where value = @vendor

Однако я не вижу в этом смысла, просто возвращаю все столбцы и выбираю тот, который вас интересует, используя набор результатов в C # (если это возможно).

1 голос
/ 16 сентября 2011

В динамическом SQL нет ничего плохого, если вы правильно указали имя столбца:

string query = string.Format(
    "SELECT Name, {0} " +
    "FROM Db1 " +
    "INNER JOIN Db2 ON Db1.Id = Db2.Id " +
    "WHERE {0} = @Value",
    Delimit(column));

где ...

public static string Delimit(string name) {

    if(name == null) {
        throw new ArgumentNullException("name");
    } else if(name.Length == 0) {
        throw new ArgumentException("name");
    }

    return "[" + name.Replace("]", "]]") + "]";

}
0 голосов
/ 16 сентября 2011

Предположительно, имя столбца уже подтверждено? Если нет, вы можете добавить дополнительную проверку, добавив select 1 from <column-names-table> where <column-name-column> = @column.

Если ваш предикат всегда =, вам не нужно выбирать @Column. Дополнительные усилия, необходимые для заполнения структуры данных на стороне клиента, тривиальны.

РЕДАКТИРОВАТЬ: Вместо проверки имени столбца при каждом вызове @ комментарий Дарина к "2011-09-16 07: 24: 34Z" здесь единовременной инициализации лучше.

0 голосов
/ 16 сентября 2011

Нет, вы не можете параметризировать имена столбцов таким образом, не в SQL Server.

Чтобы избежать построения динамического запроса, вы можете использовать выражение CASE, например:

SELECT
  Name,
  CASE @Column
    WHEN 'Age' THEN Age
    WHEN 'Weight' THEN Weight
    …
  END
…

Обратите внимание, однако, что типы возможных столбцов должны быть неявно совместимы друг с другом в этом случае (предполагается, что каламбур плох)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...