2009-11-19 4 views
0

J'ai deux tables:Rechercher si un mot de chaîne existe entre deux tables différentes dans un champ délimité par des virgules

table EmployeeTypeA
Nom champ varchar (2000) contient - 'john, sam, doug'

Table EmployeeTypeB
nom champ varchar (2000) contient - eric, sam, allen, stephanie '

Quel est le moyen le plus efficace de retourner un vrai ou faux quand un nom se trouve dans les deux listes à l'aide de MS SQL? Cela doit être fait dans une procédure stockée, donc je ne peux pas utiliser un langage de programmation pour le manipuler en dehors de SQL. Dans cet exemple puisque 'sam' est dans les deux tables, je voudrais retourner un vrai (ou 0, etc.)

Dois-je d'abord séparer le champ délimité par des virgules, puis placer les éléments dans une table temporaire? Ou utiliser des curseurs?

Merci

+0

Quel DB utilisez-vous? –

Répondre

2

séparée le champ délimité par des virgules d'abord dans une table temporaire ou une variable de table. De cette façon, vous pouvez rejoindre ou faire correspondre les lignes avec précision. Faites une rangée pour chaque nom et incluez une colonne clé qui vous aidera à corréler les lignes.

La meilleure façon de le faire est avec une « table d'assistance » comme ceci:

DECLARE @numbers TABLE (number int) 
DECLARE @i int 
SET @i = 1 
WHILE (@i < 1001) 
BEGIN  
INSERT INTO @numbers (number) VALUES (@i)  
SET @i = @i+1 
END 


DECLARE @TestString VARCHAR(200) 
SET @TestString = 'andy,john,mark' 
DECLARE @RowDelimiter VARCHAR(1) 
SET @RowDelimiter=',' 

SELECT SUBSTRING(@[email protected], number, 
    CHARINDEX(@RowDelimiter, @[email protected], number) - number) 
FROM @numbers 
WHERE number <= LEN(@TestString) 
AND SUBSTRING(@RowDelimiter+ @TestString, number, 1) = @RowDelimiter 
ORDER BY number 
-- helper table technique: [email protected] 

le résultat est:

andy 
john 
mark 

Une fois que vous avez les deux tables temporaires, puis faire COMPLET OUTER JOIN et inclure votre "trouvé dans les deux" colonne avec une valeur fixe. Vous obtiendrez la valeur NULL pour les noms non trouvés dans les deux - et vous pouvez traiter la valeur NULL comme la valeur "False".

Pouvez-vous indiquer pourquoi vous avez besoin d'une valeur booléenne pour les correspondances entre les deux tables? Qu'allez-vous en faire ensuite? Parfois, expliquant que cela conduira à de meilleures solutions. Vous pourriez trouver que vous faites des hypothèses à la hâte. Meilleur, Bill.

+0

Salut, Bill. Pouvez-vous donner du code pour la jointure externe complète? Je ne vois pas comment cela fonctionnerait. –

+0

Oui. Le code ne figurera pas dans ce commentaire, donc je posterai une autre réponse et la marquerai comme partie 2. – Newfave

+0

OU mieux encore, corrige le design et sépare-le une fois sur des tables permanentes et utilise-le pour l'avenir. Les champs de données ne doivent jamais stocker de listes délimitées par des virgules. C'est un indicateur qu'une table liée est nécessaire. – HLGEM

1

: Non testé

SELECT COUNT(*) FROM EmployeeTypeA 
WHERE ',' + nameListField + ',' LIKE '%,' + @searchedName + ',%' 

devrait retourner une valeur > 0 si le nom a été trouvé dans l'une des listes de la première table. Faites de même pour la deuxième table et renvoyez true si les deux SELECT ont renvoyé une valeur non nulle. PS: Si vous avez le droit de modifier la conception de la base de données: Faites-le. Une base de données normalisée ne doit pas contenir de listes séparées par des virgules, mais plutôt des sous-tables avec une relation de clé étrangère.

+0

On dirait que cela devrait fonctionner, même si je pourrais comprendre MS SQL biffing sur la complexité du côté gauche de 'like'. – wallyk

1

Voici un script qui crée les deux tables de test et renvoie une liste de noms avec 'True' si le nom se trouve dans les deux tables. Il fonctionne en utilisant une gauche JOIN pour trouver les noms qui sont dans les deux tables ou seulement dans le tableau A. Ce jeu de résultats est un droit à filles fusionnées JOIN pour obtenir les noms qui ne sont dans le tableau B.

DROP TABLE EmployeeTypeA 
DROP TABLE EmployeeTypeB 
GO 

CREATE TABLE EmployeeTypeA 
    (Name VARCHAR(2000)) 
GO 

CREATE TABLE EmployeeTypeB 
    (Name VARCHAR(2000)) 
GO 

INSERT INTO EmployeeTypeA VALUES ('john') 
INSERT INTO EmployeeTypeA VALUES ('sam') 
INSERT INTO EmployeeTypeA VALUES ('doug') 

INSERT INTO EmployeeTypeB VALUES ('eric') 
INSERT INTO EmployeeTypeB VALUES ('sam') 
INSERT INTO EmployeeTypeB VALUES ('allen') 
INSERT INTO EmployeeTypeB VALUES ('stephanie') 
GO 

SELECT 
    eta.Name, 
    CASE 
     WHEN etb.Name IS NULL THEN 'False' 
     ELSE 'True' 
    END 
FROM 
    EmployeeTypeA eta 
    LEFT JOIN EmployeeTypeB etb ON 
     eta.Name = etb.Name 

UNION 

SELECT 
    etb.Name, 
    'False' 
FROM 
    EmployeeTypeA eta 
    RIGHT JOIN EmployeeTypeB etb ON 
     eta.Name = etb.Name 
WHERE 
    eta.Name IS NULL 

GO 
0

Ceci est la partie 2 de moi ci-dessus afin que je puisse ajouter du code supplémentaire. Cette partie explique comment obtenir votre valeur booléenne pour savoir s'il existe ou non une correspondance entre les tables après avoir extrait vos noms dans des lignes séparées.

DECLARE @LeftTable TABLE (thisid int, thisname varchar(50)) 

INSERT INTO @LeftTable VALUES (1, 'andy') 
INSERT INTO @LeftTable VALUES (2, 'bill') 
INSERT INTO @LeftTable VALUES (3, 'zed') 

DECLARE @RightTable TABLE (thisid int, thisname varchar(50)) 

INSERT INTO @RightTable VALUES (1, 'chris') 
INSERT INTO @RightTable VALUES (2, 'bill') 
INSERT INTO @RightTable VALUES (3, 'zed') 

SELECT 
a.thisname AS theleftname, 
b.thisname AS therightname, 
CASE 
    WHEN (ISNULL(a.thisname,'') = '' OR ISNULL(b.thisname,'') = '') THEN 'False' 
    ELSE 'True' 
END  
AS namematches 
FROM @LeftTable a 
FULL OUTER JOIN @RightTable b 
ON a.thisname = b.thisname 
-- www.caliberwebgroup.com 

Voici les résultats:

theleftname therightname namematches 
NULL chris False 
bill bill True 
zed  zed  True 
andy NULL False 
0

Vous pouvez écrire une fonction table qui prend dans une chaîne séparées par des virgules et retourne une table (une colonne) de chaîne.

Create FUNCTION [dbo].[SplitStrings] 
(
    @StringList varchar(max) 
) 
RETURNS 
@Outputable table 
(
    ParsedItem varchar(2000) 
) 

as 

-- you can have a while loop here to populate the table. 

Cela rendra également votre code réutilisable. Mais souvenez-vous que cela peut être un goulot d'étranglement au niveau des performances si vous utilisez beaucoup de lignes ... Il fonctionne pour chaque ligne.

Mis à jour !!!

Oui, bien sûr, vous pouvez utiliser la jointure d'une autre réponse une fois que vous obtenez les tables.

0

Essayez cette

declare @EmployeeTypeA table(Name VARCHAR(2000)) 
insert into @EmployeeTypeA select 'john,sam,doug' 
declare @EmployeeTypeB table(Name VARCHAR(2000)) 
insert into @EmployeeTypeB select 'eric,sam,allen,stephanie' 

--program commence

declare @xA xml 
declare @xB xml 
select @xA = '<i>' + REPLACE(Name, ',', '</i><i>') + '</i>' from @EmployeeTypeA 
select @xB = '<i>' + REPLACE(Name, ',', '</i><i>') + '</i>' from @EmployeeTypeB 

select 
EmployeeTypeA 
,EmployeeTypeB  

from (
SELECT 
    EmployeeTypeA 
    ,i.value('.', 'VARCHAR(MAX)') EmployeeTypeB 
    FROM @xB.nodes('//i') x(i) 
    cross apply(
    SELECT i.value('.', 'VARCHAR(MAX)') EmployeeTypeA 
    FROM @xA.nodes('//i') x(i)) Y) Res(EmployeeTypeA,EmployeeTypeB) 
    where EmployeeTypeA = EmployeeTypeB 

Sortie:

EmployeeTypeA EmployeeTypeB

sam    sam 
Questions connexes