Поиск столбца 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?
Ответы
Ответ 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?