2009-09-24 1 views
2

(Advantage Database Server) У меня есть таблица поставщиков услуг, которые для целей аудита никогда не удаляются. У них есть дата начала и дата окончания; в случае изменений, таких как имя или адрес, существующая строка заканчивается, создается новая строка, и для измененных данных назначается новая дата начала.Соответствие только одной определенной строке в JOIN, где многие существуют

Во время обработки платежей этим провайдерам мне нужна сводная страница с указанием имени поставщика, адреса, идентификатора (ProvID) и общей суммы платежа. Это делается в довольно простом запросе с помощью SUM() и GROUP BY.

Проблема возникает, если для указанного идентификатора поставщика имеется две или более строк. Я получаю дубликаты строк (это может привести к нескольким платежам этому провайдеру, если они не пойманы).

Моя первая мысль была использовать что-то (уродливый, но работает достаточно быстро), как подзапрос:

SELECT ... FROM service s 
INNER JOIN provider p ON p.ProvID = s.ProvID 
AND (p.EndDate IS NULL or p.EndDate = (SELECT Max(EndDate) FROM 
    provider lu WHERE lu.ProvID = s.ProvID)) 

К сожалению, это все-таки в конечном итоге найти два ряда; одна строка для NULL EndDate и одна для MAX (EndDate).

я справиться с этим в других случаях (например., Размещение надлежащего Provid для услуг, предоставляемых на определенную дату) с использованием

p.EndDate is null or (s.ServiceDate BETWEEN p.StartDate AND p.EndDate) 

К сожалению, так как запрос проблемы является GROUP BY с агрегатом, то дата службы недоступна.

Любые предложения?

EDIT: Я ищу либо строку с NULL EndDate, если она существует, либо строка с Max (EndDate), если строка NULL не существует. Это касается случая, например, когда вчера поставщик был прекращен, но работал на прошлой неделе, и мы будем платить им на следующей неделе.

ответ

3

Так что я думаю, если есть строка с датой окончания NULL , вы хотите, чтобы он, в противном случае вы хотите тот, у которого самая большая дата окончания?

Я не уверен, ADS, но следующий будет работать на SQL Server:

SELECT ... FROM service s 
INNER JOIN provider p ON p.ProvID = s.ProvID 
AND (COALESCE(p.EndDate, '2037-01-01') = (
    SELECT Max(COALESCE(EndDate, '2037-01-01')) FROM 
    provider lu WHERE lu.ProvID = s.ProvID) 
) 

Оператор COALESCE возвращает первый ненулевой параметр, так что это в основном только установки аннулирует ко времени далеко в будущем, так что SELECT MAX предоставит вам ту, у которой конечная дата NULL, если она есть.

+0

Я принимаю этот ответ, потому что он довольно гладкий, и он немного быстрее, чем ответ Niedmeddine NOT EXISTS (но только незначительно из-за хорошего выбора индекса). Спасибо, Кип. :-) –

+0

Просто слился в мою кодовую базу и протестирован против фактических данных. Работает 100%, без заметной потери производительности. Еще раз спасибо, Кип! ПРИМЕЧАНИЕ ДЛЯ ДРУГИХ ПОЛЬЗОВАТЕЛЕЙ ADS: вызовы COALESCE() требуют одно дополнение: измените оба из них на COALESCE (EndDate, CAST ('2037-01-01' AS SQL_DATE)), поскольку ADS не выполняет автоматическое преобразование с даты литералы, как и другие базы данных. –

+0

@Ken White: Если у вас более одной строки с конечной датой NULL, вы получите больше одной строки в качестве результата. Я полагаю, что это будет считаться поврежденными данными в вашей схеме. – Kip

0

Возможно использовать подзапрос вместо второй таблицы:

SELECT ... FROM service s 
INNER JOIN (SELECT ..., Max(EndDate) FROM 
    provider lu WHERE lu.ProvID = s.ProvID GROUP BY ...) p ON p.ProvID = s.ProvID 

Это предполагает, что вы получите NULL обратно, если нет макс EndDate.

0

Что вы имеете в виду, это размер хранилища данных типа 2.

Вы должны присоединиться к ID и от StartDate и EndDate, чтобы получить правильные данные.

OTTOMH код

SELECT TransactionId, TransactionType 
FROM TransactionList Tx 
    INNER JOIN TransactionType TxType 
     ON Tx.TransactionTypeId = TxType.TxTypeId 
     AND Tx.TransactionDate Between TxType.StartDate and TxType.EndDate 
+0

Как я уже говорил, я не могу этого сделать, потому что я использую агрегатную функцию и группу GROUP BY. Дата транзакции недоступна из-за агрегации. –

+0

Извинения. Я явно замалчивал эту часть. –

+0

Нет проблем. Спасибо, что попробовал, Радж. –

3

во 2-м состоянии, вы должны получить максимум, только если нет NULL EndDate

SELECT ... FROM service s 
INNER JOIN provider p ON p.ProvID = s.ProvID 
AND ( p.EndDate IS NULL 
    or (p.EndDate = (SELECT Max(EndDate) 
         FROM provider lu 
         WHERE lu.ProvID = s.ProvID) 
     AND NOT EXISTS (SELECT NULL 
          FROM provider lu 
          WHERE lu.ProvID = s.ProvID 
          AND lu.EndDate IS NULL) 
     ) 
    ) 
+0

@najmeddine: Я принял ответ Кипа, потому что он был ** немного ** быстрее, чем ваш (потому что он использовал две функции COALESCE() вместо добавления другого подзапроса для теста NOT EXISTS). Тем не менее, вы хорошо работали, поэтому я тоже его повышаю. Благодаря! –

0

Что в вашей таблице поставщиков обозначает текущий дата? EndDate = NULL, EndDate = Max (EndDate) или EndDate = '9999-01-01'? Все три являются допустимыми выборами, но это должно быть действительно однозначным, поскольку, если это не так, вы все равно будете сталкиваться с повторяющимися строками в запросах, независимо от того, насколько вы продумываете этот конкретный запрос. Поэтому я предлагаю исправить это в таблице провайдера, а затем что-то вроде этого должно работать:

select p.name, p.address, p.id, sum(s.amount) 
    from provider p 
    join service s on p.id=s.provider_id 
where p.endDate is NULL 
group by p.name, p.address, p.id 
+0

Не удается исправить таблицу поставщиков. Это исторические, а также текущие данные, и аудиторы не позволят нам изменить это по любой причине. Если бы реструктуризация данных была вариантом, мне бы не нужно было публиковать здесь. Спасибо хоть. –

+0

Думая немного больше об этом, у вас есть однозначное состояние. Вы хотите отправить на адрес _current_, верно? Тогда вам не нужен s.ServiceDate. Просто используйте «где p.EndDate имеет значение null или (sysdate между p.StartDate и p.EndDate)». Помогает ли это? – wallenborn

+0

К сожалению, нет. См. Мое редактирование на оригинальное сообщение о прекращенном поставщике с выдающимися услугами до даты срока, но выплачивается после даты окончания. SysDate не попадает между начальным и конечным параметрами и не будет строки NULL конца даты в момент обработки платежа. –

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