简体   繁体   English

对于这种情况,是否存在单个自联接?

[英]For this scenario,does a single self-join exist?

I have a table ALPHA with 2 fields GroupId,Member: 我有一个表ALPHA有2个字段GroupId,成员:

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

Objective: Given the input of - A,B,C - I have to query the table to find if a GroupId exists for this exact set of members. 目标:给定-A,B,C的输入 - 我必须查询表以查找这个确切的成员集是否存在GroupId。 So, this is what I plan to do: 所以,这就是我打算做的事情:

  1. Query the table for all GroupIds whose count is 3 (since my inpt is A,B,C ..I knw its 3) 查询表中所有计数为3的GroupId(因为我的inpt是A,B,C ..我知道它的3)
  2. This will give me A1,A3. 这将给我A1,A3。 Now, query this set for exact matching Member values..which will give me A1. 现在,查询此集以获得完全匹配的成员值...这将给我A1。

I plan to write a Stored Procedure and would achieve the objective somehow. 我打算写一个存储过程,并以某种方式实现目标。 But, my question can this be achieved in a single query...a single self-join perhaps. 但是,我的问题可以在单个查询中实现......也许是单个自联接。

Clarification: The set of (A,B,C) is unique to A1. 澄清:(A,B,C)的集合是A1独有的。 And if give an input of (A,B,C,D) the query should NOT return A1. 如果输入(A,B,C,D),则查询不应返回A1。

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

This relies on you writing out the list of members in the IN clause and setting the number of (distinct) entries in the member list in the HAVING clause. 这依赖于您在IN子句中写出成员列表并在HAVING子句中设置成员列表中的(不同)条目数。 If you can't generate the SQL thusly, then you have to work harder. 如果你不能这样生成SQL,那么你必须更加努力。


As noted in an early comment, this also relies on the interpretation that you want the groups where all three of A, B, C (and possibly some others) are members of the group. 正如早期评论中所指出的,这也依赖于您希望A,B,C(以及可能还有其他)三个都是该组成员的组的解释。 One way, not necessarily the best way, of getting 'where the group contains exactly three people, namely A, B, C', is to use: 获得'组中恰好包含三个人,即A,B,C'的一种方式,不一定是最好的方法,是使用:

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

This explicitly checks that the total number of people in the group is 3 and that the members are A, B, and C (assuming that there is a unique constraint on Alpha(GroupID, Member) so that a member can't be listed twice as belonging to the same group). 这明确检查组中的总人数是3,成员是A,B和C(假设Alpha(GroupID,Member)上有唯一约束,因此成员不能列出两次属于同一组)。

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;

There's also solutions involving GROUP BY but I find the JOIN solution often has better performance. 还有涉及GROUP BY的解决方案,但我发现JOIN解决方案通常具有更好的性能。 I usually work in MySQL, and I understand MS SQL Server is better at grouping queries. 我通常在MySQL工作,我理解MS SQL Server更擅长分组查询。 So try both solutions and see what works best for the brand of RDBMS you use. 因此,尝试两种解决方案,看看哪种解决方案最适合您使用的RDBMS品牌。

Answers given so far assume that the Member field is unique for any given GroupID. 到目前为止给出的答案假设Member字段对于任何给定的GroupID都是唯一的。 In work I have done this isn't the case. 在工作中,我所做的并非如此。 And also if the group has what you're looking for, plus some extra, you need to exlucde that group. 而且,如果该小组拥有您正在寻找的内容,再加上一些额外的内容,您需要将该小组排除在外。

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


You can also replace the IN clause with a join on to a table holding the members you are searching for... 您还可以将IN子句替换为包含您正在搜索的成员的表的连接...

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

try this: 尝试这个:

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

OUTPUT: OUTPUT:

GroupID
-------
A1

(1 row(s) affected)

Here is a complete solution: 这是一个完整的解决方案:

Before you use my function, you need to set up a "helper" table, you only need to do this one time per database: 在使用我的函数之前,需要设置一个“帮助程序”表,每个数据库只需执行一次:

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

use this function to split your string, which does not loop and is very fast: 使用此函数来分割您的字符串,它不会循环并且非常快:

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

you can now use that function like this to query for any list: 您现在可以使用这样的函数来查询任何列表:

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)) 

OUTPUT: OUTPUT:

GroupID
-------
A1

从ALPHA中选择*其中的成员(从成员中选择ALPHA成员具有COUNT(*)= 3)

Try this one: 试试这个:

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

My suggestion is to parse that delimited string into a temp table, and then try something like this. 我的建议是将分隔的字符串解析为临时表,然后尝试这样的事情。

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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM