web-dev-qa-db-fra.com

Où valeur dans la colonne contenant des valeurs délimitées par des virgules

Bonjour, je me demande comment écrire une instruction SQL pour SQL Server 2008 qui sélectionne les entrées dans lesquelles une colonne contient une valeur. Désormais, la valeur dans la colonne est une liste délimitée par des virgules (généralement - il ne peut y avoir qu'une seule entrée (et aucune virgule en tête). ) alors que vérifie-t-il est "cette valeur est-elle contenue quelque part dans la liste?", par exemple:

COLUMN = Cat, Dog, Sparrow, Trout, Cow, Seahorse
Does COLUMN contain Cat? YES
Does COLUMN contain horse? NO
Does COLUMN contain Sheep? NO

ou

COLUMN = Mouse
Does COLUMN contain Hare? NO
Does COLUMN contain Mouse? YES

etc

Je pensais pouvoir utiliser le mot clé "IN" en tant que tel

SELECT id_column FROM table_name WHERE 'Cat' IN COLUMN

mais cela ne fonctionne pas car il semble que vous ne pouvez l'utiliser que pour vérifier si une colonne contient une série de valeurs délimitées par des virgules.

Je ne peux pas non plus utiliser CONTAINS () OR 'LIKE' car, dans l'exemple ci-dessus, les valeurs pour 'horse' seraient renvoyées car la chaîne entière contient horse dans 'Seahorse' et je ne peux pas rechercher l'aiguille plus. une virgule (si je cherche «cheval», la recherche sera «cheval») et si l'entrée se trouve à la fin de la liste? Et je ne peux pas rechercher une virgule plus une aiguille (si je cherche «cheval», la recherche serait «cheval») Comme si l'entrée était la première de la liste? Et je ne peux pas utiliser les deux comme quoi si l'entrée est la seule (unique) entrée?

Merci pour toute aide que tout le monde peut donner.

À votre santé.

41
Neaox

Il y a un scénario délicat. Si je cherche '40' dans la liste '17, 34,400,12 ', alors il trouverait ", 40" et renverrait cette entrée incorrecte. Cela prend en charge toutes les solutions:

WHERE (',' + RTRIM(MyColumn) + ',') LIKE '%,' + @search + ',%'
90
tbaxter120
WHERE
      MyColumn LIKE '%,' + @search + ',%' --middle
      OR
      MyColumn LIKE @search + ',%' --start
      OR
      MyColumn LIKE '%,' + @search --end
      OR 
      MyColumn =  @search --single (good point by Cheran S in comment)
22
gbn
SELECT * FROM TABLENAME WHERE FIND_IN_SET(@search, column)

S'il s'avère que votre colonne a des espaces entre les éléments de la liste, utilisez

SELECT * FROM TABLENAME WHERE FIND_IN_SET(@search, REPLACE(column, ' ', ''))

http://dev.mysql.com/doc/refman/5.0/en/string-functions.html

10
Liquinaut
DECLARE @search VARCHAR(10);
SET @search = 'Cat';

WITH T(C)
AS
(
SELECT 'Cat, Dog, Sparrow, Trout, Cow, Seahorse'
)
SELECT *
FROM T 
WHERE ', ' + C + ',' LIKE '%, ' + @search + ',%'

Cela nécessitera bien sûr une analyse complète du tableau pour chaque recherche.

6
Martin Smith

La meilleure solution dans ce cas consiste à normaliser votre table pour que les valeurs séparées par des virgules se trouvent dans différentes lignes (Première forme normale 1NF) http://en.wikipedia.org/wiki/First_normal_form

Pour cela, vous pouvez implémenter une fonction valorisée de table Nice Split en SQL en utilisant CLR http://bi-tch.blogspot.com/2007/10/sql-clr-net-function-split.html ou en utilisant du SQL simple.

CREATE FUNCTION dbo.Split
(
    @RowData nvarchar(2000),
    @SplitOn nvarchar(5)
)  
RETURNS @RtnValue table 
(
    Id int identity(1,1),
    Data nvarchar(100)
) 
AS  
BEGIN 
    Declare @Cnt int
    Set @Cnt = 1

    While (Charindex(@SplitOn,@RowData)>0)
    Begin
        Insert Into @RtnValue (data)
        Select 
            Data = ltrim(rtrim(Substring(@RowData,1,Charindex(@SplitOn,@RowData)-1)))

        Set @RowData = Substring(@RowData,Charindex(@SplitOn,@RowData)+1,len(@RowData))
        Set @Cnt = @Cnt + 1
    End

    Insert Into @RtnValue (data)
    Select Data = ltrim(rtrim(@RowData))

    Return
END

Ensuite, vous pouvez interroger la sortie normalisée en utilisant cross apply

select distinct a.id_column
from   MyTable a cross apply
       dbo.Split(A.MyCol,',') b
where  b.Data='Cat'
3
pcofre

J'ai trouvé cette réponse sur un autre forum, fonctionne parfaitement . Aucun problème pour trouver 1 s'il y a aussi un 10

WHERE tablename REGEXP "(^|,)@search(,|$)"

Je l'ai trouvé ici

3
Maurits Weebers
select *
from YourTable
where ','+replace(col, ' ', '')+',' like '%,Cat,%'
2
Mikael Eriksson

Je viens juste de savoir à ce sujet lorsque je cherchais une solution à un problème similaire. SQL a un nouveau mot-clé appelé CONTAINS que vous pouvez utiliser .. .. Pour plus de détails, voir http://msdn.Microsoft.com/en-us/library/ms187787.aspx

1
S Ravi Kumar
SELECT * FROM TABLE_NAME WHERE
        (
            LOCATE(',DOG,', CONCAT(',',COLUMN,','))>0 OR
            LOCATE(',CAT,', CONCAT(',',COLUMN,','))>0
        );
0
D3MO

Si vous connaissez l'ID plutôt que les chaînes, utilisez cette approche:

where mylookuptablecolumn IN (myarrayorcommadelimitedarray)

Assurez-vous simplement que myarrayorcommadelimitedarray n'est pas placé entre guillemets. 

fonctionne si vous voulez A OR B, mais pas AND.

0
stu

Bien que la solution astucieuse conseillée par @ tbaxter120 soit bonne, mais j'utilise cette fonction et fonctionne comme un charme, pString est une chaîne délimitée et pDelimiter est un caractère délimiteur:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER FUNCTION [dbo].[DelimitedSplit]
--===== Define I/O parameters
        (@pString NVARCHAR(MAX), @pDelimiter CHAR(1))
RETURNS TABLE WITH SCHEMABINDING AS
 RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 0 up to 10,000...
     -- enough to cover VARCHAR(8000)
  WITH E1(N) AS (
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
                ),                          --10E+1 or 10 rows
       E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
       E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
 cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
                     -- for both a performance gain and prevention of accidental "overruns"
                 SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
                ),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
                 SELECT 1 UNION ALL -- does away with 0 base CTE, and the OR condition in one go!
                 SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
                ),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
                 SELECT s.N1,
                        ---ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
                        ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,50000)
                   FROM cteStart s
                )
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
 SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
        Item       = SUBSTRING(@pString, l.N1, l.L1)
   FROM cteLen l

;

Ensuite, par exemple, vous pouvez l'appeler dans la clause where comme ci-dessous:

WHERE [fieldname] IN (SELECT LTRIM(RTRIM(Item)) FROM [dbo].[DelimitedSplit]('2,5,11', ','))

J'espère que cette aide.

0
QMaster

Où la valeur d'une colonne contenant des valeurs délimitées par des virgules recherche plusieurs délimitées par des virgules 

            declare @d varchar(1000)='-11,-12,10,121'

            set @d=replace(@d,',',',%'' or '',''+a+'','' like ''%,')

            print @d
            declare @d1 varchar(5000)=
            'select * from (
            select ''1,21,13,12'' as a
            union
            select ''11,211,131,121''
            union
            select ''411,211,131,1211'') as t
             where '',''+a+'','' like ''%,'+@d+ ',%'''

             print @d1
             exec (@d1)
0
Rajesh Kumar

Comme vous ne savez pas combien d'entrées délimitées par des virgules, vous pouvez trouver, vous devrez peut-être créer une fonction avec les fonctions SQL Server 'charindex' et 'substring'. Les valeurs renvoyées par la fonction pourraient être utilisées dans une expression "in".

Votre fonction peut être invoquée de manière récursive ou vous pouvez créer une boucle en recherchant des entrées jusqu'à ce que plus aucune entrée ne soit présente dans la chaîne. Chaque appel à la fonction utilise l'index trouvé précédent comme point de départ du prochain appel. Le premier appel commence à 0.

0
Edwin Dalorzo