J'ai une vue View_Booking
dans SQL Server 2014:
bkID bkSlot bkStatus
---- ------ --------
2 Lunch 1
4 Lunch 1
6 Dinner 0
7 Lunch 1
En c #, j'ai utilisé une grille et jeté bkStatus
dans une chaîne comme:
<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
Maintenant, je recherche dans View en utilisant cette requête:
SELECT * FROM View_Booking
WHERE CAST(bkID AS NVARCHAR(MAX)) LIKE '%" + keyword + "%'
OR bkSlot LIKE '%"+keyword+"%'
OR bkStatus LIKE << ? >>
Mais je ne sais pas comment rechercher bkStatus
qui est passé comme chaîne à partir de c # alors qu'il s'agit d'un int en sql?
La requête que vous avez fournie doit être optimisée:
Tout d'abord, l'utilisation de CAST(bkID AS NVARCHAR(MAX))
affectera les performances de la requête, car elle n'utilisera aucun index, la conversion en NVARCHAR(MAX)
diminuera également les performances.
bkStatus
est une colonne numérique, vous devez donc utiliser l'opérateur =
et comparer avec les valeurs numériques (0 or 1 or ...)
, les valeurs de texte fournies sont également définies dans la balise asp
pas dans la base de données, ils sont donc utilisés au niveau de l'application et non au niveau des données.
si vous utilisez CAST(bkID AS NVARCHAR(MAX))
pour rechercher la colonne bkid
qui contient un chiffre spécifique (ex: recherchez 1
-> résultat 1
, 10
, 11
, ...), puis essayez de diffuser à une taille spécifique (ex: CAST(bkID as NVARCHAR(10)
)
Il est recommandé d'utiliser des requêtes paramétrées pour de meilleures performances et pour empêcher les attaques par injection SQL . regardez @ réponse malchanceuse
Vous pouvez utiliser un objet dictionnaire pour stocker les valeurs d'ID liées aux mots clés
Remarque: L'utilisation de CAST et Like n'utilisera aucun index, cet exemple est basé sur vos besoins (j'ai essayé de combiner les recommandations que j'ai fournies avec d'autres recommandations)
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();
}
}
De plus, si BkID est une colonne entière, il vaut mieux l'utiliser
sqlCommand.Parameters.Add("@bkID", SqlDbType.Int).value = (Int)keyword ;
Vous avez donc besoin d'une boîte de recherche dans laquelle l'utilisateur peut rechercher en utilisant bkID
, bkSlot
ou bkStatus
, si le texte de recherche est Booked
ou Pending
nous devons ajouter le filtre pour bkStatus
qui sera un champ entier dans la base de données. droite? Peu de choses que je dois mentionner ici sont les tilisation de using
ainsi que le paramétrage des requêtes pour une manière d'exécution plus intelligente et plus sûre. Je voudrais donc suggérer de construire et d'exécuter la requête comme suit:
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();
}
}
Veuillez noter ce qui suit:
book
, Pend
etc., alors vous devez modifier la condition en conséquence en utilisant .Contains()
ou .StartsWith()
à la place pour .ToLower()
-1
pour éviter le filtre basé sur bkStatus
pour toutes les autres valeursVous pouvez utiliser la fonction declare
pour créer une table temporaire qui a une liste de bkStatus
.
Il vous sera plus facile de créer une requête en utilisant bkstatus
comme clé étrangère. Après cela, vous n'avez plus besoin d'utiliser la fonction cast
ou like
. Ce sera un peu inefficace.
Vous pouvez essayer ce code ci-dessous:
declare @bkstatus table (number int primary key , bkstatus varchar(10) )
insert into @bkstatus (number , bkstatus)
values ( 0 , 'Pending'), (1 , 'Booked')
puis en utilisant cette requête:
SELECT * FROM View_Booking v
INNER JOIN @bkstatus b on v.bkstatus = b.number
WHERE b.bkstatus = @keyword
Juste une autre option utilisant CHOOSE () pour décoder le bkStatus et TRY_CONVERT () pour tester bkID.
Exemple
Declare @KeyWord varchar(50) = 'Pending';
Select *
From View_Booking
Where bkID = try_convert(int,@KeyWord)
or bkSlot like '%'+@KeyWord+'%'
or choose(bkStatus+1,'Pending','Booked')=@KeyWord
retourne
bkID bkSlot bkStatus
6 Dinner 0
Si keyword
sera le nom d'état et non l'ID d'état, je créerais une table BookingStatus
, y aurais des colonnes bkStatus
et bkStatusTitle
et les joindrais à la View_Booking
. Vous pourriez alors facilement faire LIKE sur bkStatusTitle
.
SELECT * FROM View_Booking
WHERE CAST(bkID AS NVARCHAR(16)) LIKE '%' + @keyword + '%'
OR bkSlot LIKE '%' + @keyword + '%'
OR bkStatusTitle LIKE '%' + @keyword + '%'
Si keyword
sera une représentation sous forme de chaîne de bkStatus
, je verrais simplement si les valeurs sont les mêmes.
En remarque, c'est une mauvaise idée de construire vos requêtes SQL concaténant les entrées utilisateur comme '%' + keyword + '%'
. Ceci est ouvert aux attaques par injection SQL. Il est préférable d'utiliser des paramètres SQL pour transmettre les entrées utilisateur aux requêtes SQL. En utilisant '%' + @keyword + '%'
dans le bit SQL et en C # quelque chose comme l'exemple ci-dessous serait beaucoup plus sûr.
sqlCommand.Parameters.Add("@keyword", SqlDbType.VarChar, 1000);
sqlCommand.Parameters["@keyword"].Value = searchText;
Les requêtes paramétrées vous offrent également un avantage du même texte de requête pour plusieurs requêtes, ce qui permet à SQL Server de mettre en cache les plans d'exécution SQL et de les réutiliser, offrant des performances légèrement meilleures.
Votre bkStatus est un entier. Dans la vue, vous traduisez la valeur entière en une chaîne significative pour l'utilisateur. Jusque-là, tout va bien. Maintenant, pour que l'utilisateur puisse rechercher, il vous suffit d'inverser vos traductions de chaînes en entier et de rechercher des entiers.
Garder les choses simples
searchStatusKey = yourVariableHodingTheString == "Booked" ? 1 : 0;
Cependant, pour éviter les bogues fastidieux et les mises à niveau indolores du code à plusieurs endroits (disons parce qu'ils ont décidé d'y ajouter un autre statut), je recommanderais une table de traduction ici. Quelque chose comme HashMap (tableau associatif).
var statusMap = new Dictionary<int, string> {
{ 0, "Pending" },
{ 1, "Booked" },
/* can always add more pairs in the future as needed */
};
Maintenant, en supposant que votre paramètre se trouve dans une variable appelée searchStatus
searchStatusKey = statusMap.FirstOrDefault(x => x.Value == searchStatus).Key;
Maintenant, fournissez comme paramètre bkStatus dans la partie where la valeur searchStatusKey
et vous avez terminé.
select * from View_Booking where bkStatus = << ? >>
Je vais me concentrer sur cette partie de votre question (est-ce la question elle-même?):
Mais je ne sais pas comment rechercher bkStatus qui est passé en tant que chaîne à partir de c # alors qu'il s'agit d'un int en sql?
Une façon de gérer cela dans [~ # ~] sql [~ # ~] est à l'aide de CASE clause. Dans votre cas spécifique, vous pourriez (ne signifie pas devrait) faire quelque chose comme:
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'
Mais je suggère l'utilisation de paramètres comme indiqué dans la réponse @ un-lucky . Il y a beaucoup plus que nous pourrions discuter en termes de meilleures pratiques ici, donc je vous suggère de jeter un œil aux articles suivants:
bkStatus
est de type INT
soy Je suppose que vous pourriez avoir plus d'options que Réservé ou En attente, par exemple: Réservé ou Annulé. Dans ce cas, votre code réel peut devenir de plus en plus désordonné avec chaque option que vous ajoutez.J'espère que ça aide.
Créez un enum
pour BookingStatus
et créez une fonction qui accepte la chaîne et renvoie la valeur enum. Voir le code ci-dessous.
public enum BookingStatus {
[Description("Pending")]
Pending = 0,
[Description("Booked")]
Booked = 1
}
Maintenant, la fonction est comme ci-dessous,
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);
}
Maintenant, dans le paramètre de la requête SQL, appelez cette fonction avec le paramètre comme "Booked"
ou "Pending"
et il renverra enum BookingStatus.Booked
. Vous pouvez facilement en extraire la valeur int
.
(int)BookingStatus.Booked // will give 1
Il semble que vous essayiez de rechercher librement parmi plusieurs colonnes. C'est un problème assez courant, et la vraie solution peut être trouvée sur www.Sommarskog.se sur les conditions de recherche dynamique.
Votre solution semble vulnérable à l'injection SQL. Puis-je vous suggérer d'implémenter quelque chose de similaire à la procédure stockée search_orders_3?