Группировка записей по подмножествам SQL - PullRequest
3 голосов
/ 17 мая 2010

У меня есть база данных с PermitHolders (PermitNum = PK) и подробными возможностями каждого держателя разрешений. В таблице tblPermitDetails есть 2 столбца

  1. PermitNum (внешний ключ)
  2. FacilityID (целочисленный поиск внешнего ключа в таблице Facility).

В разрешении может быть от 1 до 29 предметов, т.е. Разрешение 50 может иметь док-станцию ​​для лодок (FacID 4), асфальтированный переход (FacID 17), подпорную стенку (FacID 20) и т. Д. Мне нужен фильтр / отображение SQL независимо от ВСЕГО РАЗРЕШЕНИЯ, которые имеют ТОЛЬКО FacID 19, 20 или 28, НЕ те, которые имеют эти плюс "х" другие .... только это подмножество. Я работал над этим в течение 4 дней, кто-нибудь, пожалуйста, помогите мне? Я написал на другой BB, но не получил никаких полезных предложений.

Как и предположил Одед, вот больше подробностей. Для таблицы tblPermitDetails нет PK.

Допустим, у нас есть Permitees 1 - 10; Разрешением 1 является Джон Доу, у него есть причал для лодок (FacID 1), пешеходная дорожка (FacID 4), буй (FacID 7) и Underbrushing (FacID 19) ... это 3 записи для Разрешения 1. Разрешением 2 является Сьюз Браун, у нее ТОЛЬКО нижняя часть (FacID 19), разрешение 3 - Стив Тони, у него есть причал для лодок (FacID 1), дорожка (FacID 4), буй (FacID 7) и подпорная стенка (FacID 20) , Разрешение 4 - Джилл Джек, у нее есть Underbrushing (FacID 19) и Подпорная стена (FacID 20). Я мог бы продолжать, но я надеюсь, что вы следуете за мной. Я хочу SQL (для MS Access), который покажет мне ТОЛЬКО разрешения 2 и 4, потому что они имеют комбинацию FacID 19 и 20 [либо оба, либо один, либо другой], НО НИЧЕГО, как Permit 1, который имеет # 19, но также имеет 4 и 7.

Надеюсь, это поможет, скажите, пожалуйста, если нет.

О да, я действительно знаю разницу между т. Е. И, например, так как мне за 40, я написал более 3000 страниц археологических полевых отчетов и магистерскую диссертацию, но я действительно не согласен с этим SQL, и мне было бы наплевать на то, чтобы проконсультироваться с Чикагским руководством по стилю перед тем, как заявить о своей просьбе. за помощью. ТАК, НЕ ДАВАЙТЕ о моих ошибках в написании! Спасибо!

Ответы [ 7 ]

1 голос
/ 17 мая 2010

Не проверено, а как на счет этого?

SELECT DISTINCT p.PermitNum
           FROM tblPermitDetails p
          WHERE EXISTS
                (SELECT '+'
                   FROM tblFacility f
                  WHERE p.FacilityID = f.FacilityID
                    AND f.facilityID = 19 )
            AND EXISTS
                (SELECT '+'
                   FROM tblFacility f
                  WHERE p.FacilityID = f.FacilityID
                    AND f.facilityID = 20 )
            AND EXISTS
                (SELECT '+'
                   FROM tblFacility f
                  WHERE p.FacilityID = f.FacilityID
                    AND f.facilityID = 28 )
            AND NOT EXISTS
                (SELECT '+'
                   FROM tblFacility f
                  WHERE p.FacilityID = f.FacilityID
                    AND f.facilityID NOT IN (19,20,28) )
0 голосов
/ 25 августа 2010

Как насчет (не проверено)

select permitnum 
from tblPermitDetails t1
left outer join
(Select distinct permitnum from tblPermitDetails where facilityId not in (19, 20, or 28)) t2
on t1.permitnum=t2.permitnum
where t2.permitnum is null

т.е. мы находим все разрешения, которые не могут соответствовать вашим критериям (у них есть хотя бы одна деталь вне тех, которые вы перечислите), затем мы находим все разрешения, которые остались, через левое соединение и критерии где.

с правильно настроенными индексами, это должно быть довольно быстро.

0 голосов
/ 27 мая 2010
SELECT DISTINCT PermitNum FROM tblPermitDetails t1
 WHERE FacilityID IN (19, 20, 28)
   AND NOT EXISTS (SELECT 1 FROM tblPermitDetails t2
                    WHERE t2.PermitNum = t1.PermitNum
                      AND FacilityId NOT IN (19, 20, 28));

Или, в прозе, получите список PermitNums, которые имеют любой из запрошенных номеров разрешений, если не существует строки для этого PermitNum, которого нет в запрошенном списке.

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

SELECT PermitNum FROM (SELECT DISTINCT PermitNum FROM tblPermitDetails
                        WHERE FacilityID IN (19, 20, 28)) AS t1
 WHERE NOT EXISTS (SELECT 1 FROM tblPermitDetails t2
                    WHERE t2.PermitNum = t1.PermitNum
                      AND FacilityID NOT IN (19, 20, 28));

Его немного сложнее читать, но он будет включать меньше подзапросов "НЕ СУЩЕСТВУЕТ", выполнив сначала часть "DISTINCT".

Обновление:

Дэвид-У-Фентон упоминает, что по причинам оптимизации следует избегать НЕ СУЩЕСТВУЕТ. Для небольшой таблицы это, вероятно, не будет иметь большого значения, но вы также можете выполнить запрос, используя COUNT (*), если вам нужно избежать NOT EXISTS:

SELECT DISTINCT PermitNum FROM tblPermitDetails t1
 WHERE (SELECT COUNT(*) FROM tblPermitDetails t2
         WHERE t1.PermitNum = t2.PermitNum
           AND FacilityID IN (19, 20, 28))
       =
       (SELECT COUNT(*) FROM tblPermitDetails t3
         WHERE t1.PermitNum = t3.PermitNum)
0 голосов
/ 19 мая 2010

Я не был уверен, хотите ли вы ВСЕ из 19,20,28 или ЛЮБОЕ из 19,20,28 ... также, это не проверено, но если вы хотите любое из решений, оно должно быть довольно близко

Select
    allowed.PermitNum
from
    DetailedFacilties allowed
    join DetailedFacilities disallowed on allowed.PermitNum != disallowed.PermitNum
where
    allowed.FacilityID in (19, 20, 28)
    and disallowed.FacilityID not in (19, 20, 28)
0 голосов
/ 17 мая 2010

Хорошо, кажется, я сначала не понял проблему. Итак, еще раз:

Я воссоздаю пример Стейси здесь:

DECLARE @PermitHolders TABLE 
(PermitNum INT NOT NULL,
PermitHolder VARCHAR(20))

DECLARE @tblPermitDetails TABLE
(PermitNum INT,
FacilityID INT)

INSERT INTO @PermitHolders VALUES (1, 'John Doe')
INSERT INTO @PermitHolders VALUES (2, 'Sus Brown')
INSERT INTO @PermitHolders VALUES (3, 'Steve Toni')
INSERT INTO @PermitHolders VALUES (4, 'Jill Jack')

INSERT INTO @tblPermitDetails VALUES (1, 1)
INSERT INTO @tblPermitDetails VALUES (1, 4)
INSERT INTO @tblPermitDetails VALUES (1, 7)
INSERT INTO @tblPermitDetails VALUES (1, 19)
INSERT INTO @tblPermitDetails VALUES (2, 19)
INSERT INTO @tblPermitDetails VALUES (3, 1)
INSERT INTO @tblPermitDetails VALUES (3, 4)
INSERT INTO @tblPermitDetails VALUES (3, 7)
INSERT INTO @tblPermitDetails VALUES (3, 20)
INSERT INTO @tblPermitDetails VALUES (4, 19)
INSERT INTO @tblPermitDetails VALUES (4, 20)

И вот решение:

SELECT * FROM @PermitHolders 
WHERE (PermitNum IN (SELECT PermitNum FROM @tblPermitDetails WHERE FacilityID IN (19, 20, 28)))
AND (PermitNum NOT IN (SELECT PermitNum FROM @tblPermitDetails WHERE FacilityID NOT IN (19, 20, 28)))

У меня есть одно наблюдение на стороне: Вы не упомянули PK для tblPermitDetails. Если не существует, это может быть не хорошо для производительности. Я рекомендую вам создать PK, используя и PermitNum, и FacilityID (составной ключ), потому что это будет и вашим PK, и полезным индексом для ожидаемых запросов.

0 голосов
/ 17 мая 2010
SELECT PermitNum
FROM tblPermitDetails
WHERE FacilityID IN (19, 20, 28)
GROUP BY PermitNum
HAVING COUNT(PermitNum)=3
0 голосов
/ 17 мая 2010

Быстрый способ может заключаться в том, чтобы смотреть только на те, которые имеют ровно три совпадения (с внутренним запросом), а затем в число тех, которые имеют 19, 20 и 28.

Конечно, это метод грубой силы, и не очень элегантный. Но у этого есть небольшое преимущество того, чтобы быть понятным. Ни один из подходов, которые я могу придумать, не будет легко настроить для различных других наборов значений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...