Filtrage avec des zones à sélection multiple avec SQL Server
-
03-07-2019 - |
Question
Je dois filtrer les ensembles de résultats du serveur SQL en fonction des sélections effectuées dans une zone de liste à sélection multiple. J'ai déjà eu l'idée de créer un instrument pour déterminer si la valeur de la ligne existe dans les valeurs de filtre sélectionnées, mais cela risque de donner lieu à des correspondances partielles (par exemple, Car Match Carpet).
J'ai également scié la chaîne en une table et joint / mis en correspondance en fonction de cela, mais j'ai des réserves quant à la manière dont cela va se dérouler.
Etant donné qu'il s'agit d'une tâche apparemment courante, je me tourne vers la communauté Stack Overflow pour obtenir ses commentaires et peut-être quelques suggestions sur l'approche la plus couramment utilisée pour résoudre ce problème.
La solution
J'ai résolu ce problème en écrivant une fonction table (nous utilisons 2005) qui prend une chaîne délimitée et retourne une table. Vous pouvez ensuite vous y joindre ou utiliser WHERE EXISTS ou WHERE x IN. Nous n'avons pas encore effectué de tests de résistance complets, mais avec une utilisation limitée et des ensembles d'éléments relativement petits, je pense que les performances devraient être correctes.
Vous trouverez ci-dessous l’une des fonctions. J'en ai aussi une écrite pour accepter spécifiquement une liste délimitée d'INT pour les valeurs d'ID dans les tables de consultation, etc.
Une autre possibilité consiste à utiliser LIKE avec les délimiteurs pour vous assurer que les correspondances partielles sont ignorées, mais que vous ne pouvez pas utiliser d'index avec cela, les performances seront donc médiocres pour toute table volumineuse. Par exemple:
SELECT
my_column
FROM
My_Table
WHERE
@my_string LIKE '%|' + my_column + '|%'
.
/*
Name: GetTableFromStringList
Description: Returns a table of values extracted from a delimited list
Parameters:
@StringList - A delimited list of strings
@Delimiter - The delimiter used in the delimited list
History:
Date Name Comments
---------- ------------- ----------------------------------------------------
2008-12-03 T. Hummel Initial Creation
*/
CREATE FUNCTION dbo.GetTableFromStringList
(
@StringList VARCHAR(1000),
@Delimiter CHAR(1) = ','
)
RETURNS @Results TABLE
(
String VARCHAR(1000) NOT NULL
)
AS
BEGIN
DECLARE
@string VARCHAR(1000),
@position SMALLINT
SET @StringList = LTRIM(RTRIM(@StringList)) + @Delimiter
SET @position = CHARINDEX(@Delimiter, @StringList)
WHILE (@position > 0)
BEGIN
SET @string = LTRIM(RTRIM(LEFT(@StringList, @position - 1)))
IF (@string <> '')
BEGIN
INSERT INTO @Results (String) VALUES (@string)
END
SET @StringList = RIGHT(@StringList, LEN(@StringList) - @position)
SET @position = CHARINDEX(@Delimiter, @StringList, 1)
END
RETURN
END
Autres conseils
J'ai eu l'idée de faire un instring pour déterminer si la valeur de la ligne existe dans les valeurs de filtre sélectionnées, mais c'est sujette à des allumettes partielles (par exemple, allumettes de voiture moquette)
Il me semble que vous n'incluez pas d'ID unique, ni éventuellement de clé primaire dans les valeurs de votre zone de liste. Idéalement, chaque option aura un identifiant unique qui correspond à une colonne de la table sur laquelle vous recherchez. Si votre zone de liste était comme ci-dessous, vous pourrez alors filtrer spécifiquement pour les voitures car vous obtiendrez la valeur unique 3.
<option value="3">Car</option>
<option value="4">Carpret</option>
Ensuite, vous créez simplement une clause where qui vous permettra de trouver les valeurs dont vous avez besoin.
Mis à jour, pour répondre au commentaire.
Comment ferais-je la jointure associée considérant que l'utilisateur peut sélectionner et un nombre arbitraire d'options de la liste de sélection? SELECT * FROM tblTable JOIN tblOptions ON tblTable.FK =? le problème ici est que je dois rejoindre le plusieurs valeurs.
J'ai répondu à une question similaire ici .
Une méthode consiste à créer une table temporaire et à ajouter chaque option sélectionnée sous forme de ligne à la table temporaire. Ensuite, vous feriez simplement une jointure à votre table temporaire.
Si vous voulez simplement créer votre SQL dynamiquement, vous pouvez faire quelque chose comme ça.
SELECT * FROM tblTable WHERE option IN (selected_option_1, selected_option_2, selected_option_n)
J'ai constaté qu'une fonction de table CLR qui prend votre chaîne délimitée et appelle Split sur la chaîne (renvoyant le tableau sous la forme IEnumerable) est plus performante que tout ce qui est écrit en T-SQL (elle commence à se décomposer lorsque vous avez environ un million d'éléments dans la liste délimitée, mais c'est beaucoup plus loin que la solution T-SQL).
Ensuite, vous pourrez rejoindre la table ou vérifier avec EXISTS.