Question

J'ai ALPHA table avec 2 champs GroupId, membres:

GroupId | Member;
A1----------A;
A1----------B; 
A1----------C;
A2----------A;
A2----------B;
A3----------A;
A3----------D;
A3----------E;

Objectif: Compte tenu de l'entrée - A, B, C - Je dois interroger la table pour trouver si un GroupId existe pour cet ensemble exact des membres. Donc, ce que je prévois de faire:

  1. interroger la table pour tous groupids dont le nombre est de 3 (depuis mon INPT est A, B, C ..Je knw ses 3)
  2. Cela me donnera A1, A3. Maintenant, interroger cet ensemble pour values..which sera membre correspondant exactement A1 me donner.

Je prévois d'écrire une procédure stockée et pour atteindre l'objectif en quelque sorte. Mais, ma question peut-il être réalisé en une seule requête ... une seule autojointure peut-être.

Clarification: L'ensemble des (A, B, C) est unique à A1. Et si donner une entrée (A, B, C, D) la requête ne rentrerais pas A1.

Était-ce utile?

La solution

Les réponses données supposent bien que si le champ est membre unique pour toute GroupID donnée. Dans le travail, je l'ai fait ce n'est pas le cas. Et si le groupe a ce que vous cherchez, plus un peu supplémentaire, vous devez exlucde ce groupe.

SELECT
   [Alpha].GroupID
FROM
   [Alpha]
GROUP BY
   [Alpha].GroupID
HAVING
       SUM(CASE WHEN [alpha].Member IN ('A','B','C') THEN 1 ELSE 0 END) = 3
   AND MIN(CASE WHEN [alpha].Member IN ('A','B','C') THEN 1 ELSE 0 END) = 1

Vous pouvez également remplacer la clause IN avec une jointure sur une table contenant les membres que vous recherchez ...

SELECT
   [Alpha].GroupID
FROM
   [Alpha]
LEFT JOIN
   [Search]
       ON [Search].Member
GROUP BY
   [Alpha].GroupID
HAVING
       SUM(CASE WHEN [alpha].Member = [search].Member THEN 1 ELSE 0 END) = (SELECT COUNT(*) FROM [search])
   AND MIN(CASE WHEN [alpha].Member = [search].Member THEN 1 ELSE 0 END) = 1

Autres conseils

SELECT GroupID
  FROM ALPHA
 WHERE Member IN ('A', 'B', 'C')
 GROUP BY GroupID
HAVING COUNT(*) = 3

Cela repose sur vous écrire la liste des membres dans la clause IN et fixer le nombre d'entrées (distinctes) dans la liste des membres dans la clause HAVING. Si vous ne pouvez pas générer le SQL thusly, alors vous devez travailler plus dur.


Comme il est indiqué dans un commentaire au début, il se fonde également sur l'interprétation que vous voulez que les groupes où les trois A, B, C (et peut-être quelques autres) sont membres du groupe. Une façon, pas nécessairement la meilleure façon, d'obtenir « où le groupe contient exactement trois personnes, à savoir A, B, C », est d'utiliser:

SELECT GroupID
  FROM ALPHA A1
 WHERE Member IN ('A', 'B', 'C')
   AND 3 = (SELECT COUNT(*) FROM ALPHA A2 WHERE A2.GroupID = A1.GroupID)
 GROUP BY GroupID
HAVING COUNT(*) = 3

vérifie explicitement que le nombre total de personnes dans le groupe est 3 et que les membres sont A, B et C (en supposant qu'il existe une contrainte unique sur Alpha (GroupID, membre) de sorte qu'un membre ne peut pas figurer deux fois comme appartenant au même groupe).

SELECT DISTINCT aa.GroupId
FROM Alpha aa
JOIN Alpha ab ON (aa.GroupId = ab.GroupId)
JOIN Alpha ac ON (aa.GroupId = ac.GroupId)
LEFT OUTER JOIN Alpha ax ON (aa.GroupId = ax.GroupId AND ax.Member NOT IN ('A', 'B', 'C')
WHERE aa.Member = 'A' AND ab.Member = 'B' AND ac.Member = 'C'
 AND ax.GroupId IS NULL;

Il y a aussi des solutions impliquant GROUP BY mais je trouve la solution JOIN a souvent de meilleures performances. Je travaille habituellement dans MySQL, et je comprends MS SQL Server est mieux à des requêtes de regroupement. Donc, essayez les deux solutions et voir ce qui fonctionne le mieux pour la marque de SGBDR que vous utilisez.

essayez ceci:

declare @YourTable table (GroupID char(2),Member char(1))

insert into @YourTable values ('A1','A')
insert into @YourTable values ('A1','B')
insert into @YourTable values ('A1','C')
insert into @YourTable values ('A2','A')
insert into @YourTable values ('A2','B')
insert into @YourTable values ('A3','A')
insert into @YourTable values ('A3','D')
insert into @YourTable values ('A3','E')
insert into @YourTable values ('A5','A')
insert into @YourTable values ('A5','B')
insert into @YourTable values ('A5','C')
insert into @YourTable values ('A5','D')
SELECT t1.GroupID
    FROM @YourTable t1
        LEFT OUTER JOIN @YourTable t2 ON t1.GroupID=t2.GroupID AND t2.Member NOT IN ('A', 'B', 'C') 
    WHERE t1.Member IN ('A', 'B', 'C') 
        AND t2.GroupID IS NULL
    GROUP BY t1.GroupID
    HAVING COUNT(*) = 3

SORTIE:

GroupID
-------
A1

(1 row(s) affected)

Voici une solution complète:

Avant d'utiliser ma fonction, vous devez mettre en place une table « d'aide », il vous suffit de le faire une fois par base de données:

CREATE TABLE Numbers
(Number int  NOT NULL,
    CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number ASC)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
DECLARE @x int
SET @x=0
WHILE @x<8000
BEGIN
    SET @x=@x+1
    INSERT INTO Numbers VALUES (@x)
END

utiliser cette fonction pour diviser votre chaîne, qui ne fonctionne pas en boucle et est très rapide:

CREATE FUNCTION [dbo].[FN_ListToTable]
(
     @SplitOn              char(1)              --REQUIRED, the character to split the @List string on
    ,@List                 varchar(8000)        --REQUIRED, the list to split apart
)
RETURNS
@ParsedList table
(
    ListValue varchar(500)
)
AS
BEGIN

/**
Takes the given @List string and splits it apart based on the given @SplitOn character.
A table is returned, one row per split item, with a column name "ListValue".
This function workes for fixed or variable lenght items.
Empty and null items will not be included in the results set.


Returns a table, one row per item in the list, with a column name "ListValue"

EXAMPLE:
----------
SELECT * FROM dbo.FN_ListToTable(',','1,12,123,1234,54321,6,A,*,|||,,,,B')

    returns:
        ListValue  
        -----------
        1
        12
        123
        1234
        54321
        6
        A
        *
        |||
        B

        (10 row(s) affected)

**/



----------------
--SINGLE QUERY-- --this will not return empty rows
----------------
INSERT INTO @ParsedList
        (ListValue)
    SELECT
        ListValue
        FROM (SELECT
                  LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(@SplitOn, List2, number+1)-number - 1))) AS ListValue
                  FROM (
                           SELECT @SplitOn + @List + @SplitOn AS List2
                       ) AS dt
                      INNER JOIN Numbers n ON n.Number < LEN(dt.List2)
                  WHERE SUBSTRING(List2, number, 1) = @SplitOn
             ) dt2
        WHERE ListValue IS NOT NULL AND ListValue!=''



RETURN

END --Function FN_ListToTable

vous pouvez maintenant utiliser cette fonction comme celui-ci pour interroger pour toute liste:

DECLARE @List varchar(100)
SET @List='A,B,C'
declare @YourTable table (GroupID char(2),Member char(1))

insert into @YourTable values ('A1','A')
insert into @YourTable values ('A1','B')
insert into @YourTable values ('A1','C')
insert into @YourTable values ('A2','A')
insert into @YourTable values ('A2','B')
insert into @YourTable values ('A3','A')
insert into @YourTable values ('A3','D')
insert into @YourTable values ('A3','E')
insert into @YourTable values ('A5','A')
insert into @YourTable values ('A5','B')
insert into @YourTable values ('A5','C')
insert into @YourTable values ('A5','D')

SELECT t1.GroupID
    FROM @YourTable t1
        LEFT OUTER JOIN @YourTable t2 ON t1.GroupID=t2.GroupID AND t2.Member NOT IN (SELECT ListValue FROM dbo.FN_ListToTable(',',@List))
    WHERE t1.Member IN (SELECT ListValue FROM dbo.FN_ListToTable(',',@List))
        AND t2.GroupID IS NULL
    GROUP BY t1.GroupID
    HAVING COUNT(*) = (SELECT COUNT(*) FROM dbo.FN_ListToTable(',',@List)) 

SORTIE:

GroupID
-------
A1

select * from ALPHA OIVENT en ( sélectionnez Membre de ALPHA groupe par membre ayant COUNT (*) = 3)

Essayez celui-ci:

SELECT GroupId
  FROM ALPHA
 GROUP BY GroupId
HAVING SUM(CASE WHEN Member='A' THEN 1.0
                WHEN Member='B' THEN 2.0
                WHEN Member='C' THEN 4.0
                ELSE 7.31415
          END) = 7.0

Ma suggestion est d'analyser cette chaîne délimité dans une table temporaire, puis essayer quelque chose comme ça.

create table #temp(member varchar(10))

create table #groups
(
groupID varchar(2),
member char(1)
)

--#temp holds the members from your delimited string.
--#groups holds your relationships.



select distinct groupID
from #groups
where 
 (select count(*) from #groups i, #temp t
  where i.member = t.member and i.groupID = #groups.groupID) = 
(select count(*) from #temp)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top