Поиск пропущенных записей в таблице SQL с учетом критериев - PullRequest
0 голосов
/ 17 октября 2018

У меня скромный опыт работы с SQL (здесь используется MS SQL Server 2012), но это меня уклоняет.Я хочу вывести отдельные имена из таблицы (ранее успешно созданной из объединения), в которой отсутствуют некоторые обязательные записи, но при условии наличия другой аналогичной записи.Для любого, у кого есть местоположение 90, я хочу проверить, что у них также есть местоположения 10 и 20 ...

Например, рассмотрим следующую таблицу:

Name    |Number |Location
--------|-------|--------
Alice   |136218 |90
Alice   |136218 |10
Alice   |136218 |20
Alice   |136218 |40
Bob     |121478 |10
Bob     |121478 |90
Chris   |147835 |20
Chris   |147835 |90
Don     |138396 |20
Don     |138396 |10
Emma    |136412 |10
Emma    |136412 |20
Emma    |136412 |90
Fred    |158647 |90
Gay     |154221 |90
Gay     |154221 |10
Gay     |154221 |30

Итак, формально я хотел быполучить имена (и номера) тех записей в таблице, которые:

  1. имеют запись в местоположении 90
  2. и не имеют всех других необходимых записей местоположения - в этом случаетакже 10 и 20.

Таким образом, в приведенном выше примере

  • Алиса и Эмма не выводятся по этому запросу, они имеют записи для 90, 10 и 20 (все присутствующиеи правильно, мы игнорируем запись местоположения 40).
  • Дон не выводится этим запросом, у него нет записи для местоположения 90.
  • Боб и Гай выводятся этим запросом,они оба пропускают местоположение 20 (мы игнорируем запись о местоположении гея 30).
  • Крис выводится по этому запросу, он пропускает местоположение 10.
  • Фред выводится по этому запросу, он отсутствуетместоположения 10 и 20.

Желаемым результатом запроса являетсяпеределать что-то вроде:

Name    |Number |Location
--------|-------|--------
Bob     |121478 |20
Chris   |147835 |10
Fred    |158647 |10
Fred    |158647 |20
Gay     |154221 |20

Я пробовал несколько подходов с левым / правым объединением, где B.Key имеет значение null и выбирал из ... за исключением, но пока я не могу понять логическиподход правильный.В исходной таблице сотни тысяч записей и только несколько десятков пропущенных совпадений.К сожалению, я не могу использовать ничего, что подсчитывает записи, так как запрос должен быть привязан к конкретным местоположениям, и есть другие допустимые записи таблицы в других местоположениях, кроме желаемых.

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

Любой совет будет очень признателен.

=== Ответьте и используйте код, вставленный здесь ===

    --STEP 0: Create a CTE of all valid actual data in the ranges that we want
WITH ValidSplits AS
(
    SELECT DISTINCT C.StartNo, S.ChipNo, S.TimingPointId
    FROM Splits AS S INNER JOIN Competitors AS C                    
        ON  S.ChipNo = C.ChipNo                     
            AND (                               
                S.TimingPointId IN (SELECT TimingPointId FROM @TimingPointCheck)
                OR
                S.TimingPointId = @TimingPointMasterCheck
            )
),

--STEP 1: Create a CTE of the actual data that is specific to the precondition of passing @TimingPointMasterCheck
MasterSplits AS
(
    SELECT DISTINCT StartNo, ChipNo, TimingPointId 
    FROM ValidSplits
        WHERE TimingPointId = @TimingPointMasterCheck           
)

--STEP 2: Create table of the other data we wish to see, i.e. a representation of the StartNo, ChipNo and TimingPointId of the finishers at the locations in @TimingPointCheck
--The key part here is the CROSS JOIN which makes a copy of every Start/ChipNo for every TimingPointId
SELECT StartNo, ChipNo, Missing.TimingPointId
FROM MasterSplits
    CROSS JOIN (SELECT * FROM @TimingPointCheck) AS Missing(TimingPointId)
EXCEPT
    SELECT StartNo, ChipNo, TimingPointId FROM ValidSplits
ORDER BY StartNo

Ответы [ 2 ]

0 голосов
/ 17 октября 2018

Вам нужно сгенерировать наборов, состоящих из name, number, 10_and_20 для всех строк, где location = 90. Затем вы можете использовать свой любимый метод (left join + null, not not, not in) для фильтрациинесуществующие строки:

WITH name_number_location AS (
    SELECT t.Name, t.Number, v.Location
    FROM @yourdata AS t
    CROSS JOIN (VALUES (10), (20)) AS v(Location)
    WHERE t.Location = 90
)
SELECT *
FROM name_number_location AS r
WHERE NOT EXISTS (
    SELECT *
    FROM @yourdata AS t
    WHERE r.Name = t.Name AND r.Location = t.Location
)
0 голосов
/ 17 октября 2018

Добро пожаловать в переполнение стека.

Что вам нужно, это немного сложно, так как вы хотите увидеть данные, которые не существуют.Таким образом, сначала мы должны создать все возможные строки, а затем вычесть те, которые существуют

    select ppl_with_90.Name,ppl_with_90.Number,search_if_miss.Location 
    from
    (
        select distinct Name,Number
        from yourtable t
        where Location=90
    )ppl_with_90 -- All Name/Numbers that have the 90
    cross join (values (10),(20)) as search_if_miss(Location) -- For all the previous, combine them with both 10 and 20
except -- remove the lines already existing
    select * 
    from yourtable 
    where Location in (10,20)
...