J'ai WebService qui reçoit un tableau d'ints .. Je voudrais faire la déclaration select comme suit mais continuer à avoir des erreurs Dois-je changer le tableau en chaîne?
[WebMethod]
public MiniEvent[] getAdminEvents(int buildingID, DateTime startDate)
{
command.CommandText = @"SELECT id,
startDateTime, endDateTime From
tb_bookings WHERE buildingID IN
(@buildingIDs) AND startDateTime <=
@fromDate";
SqlParameter buildID = new SqlParameter("@buildingIDs", buildingIDs);
}
Vous ne pouvez pas (malheureusement) faire ça. Un paramètre Sql ne peut être qu'une seule valeur, vous devez donc faire:
WHERE buildingID IN (@buildingID1, @buildingID2, @buildingID3...)
Ce qui, bien sûr, vous oblige à connaître le nombre d'identifiants de bâtiment ou à construire dynamiquement la requête.
Pour contourner le problème *, j'ai effectué les opérations suivantes:
WHERE buildingID IN (@buildingID)
command.CommandText = command.CommandText.Replace(
"@buildingID",
string.Join(buildingIDs.Select(b => b.ToString()), ",")
);
qui remplacera le texte de la déclaration par les chiffres, pour aboutir à quelque chose comme:
WHERE buildingID IN (1,2,3,4)
Vous allez d'abord avoir besoin d'une fonction et d'un sproc. La fonction divisera vos données et retournera une table:
CREATE function IntegerCommaSplit(@ListofIds nvarchar(1000))
returns @rtn table (IntegerValue int)
AS
begin
While (Charindex(',',@ListofIds)>0)
Begin
Insert Into @Rtn
Select ltrim(rtrim(Substring(@ListofIds,1,Charindex(',',@ListofIds)-1)))
Set @ListofIds = Substring(@ListofIds,Charindex(',',@ListofIds)+len(','),len(@ListofIds))
end
Insert Into @Rtn
Select ltrim(rtrim(@ListofIds))
return
end
Ensuite, vous avez besoin d'un sproc pour l'utiliser:
create procedure GetAdminEvents
@buildingids nvarchar(1000),
@startdate datetime
as
SELECT id,startDateTime, endDateTime From
tb_bookings t INNER JOIN
dbo.IntegerCommaSplit(@buildingids) i
on i.IntegerValue = t.id
WHERE startDateTime <= @fromDate
Enfin, votre code:
[WebMethod]
public MiniEvent[] getAdminEvents(int[] buildingIDs, DateTime startDate)
command.CommandText = @"exec GetAdminEvents";
SqlParameter buildID= new SqlParameter("@buildingIDs", buildingIDs);
Cela va bien au-delà de ce que votre question a demandé, mais cela fera tout ce dont vous avez besoin.
Remarque: si vous transmettez quelque chose qui n'est pas un int, la fonction de base de données entière échouera. Je laisse la gestion des erreurs pour cela comme un exercice pour l'utilisateur final.
NOTE: Je ne suis généralement pas pour utiliser des requêtes non paramétrées. DANS CETTE INSTANCE, cependant, étant donné que nous avons affaire à un tableau d’entiers, vous pourriez faire une telle chose et ce serait plus efficace. Cependant, étant donné que tout le monde semble vouloir déclasser la réponse parce que cela ne répond pas à leurs critères de conseil valide, je vais soumettre une autre réponse qui exécute horriblement mais qui fonctionnera probablement dans LINK2SQL.
En supposant, comme le stipule votre question, que vous disposiez d'un tableau d'ints, vous pouvez utiliser le code suivant pour renvoyer une chaîne contenant une liste délimitée par des virgules que SQL accepterait:
private string SQLArrayToInString(Array a)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < a.GetUpperBound(0); i++)
sb.AppendFormat("{0},", a.GetValue(i));
string retVal = sb.ToString();
return retVal.Substring(0, retVal.Length - 1);
}
Ensuite, je vous conseillerais de ne pas essayer de paramétrer la commande étant donné qu’il s’agit d’un tableau d’ints et d’utiliser simplement:
command.CommandText = @"SELECT id,
startDateTime, endDateTime From
tb_bookings WHERE buildingID IN
(" + SQLArrayToInString(buildingIDs) + ") AND startDateTime <=
@fromDate";
Une méthode XML ultra-rapide qui ne nécessite aucun code non sécurisé ni fonction définie par l'utilisateur:
Vous pouvez utiliser une procédure stockée et transmettre la liste des ID de bâtiment séparés par des virgules:
Declare @XMLList xml
SET @XMLList=cast('<i>'+replace(@buildingIDs,',','</i><i>')+'</i>' as xml)
SELECT x.i.value('.','varchar(5)') from @XMLList.nodes('i') x(i))
Tout le mérite revient au gourou Blog de Brad Schulz
Visitez Procédure stockée T-SQL qui accepte plusieurs valeurs Id pour des idées sur la façon de procéder.
J'utilise cette approche et travaille pour moi.
Ma variable act = ma liste d'ID à la chaîne.
act = "1, 2, 3, 4"
command = new SqlCommand("SELECT x FROM y WHERE x.id IN (@actions)", conn);
command.Parameters.AddWithValue("@actions", act);
command.CommandText = command.CommandText.Replace("@actions", act);
[WebMethod]
public MiniEvent [] getAdminEvents (int buildingIDDateDate Date)
...
SqlParameter buildID = new SqlParameter ("@ buildingIDs", iD de bâtiment)
Peut-être que je suis trop détaillé, mais cette méthode accepte un seul int, pas un tableau d'ints. Si vous prévoyez de passer dans un tableau, vous devrez mettre à jour la définition de votre méthode pour créer un tableau int. Une fois que vous obtenez ce tableau, vous devrez le convertir en chaîne si vous envisagez de l’utiliser dans une requête SQL.
Voici une solution Linq que j'ai imaginée. Tous les éléments de la liste seront automatiquement insérés en tant que paramètres @ item0, @ item1, @ item2, @ item3, etc.
[WebMethod]
public MiniEvent[] getAdminEvents(Int32[] buildingIDs, DateTime startDate)
{
// Gets a list with numbers from 0 to the max index in buildingIDs,
// then transforms it into a list of strings using those numbers.
String idParamString = String.Join(", ", (Enumerable.Range(0, buildingIDs.Length).Select(i => "@item" + i)).ToArray());
command.CommandText = @"SELECT id,
startDateTime, endDateTime From
tb_bookings WHERE buildingID IN
(" + idParamString + @") AND startDateTime <=
@fromDate";
// Reproduce the same parameters in idParamString
for (Int32 i = 0; i < buildingIDs.Length; i++)
command.Parameters.Add(new SqlParameter ("@item" + i, buildingIDs[i]));
command.Parameters.Add(new SqlParameter("@fromDate", startDate);
// the rest of your code...
}
Vous pouvez utiliser ceci. Exécutez dans SQLServer pour créer une fonction sur votre base de données (une seule fois):
IF EXISTS(
SELECT *
FROM sysobjects
WHERE name = 'FN_RETORNA_ID_FROM_VARCHAR_TO_TABLE_INT')
BEGIN
DROP FUNCTION FN_RETORNA_ID_FROM_VARCHAR_TO_TABLE_INT
END
GO
CREATE FUNCTION [dbo].FN_RETORNA_ID_FROM_VARCHAR_TO_TABLE_INT (@IDList VARCHAR(8000))
RETURNS
@IDListTable TABLE (ID INT)
AS
BEGIN
DECLARE
--@IDList VARCHAR(100),
@LastCommaPosition INT,
@NextCommaPosition INT,
@EndOfStringPosition INT,
@StartOfStringPosition INT,
@LengthOfString INT,
@IDString VARCHAR(100),
@IDValue INT
--SET @IDList = '11,12,113'
SET @LastCommaPosition = 0
SET @NextCommaPosition = -1
IF LTRIM(RTRIM(@IDList)) <> ''
BEGIN
WHILE(@NextCommaPosition <> 0)
BEGIN
SET @NextCommaPosition = CHARINDEX(',',@IDList,@LastCommaPosition + 1)
IF @NextCommaPosition = 0
SET @EndOfStringPosition = LEN(@IDList)
ELSE
SET @EndOfStringPosition = @NextCommaPosition - 1
SET @StartOfStringPosition = @LastCommaPosition + 1
SET @LengthOfString = (@EndOfStringPosition + 1) - @StartOfStringPosition
SET @IDString = SUBSTRING(@IDList,@StartOfStringPosition,@LengthOfString)
IF @IDString <> ''
INSERT @IDListTable VALUES(@IDString)
SET @LastCommaPosition = @NextCommaPosition
END --WHILE(@NextCommaPosition <> 0)
END --IF LTRIM(RTRIM(@IDList)) <> ''
RETURN
ErrorBlock:
RETURN
END --FUNCTION
Après avoir créé la fonction, vous devez l'appeler dans votre code:
command.CommandText = @"SELECT id,
startDateTime, endDateTime From
tb_bookings WHERE buildingID IN
(SELECT ID FROM FN_RETORNA_ID_FROM_VARCHAR_TO_TABLE_INT(@buildingIDs))) AND startDateTime <=
@fromDate";
command.Parameters.Add(new SqlParameter(){
DbType = DbType.String,
ParameterName = "@buildingIDs",
Value = "1,2,3,4,5" //Enter the parameters here separated with commas
});
Cette fonction récupère les virgules internes dans "tableau" et crée une table avec cette valeur sous la forme int, appelée ID. Lorsque cette fonction est sur votre base de données, vous pouvez l'utiliser dans n'importe quel projet.
Merci à Microsoft MSDN.
Igo S Ventura
Microsoft MVA
Sistema Ari de Sá
P.S .: Je viens du Brésil. Excuse mon anglais ... XD