1

У меня есть таблица клиентов и таблица подробностей.SQL Left join: выбор последних записей в отношениях «один ко многим»

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

В настоящее время мое предложение where отфильтровывает клиентов.

Я попытался переместить предложение where в левое внешнее соединение, но я не смог получить желаемый результат.

Это не похоже на фильтрацию продукта, когда я запускаю запрос.

SELECT 
    cust.Customer 
    , cust.Company 
    , inv.Date 
    , inv.Product 
    , inv.Units 
    , inv.Extended 
FROM 
    customerlist cust 
LEFT OUTER JOIN 
    detail inv 
ON 
    cust.customer = inv.customer 
LEFT OUTER JOIN 
    detail inv2 
ON 
     inv.customer = inv2.customer 
    AND (
      inv.date < inv2.date 
     OR inv.date = inv2.date AND inv.customer < inv2.customer 
    ) 
WHERE 
     (
      inv.Product = 'CC' 
     OR inv.Product = 'CG' 
     OR inv.Product = 'CH' 
    ) 
    AND inv2.customer IS NULL 

Мой вопрос похож на

SQL join: selecting the last records in a one-to-many relationship

Пытаюсь за то же самое просто хочу, чтобы включить каждого клиента и фильтр по продукту.

Update

Выборочные данные

Вот мой исходный запрос, который отлично, за исключением я пропускаю клиентов

Here is my original query

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

LEFT OUTER JOIN 
    detail inv2 
ON 
     inv.customer = inv2.customer 
    AND (
      inv.date < inv2.date 
     OR inv.date = inv2.date AND inv.customer < inv2.customer 
    ) 
    AND ( 
      inv.Product = 'CC' 
     OR inv.Product = 'CHECK' 
     OR inv.Product = 'ACH' 
    ) 

Вот результат Появились столбцы товаров, которые не являются «CC» и т. Д. И клиенты дублируются.

enter image description here

+0

Может ли это делать это: И inv2.customer IS NULL – logixologist

+0

Какие RDMS вы используете (Sql-сервер, mysql, oracel)? – Arion

+0

Можете ли вы разместить некоторые данные образца? – logixologist

ответ

3

Вы получили это почти правильно.

Ваш первый запрос удаляет всех клиентов, у которых нет данных с указанным продуктом, потому что вы не указали фильтр продукта в состоянии ON первого OUTER JOIN.

SELECT 
    cust.Customer 
    , cust.Company 
    , inv.Date 
    , inv.Product 
    , inv.Units 
    , inv.Extended 
FROM 
    customerlist cust 
LEFT OUTER JOIN 
    detail inv 
ON 
     cust.customer = inv.customer 
    AND inv.Product IN ('CC', 'CG', 'CH') 
LEFT OUTER JOIN 
    detail inv2 
ON 
     inv.customer = inv2.customer 
    AND (
      inv.date < inv2.date 
     OR inv.date = inv2.date AND inv.customer < inv2.customer 
    ) 
WHERE 
    inv2.customer IS NULL 

Это должно быть сделано.

Есть еще одна вещь, которая, я думаю, не совсем правильная. Часть AND inv.customer < inv2.customer должна, вероятно, быть AND inv.id < inv2.id (если в таблице detail есть поле id).

Это потому, что условие OR является фильтрацией записей detail, имеющих одну и ту же дату по их первичному ключу.

UPDATE

Поскольку таблица в вопросе не имеет поля первичного ключа вы можете использовать ROWID ADS особенность, чтобы решить, что:

SELECT 
    cust.Customer 
    , cust.Company 
    , inv.Date 
    , inv.Product 
    , inv.Units 
    , inv.Extended 
FROM 
    customerlist cust 
LEFT OUTER JOIN 
    detail inv 
ON 
     cust.customer = inv.customer 
    AND inv.Product IN ('CC', 'CG', 'CH') 
LEFT OUTER JOIN 
    detail inv2 
ON 
     inv.customer = inv2.customer 
    AND (
      inv.date < inv2.date 
     OR inv.date = inv2.date AND inv.ROWID < inv2.ROWID 
    ) 
WHERE 
    inv2.customer IS NULL 
+0

Отлично, спасибо! – Trevor

+0

Хорошее наблюдение, к сожалению, в таблице подробностей нет поля id. Но, к счастью, случаи случаются редко, когда в тот же день для клиента будет две записи указанного продукта. И спасибо за то, что этот вопрос стал более читаемым. – Trevor

+0

Добро пожаловать. Вы должны знать, что для таких случаев клиент и детали будут исключены из результатов. Я не помню, поддерживает ли ADS 9.10 «ROWID» (или «ROWNUM()»), но его можно использовать для имитации поля первичного ключа и решения этой проблемы. –

1

Ваши второй LEFT JOIN предназначен для исключения заказов перед последними клиентами? Я бы переписал это как EXISTS. Я не знаком с Advantage DataBase, надеюсь, его реализация SQL не является чем-то необычным.

SELECT 
    Cust.Customer, 
    Cust.Company, 
    Inv.Date, 
    Inv.Product, 
    Inv.Units, 
    Inv.Extended 
FROM 
    customerlist AS Cust 
    LEFT JOIN detail AS Inv ON Cust.customer = Inv.customer 
WHERE 
     (Inv.Product = 'CC' OR Inv.Product = 'CG' OR Inv.Product = 'CH') 
    AND NOT EXISTS (SELECT * FROM detail AS Inv2 WHERE Cust.customer = Inv2.customer AND Inv2.date > inv.date) 

можно упростить, заменив X = 'A' OR X = 'B' с X IN ('A', 'B'), предполагая, что Advantage поддерживает IN.


Обратите внимание, что, как написано, это показывает клиентам, где их последний заказ имеет тип «CC», «CG», или «CH». Если цель состоит в том, чтобы показать последний порядок клиентов такого типа, даже если у них есть последующий порядок другого типа, для которого потребуется корректировка.

Например, если Боб купил AB, BC, CC и DE, он исключен из запроса выше. Если вы хотите включить его, потому что он купил продукт CC, хотя позже он купил DE, комментарий, и я покажу, как это сделать.

+0

Это не решение проблемы недостающих клиентов, Trevor хочет иметь строки для каждого клиента в результате даже для клиентов, где не найдены соответствующие данные, если я правильно понял проблему. –

+0

Оптимизация 'IN' должна отлично работать в ADS. –

+0

@ JensMühlenhoff: Хм, возможно, вы правы. Хотя, если это так, я не знаю, что означает OP для второй «LEFT JOIN». Похоже, вы на правильном пути, хотя, надеюсь, вы можете получить OP к своей цели. –

Смежные вопросы