Для этого сценария существует ли единое самосоединение?

StackOverflow https://stackoverflow.com/questions/1380735

  •  21-09-2019
  •  | 
  •  

Вопрос

У меня есть таблица ALPHA с 2 полями groupId,Member:

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

Цель: Учитывая входные данные - A, B, C - я должен запросить таблицу, чтобы узнать, существует ли groupId для этого точного набора участников.Итак, это то, что я планирую сделать:

  1. Запросите таблицу для всех идентификаторов групп, количество которых равно 3 (поскольку мой inpt равен A, B, C .. я знаю его 3).
  2. Это даст мне A1, A3.Теперь запросите этот набор для точного совпадения значений элементов..что даст мне A1.

Я планирую написать Хранимую процедуру и каким-то образом достичь поставленной цели.Но, мой вопрос, может ли это быть достигнуто с помощью одного запроса ... возможно, с помощью одного самостоятельного соединения.

Разъяснение:Набор (A,B, C) уникален для A1.И если ввести значение (A, B, C, D), запрос НЕ должен возвращать A1.

Это было полезно?

Решение

Ответы, приведенные до сих пор, предполагают, что поле Участника уникально для любого заданного groupId.В работе, которую я выполнял, это не так.А также, если в группе есть то, что вы ищете, плюс еще кое-что, вам нужно исключить эту группу.

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


Вы также можете заменить предложение IN на join on для таблицы, содержащей элементы, которые вы ищете...

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

Другие советы

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

Это зависит от того, что вы выписываете список участников в предложении IN и устанавливаете количество (различных) записей в списке участников в предложении HAVING.Если вы не можете сгенерировать SQL таким образом, то вам придется потрудиться еще усерднее.


Как отмечалось в раннем комментарии, это также зависит от интерпретации, согласно которой вам нужны группы, в которых все трое из A, B, C (и, возможно, некоторые другие) являются членами группы.Один из способов, не обязательно лучший способ, получить "где группа содержит ровно трех человек, а именно 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

Это явно проверяет, что общее количество людей в группе равно 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;

Существуют также решения, включающие GROUP BY но я нахожу, что JOIN решение часто имеет более высокую производительность.Обычно я работаю в MySQL, и я понимаю, что MS SQL Server лучше группирует запросы.Поэтому попробуйте оба решения и посмотрите, что лучше всего подходит для той марки СУБД, которую вы используете.

попробуй это:

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

ВЫХОДНОЙ СИГНАЛ:

GroupID
-------
A1

(1 row(s) affected)

Вот полное решение:

Прежде чем вы воспользуетесь моей функцией, вам нужно настроить "вспомогательную" таблицу, вам нужно сделать это только один раз для каждой базы данных:

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

используйте эту функцию для разделения вашей строки, которая не зацикливается и выполняется очень быстро:

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

теперь вы можете использовать эту функцию следующим образом для запроса любого списка:

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

ВЫХОДНОЙ СИГНАЛ:

GroupID
-------
A1

выберите * из АЛЬФА где участник ( выберите участника из АЛЬФА сгруппируйте по участнику количество (*) = 3)

Попробуй вот это:

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

Мое предложение состоит в том, чтобы разобрать эту строку с разделителями во временной таблице, а затем попробовать что-то вроде этого.

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 с атрибуция
Не связан с StackOverflow
scroll top