Question

Existe-t-il un moyen simple de gérer le passage d'une liste d'identifiants en tant que paramètre à une procédure stockée ?

Par exemple, je veux que les départements 1, 2, 5, 7, 20 soient renvoyés par ma procédure stockée.Dans le passé, j'ai transmis une liste d'identifiants délimités par des virgules, comme le code ci-dessous, mais je me sens vraiment sale en le faisant.

SQL Server 2005 est ma seule limitation applicable, je pense.

create procedure getDepartments
  @DepartmentIds varchar(max)
as
  declare @Sql varchar(max)     
  select @Sql = 'select [Name] from Department where DepartmentId in (' + @DepartmentIds + ')'
  exec(@Sql)
Était-ce utile?

La solution

Erland Sommarskog reste la réponse faisant autorité à cette question depuis 16 ans : Tableaux et listes dans SQL Server.

Il existe au moins une douzaine de façons de transmettre un tableau ou une liste à une requête ;chacun a ses propres avantages et inconvénients.

  • Paramètres table.SQL Server 2008 et versions ultérieures uniquement, et probablement la plus proche d'une « meilleure » approche universelle.
  • La méthode itérative.Passez une chaîne délimitée et parcourez-la.
  • Utiliser le CLR.SQL Server 2005 et versions ultérieures à partir des langages .NET uniquement.
  • XML.Très bien pour insérer de nombreuses lignes ;peut être excessif pour les SELECT.
  • Tableau des nombres.Performances/complexité supérieures à la simple méthode itérative.
  • Éléments de longueur fixe.La longueur fixe améliore la vitesse sur la chaîne délimitée
  • Fonction des nombres.Variations du tableau des nombres et de longueur fixe où les nombres sont générés dans une fonction plutôt que extraits d'un tableau.
  • Expression de table commune récursive (CTE).SQL Server 2005 et supérieur, toujours pas trop complexe et plus performant que la méthode itérative.
  • SQL dynamique.Peut être lent et avoir des implications en matière de sécurité.
  • Passer la liste comme De nombreux paramètres.Fastidieux et sujet aux erreurs, mais simple.
  • Méthodes vraiment lentes.Méthodes qui utilisent charindex, patindex ou LIKE.

Je ne peux vraiment pas en recommander assez lire l'article pour en savoir plus sur les compromis entre toutes ces options.

Autres conseils

Oui, votre solution actuelle est sujette aux attaques par injection SQL.

La meilleure solution que j'ai trouvée est d'utiliser une fonction qui divise le texte en mots (il y en a quelques-uns publiés ici, ou vous pouvez utiliser celui-ci de mon blog), puis joignez-le à votre table.Quelque chose comme:

SELECT d.[Name]
FROM Department d
    JOIN dbo.SplitWords(@DepartmentIds) w ON w.Value = d.DepartmentId

Vous pouvez utiliser XML.

Par exemple.

declare @xmlstring as  varchar(100) 
set @xmlstring = '<args><arg value="42" /><arg2>-1</arg2></args>' 

declare @docid int 

exec sp_xml_preparedocument @docid output, @xmlstring

select  [id],parentid,nodetype,localname,[text]
from    openxml(@docid, '/args', 1) 

La commande sp_xml_preparedocument est intégré.

Cela produirait le résultat :

id  parentid    nodetype    localname   text
0   NULL        1           args        NULL
2   0           1           arg         NULL
3   2           2           value       NULL
5   3           3           #text       42
4   0           1           arg2        NULL
6   4           3           #text       -1

qui contient tout (plus ?) ce dont vous avez besoin.

Une méthode que vous pourriez envisager si vous comptez travailler beaucoup avec les valeurs consiste d'abord à les écrire dans une table temporaire.Ensuite, vous vous y joignez comme d’habitude.

De cette façon, vous n'analysez qu'une seule fois.

Il est plus simple d'utiliser l'un des UDF « Split », mais tant de personnes en ont posté des exemples que j'ai pensé que j'emprunterais une voie différente ;)

Cet exemple créera une table temporaire sur laquelle vous pourrez vous joindre (#tmpDept) et la remplira avec les identifiants de service que vous avez transmis.Je suppose que vous les séparez par des virgules, mais vous pouvez – bien sûr – le changer comme vous le souhaitez.

IF OBJECT_ID('tempdb..#tmpDept', 'U') IS NOT NULL
BEGIN
    DROP TABLE #tmpDept
END

SET @DepartmentIDs=REPLACE(@DepartmentIDs,' ','')

CREATE TABLE #tmpDept (DeptID INT)
DECLARE @DeptID INT
IF IsNumeric(@DepartmentIDs)=1
BEGIN
    SET @DeptID=@DepartmentIDs
    INSERT INTO #tmpDept (DeptID) SELECT @DeptID
END
ELSE
BEGIN
        WHILE CHARINDEX(',',@DepartmentIDs)>0
        BEGIN
            SET @DeptID=LEFT(@DepartmentIDs,CHARINDEX(',',@DepartmentIDs)-1)
            SET @DepartmentIDs=RIGHT(@DepartmentIDs,LEN(@DepartmentIDs)-CHARINDEX(',',@DepartmentIDs))
            INSERT INTO #tmpDept (DeptID) SELECT @DeptID
        END
END

Cela vous permettra de transmettre un identifiant de service, plusieurs identifiants avec des virgules entre eux, ou même plusieurs identifiants avec des virgules et des espaces entre eux.

Donc si vous faisiez quelque chose comme :

SELECT Dept.Name 
FROM Departments 
JOIN #tmpDept ON Departments.DepartmentID=#tmpDept.DeptID
ORDER BY Dept.Name

Vous verriez les noms de tous les numéros de service que vous avez transmis...

Encore une fois, cela peut être simplifié en utilisant une fonction pour remplir la table temporaire...Je l'ai principalement fait sans, juste pour tuer un peu l'ennui :-P

-Kevin Fairchild

Une méthode XML ultra-rapide, si vous souhaitez utiliser une procédure stockée et transmettre la liste des ID de service séparés par des virgules :

Declare @XMLList xml
SET @XMLList=cast('<i>'+replace(@DepartmentIDs,',','</i><i>')+'</i>' as xml)
SELECT x.i.value('.','varchar(5)') from @XMLList.nodes('i') x(i))

Tout le mérite revient à Guru Le blog de Brad Schulz

Essaye celui-là:

@list_of_params varchar(20) -- value 1, 2, 5, 7, 20 

SELECT d.[Name]
FROM Department d
where @list_of_params like ('%'+ CONVERT(VARCHAR(10),d.Id)  +'%')

très simple.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top