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é.
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 + ',%'
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)
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
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.
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'
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(,|$)"
select *
from YourTable
where ','+replace(col, ' ', '')+',' like '%,Cat,%'
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
SELECT * FROM TABLE_NAME WHERE
(
LOCATE(',DOG,', CONCAT(',',COLUMN,','))>0 OR
LOCATE(',CAT,', CONCAT(',',COLUMN,','))>0
);
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.
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.
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)
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.