Подтвердить что ты не робот

Поиск столбца int на основе строкового значения

У меня есть View View_Booking в SQL Server 2014:

bkID    bkSlot    bkStatus
----    ------    --------
2       Lunch     1
4       Lunch     1
6       Dinner    0
7       Lunch     1

Пока в С# я использовал gridview и литой bkStatus в строку вроде:

<asp:Label ID="lblStatus" Text='<%# (Eval("bkStatus")+"" == "1") ? "Booked" : "Pending" %>'
    ... ></asp:Label>

bkID    bkSlot    bkStatus
----    ------    --------
2       Lunch     Booked
4       Lunch     Booked
6       Dinner    Pending
7       Lunch     Booked

Теперь я ищу в представлении с помощью этого запроса:

SELECT * FROM View_Booking 
WHERE CAST(bkID AS NVARCHAR(MAX)) LIKE '%" + keyword + "%' 
OR bkSlot LIKE '%"+keyword+"%' 
OR bkStatus LIKE << ? >>

Но не знаю, как искать bkStatus, который передается как строка из С#, в то время как int в sql?

4b9b3361

Ответ 1

Некоторые рекомендации

Запрошенный вами запрос должен быть оптимизирован:

  • Во-первых, использование CAST(bkID AS NVARCHAR(MAX)) будет влиять на производительность запроса, потому что он не будет использовать какой-либо индекс, также приведение к NVARCHAR(MAX) приведет к снижению производительности.

  • bkStatus - это числовой столбец, поэтому вам нужно использовать оператор = и сравнить его с числовыми значениями (0 or 1 or ...), а также предоставленные текстовые значения определены в теге asp, но не в базе данных, поэтому они используются на уровне приложения, а не уровне данных.

  • если вы используете CAST(bkID AS NVARCHAR(MAX)) для поиска столбца bkid, который содержит определенную цифру (например: поиск 1 → результат 1, 10, 11...), затем попробуйте Casting для определенного размера (например: CAST(bkID as NVARCHAR(10))

  • Рекомендуется использовать параметризованные запросы для повышения производительности и предотвращения атак Sql injection. посмотрите на @не повезло ответить

  • Вы можете использовать словарь Object для хранения значений идентификаторов, связанных с ключевыми словами

Пример

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

var dicStatus = new Dictionary<int, string> { 
    { 0, "Pending" }, 
    { 1, "Booked"  },
    { 2, "Cancelled" }
    // ...
};

string querySql = " SELECT * FROM View_Booking" +
                  " WHERE CAST(bkID AS NVARCHAR(10)) LIKE @bkID" + 
                  " OR bkSlot LIKE @bkSlot" +
                  " OR bkStatus = @status";
using (SqlConnection dbConn = new SqlConnection(connectionString))
{
    dbConn.Open();
    using (SqlCommand sqlCommand = new SqlCommand(querySql, dbConn))
    {
        sqlCommand.Parameters.Add("@bkID", SqlDbType.VarChar).value ="%" + keyword + "%";
        sqlCommand.Parameters.Add("@bkSlot", SqlDbType.VarChar).value ="%" + keyword + "%";
        sqlCommand.Parameters.Add("@status", SqlDbType.Int).value = dicStatus.FirstOrDefault(x => x.Value == keyword).Key;
        sqlCommand.ExecuteNonQuery();
     }
}

Также, если BkID является целым столбцом, лучше использовать

sqlCommand.Parameters.Add("@bkID", SqlDbType.Int).value = (Int)keyword ;

Ссылки и полезные ссылки

Ответ 2

Таким образом, вам нужно поле поиска, в котором пользователь может выполнять поиск с помощью bkID, bkSlot или bkStatus Если текст поиска Booked или находится в состоянии Pending мы должны добавить фильтр для bkStatus который будет целочисленным полем в базе данных. право? Мало кто еще, что я должен упомянуть здесь является использование using, а также параметризация запросов для более рационального и безопасного способа исполнения. Поэтому я хотел бы предложить построить и выполнить запрос следующим образом:

int statusCode = -1;
if(keyword.ToLower() == "booked")
   statusCode = 1;
else if(keyword.ToLower() == "pending")
   statusCode = 0;
string querySql = " SELECT * FROM View_Booking" +
                  " WHERE CAST(bkID AS NVARCHAR(MAX)) LIKE @bkID" + 
                  " OR bkSlot LIKE @bkSlot" +
                  " OR bkStatus = @status";
using (SqlConnection dbConn = new SqlConnection("connectionString here"))
{
    dbConn.Open();
    using (SqlCommand sqlCommand = new SqlCommand(querySql, dbConn))
    {
        sqlCommand.Parameters.Add("@bkID", SqlDbType.VarChar).value ="%" + keyword + "%";
        sqlCommand.Parameters.Add("@bkSlot", SqlDbType.VarChar).value ="%" + keyword + "%";
        sqlCommand.Parameters.Add("@status", SqlDbType.int).value = statusCode;
        sqlCommand.ExecuteNonQuery();
     }
}

Пожалуйста, обратите внимание на следующее:

  • Если вы хотите включить фильтр bkStatus для book, Pend и т.д.., то вы должны изменить состояние соответственно с помощью .Contains() или .StartsWith() вместо того, чтобы для .ToLower()
  • statusCode инициализируется с -1 чтобы избежать фильтра на основе bkStatus для всех других значений

Ответ 3

Вы можете использовать функцию declare для создания временной таблицы со списком bkStatus.

Вам будет проще создать запрос, используя bkStatus в качестве внешнего ключа. После этого вам больше не нужно использовать функцию cast или like. Это будет немного неэффективно.

Вы можете попробовать следующий код:

declare @bkstatus table (number int primary key , bkstatus varchar(10) )
insert into @bkstatus (number , bkstatus)
values ( 0 , 'Pending'), (1 , 'Booked')

а затем используя этот запрос:

SELECT * FROM View_Booking v
INNER JOIN @bkstatus b on v.bkstatus = b.number
WHERE b.bkstatus =  @keyword  

Ответ 4

Еще одна опция, использующая CHOOSE() для декодирования bkStatus и TRY_CONVERT() для проверки bkID.

Пример

Declare @KeyWord varchar(50) = 'Pending';

Select * 
 From  View_Booking 
 Where bkID = try_convert(int,@KeyWord)
    or bkSlot like '%'[email protected]+'%'
    or choose(bkStatus+1,'Pending','Booked')[email protected]

Возвращает

bkID    bkSlot  bkStatus
6       Dinner  0

Ответ 5

Если keyword будет именем статуса, а не идентификатором состояния, я бы создал таблицу BookingStatus, там были столбцы bkStatus и bkStatusTitle и присоединили ее к View_Booking. Вы можете легко сделать LIKE на bkStatusTitle, затем.

SELECT * FROM View_Booking 
WHERE CAST(bkID AS NVARCHAR(16)) LIKE '%' + @keyword + '%' 
OR bkSlot LIKE '%' + @keyword + '%' 
OR bkStatusTitle LIKE '%' + @keyword + '%'

Если keyword будет строковым представлением bkStatus, я просто посмотрю, будут ли значения одинаковыми.

В качестве побочной заметки неплохо было бы построить ваши SQL-запросы, объединяя в них пользовательский ввод, например '%' + keyword + '%'. Это доступно для атак SQL-инъекций. Лучше использовать параметры SQL для передачи пользовательского ввода в SQL-запросы. Использование '%' + @keyword + '%' в бите SQL и на С#, что-то вроде примера ниже было бы намного безопаснее.

sqlCommand.Parameters.Add("@keyword", SqlDbType.VarChar, 1000);
sqlCommand.Parameters["@keyword"].Value = searchText;

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

Ответ 6

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

Сохранение простых вещей

searchStatusKey = yourVariableHodingTheString == "Booked" ? 1 : 0;

Однако, чтобы избежать утомительных ошибок и безболезненных обновлений кода в нескольких местах (скажем, потому что они решили добавить еще один статус там), я бы рекомендовал таблицу перевода здесь. Что-то вроде HashMap (ассоциативный массив).

var statusMap = new Dictionary<int, string> { 
    { 0, "Pending" }, 
    { 1, "Booked" },
    /* can always add more pairs in the future as needed */
};

Теперь, если ваш параметр находится в переменной с именем searchStatus

searchStatusKey = statusMap.FirstOrDefault(x => x.Value == searchStatus).Key;

Теперь укажите как параметр bkStatus в той части, где значение searchStatusKey и вы закончите.

select * from View_Booking where bkStatus = << ? >>

Ответ 7

Я просто сосредоточусь на этой части вашего вопроса (это сам вопрос?):

Но не знаю, как искать bkStatus, который передается как строка из С#, в то время как int в sql?

Один из способов справиться с этим в SQL - с помощью CASE. В вашем конкретном случае вы могли бы (не означает "должен" делать что-то вроде:

SELECT * FROM View_Booking 
WHERE CAST(bkID AS NVARCHAR(MAX)) LIKE '%" + keyword + "%' 
OR bkSlot LIKE '%"+keyword+"%' 
OR bkStatus = CASE '%"+keyword+"%' WHEN 'Booked' THEN CAST(1 AS INT) WHEN 'Pending' THEN CAST(0 AS INT) WHEN ... THEN ... ELSE ... END' 

Но я предлагаю использовать параметры, как указано в ответе @un-lucky. Здесь гораздо больше можно обсудить с точки зрения передового опыта, поэтому я предлагаю вам взглянуть на следующие статьи:

  • Таблицы поиска: Вы указали, что bkStatus имеет тип INT soy. Я предполагаю, что у вас может быть больше параметров, чем в Booked или Pending, например: Зарезервировано или Отменено. В этом случае ваш фактический код может стать все более неопрятным с каждой добавленной вами опцией.
  • Рекомендации по использованию ADO.NET: Вы не указали, как вы получаете доступ к базе данных с вашего интерфейса. Несмотря на то, что эта статья существует уже много лет, большая часть ее контента по-прежнему актуальна. Я предполагаю, что это может быть полезно.
  • Создание лучшей структуры Entity: Если вы используете Entity Framework для доступа к вашей базе данных.

Надеюсь, что это поможет.

Ответ 8

Сделайте enum для BookingStatus и создайте функцию, которая принимает строку и возвращает значение перечисления. См. Приведенный ниже код.

public enum BookingStatus {
    [Description("Pending")]
    Pending = 0,
    [Description("Booked")]
    Booked = 1
}

Теперь функция следующая,

    public static T GetValueFromDescription<T>(string p_description)
    {
        var type = typeof(T);
        if (!type.IsEnum) throw new InvalidOperationException();
        foreach (var field in type.GetFields())
        {
            var attribute = Attribute.GetCustomAttribute(field,
                typeof(DescriptionAttribute)) as DescriptionAttribute;
            if (attribute != null)
            {
                if (attribute.Description == p_description)
                    return (T)field.GetValue(null);
            }
            else
            {
                if (field.Name == p_description)
                    return (T)field.GetValue(null);
            }
        }
        throw new ArgumentException("Not found.", "description");
        // or return default(T);
    }

Теперь в параметре в sql-запросе вызовите эту функцию с параметром как "Booked" или "Pending", и он вернет enum BookingStatus.Booked. Вы можете легко извлечь из него значение int.

(int)BookingStatus.Booked // will give 1

Ответ 9

Похоже, вы пытаетесь свободно искать среди нескольких столбцов. Это довольно распространенная проблема, и реальное решение можно найти на www.Sommarskog.se по динамическому поиску условия.

Ваше решение выглядит так, как будто оно уязвимо для SQL Injection. Могу ли я предложить вам реализовать что-то похожее на хранимую процедуру search_orders_3?