2013-09-23 3 views
1

Помогите с проблемой, с которой я столкнулся, чтобы правильно соединить несколько таблиц в одном запросе.Как иметь левое соединение, включающее дополнительный запрос?

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

TRANSACTION TABLE: 
----------------- 

TRANSACTION_DATE  AMOUNT CURRENCY 
---------------- ---------- -------- 
22/09/13    100 EUR  
22/09/13    200 FRA  
22/09/13    200 GBP 

CURRENCY_CONVERSION TABLE: 
------------------------- 

FROM_CURRENCY TO_CURRENCY CONVERSION_DATE CONVERSION_RATE 
------------- ----------- --------------- --------------- 
GBP   USD   23/09/13     1.61 
EUR   USD   22/09/13     1.35 

В настоящее время запрос, который работает следующим образом (с использованием Sybase/SQL Server синтаксис объединения)

SELECT 
    t.transaction_date, 
    t.amount, 
    t.currency, 
    t.amount * cc.conversion_rate as amount_usd 
FROM transaction t, currency_conversion cc, 
WHERE t.transaction_date *= cc.conversion_date 
AND t.currency *= cc.from_currency 
AND cc.to_currency = 'USD' 

Мы сталкиваемся вопрос в приведенном выше запросе, когда currency_conversion таблица может не иметь коэффициента конверсии для определенных дат. В этом случае недостающее значение бизнес хочет использовать последний последний коэффициент конверсии, доступный в БД. Например, если курс евро к доллару на сегодняшний день недоступен, используйте вчерашний курс. Я стараюсь сделать это следующим образом:

SELECT 
    t.transaction_date, 
    t.amount, 
    t.currency, 
    t.amount * cc.conversion_rate as amount_usd 
FROM transaction t, currency_conversion cc, 
WHERE t.transaction_date *= 
          (SELECT max(c1.conversion_date) FROM currency_conversion c1 
          WHERE c1.from_currency = t.from_currency 
          AND c1.to_currency = 'USD') 
AND t.currency *= cc.from_currency 
AND cc.to_currency = 'USD' 

Этот запрос не работает. Левое соединение на t.transaction_date является незаконным. Это будет работать, если я отпущу объединение, но затем я пропущу некоторые транзакции в конечном результате.

Просьба сообщить.

Пожалуйста, обратите внимание, что:

  • его важно для того чтобы достигнуть этого в одном запросе (два шага решения с двумя запросами не поможет)
    • используется БД Sybase 12, хотя я думаю, что это общий материал SQL
    • это упрощенная схема, в фактическом запросе я использую еще два таких преобразования для доллара США, требуемых для разных сумм
    • , если возможно, предложите решение вокруг существующего que чень с минимальными изменениями

Спасибо за ваше время.

+0

изменение тегов - не нужно помещать метки oracle и mssql2008 в вопрос sybase/sql. Вы хотите иметь максимальную логику на уровне таблицы в инструкции from не в предложении where. Можете ли вы предположить, что обменный курс, который вы хотите, всегда является самым последним – Twelfth

+1

SInce SYbase и SQl Server основаны на одном и том же движке, я бы очень опасался использования синтаксиса implict для левых объединений. Он иногда приводил неверные результаты на сервере SQl (BAck в SQL Server 200, когда он был еще разрешен) когда-то интерпретировал его как кросс-соединение. Infact. Это antipatter SQL для использования соединений implict вообще, и вы действительно должны прекратить использовать эту проакцию для всех запросов. – HLGEM

+0

PArt вашей проблемы - плохой дизайн стола. Если в вашей таблице конверсии валют были начальные и конечные даты, было бы проще запросить. – HLGEM

ответ

1

Это выглядит ужасно, как T-SQL Subquery Max(Date) and Joins. Разве это не помогает?

(К сожалению, я не могу совсем комментарий еще!)

+1

Из сообщения OP ясно, что он использует 'Sybase'. –

+0

@ PM77-1: но Sybase и SQL Server очень похожи (на самом деле они основаны на тех же корнях) –

+0

@a_horse_with_no_name - My bad. Я просто не знал. –

1

Было бы разумно сделать (временным, если необходимая) таблица, которая будет завершена? То есть, сгенерируйте список всех дат, которые вам могут понадобиться, затем заполните таблицу петлей INSERT, чтобы вы знали, что ваше соединение всегда будет соответствовать партнеру? Это немного лишняя стоимость и более продолжительное соединение, но это более простое соединение, которое должно быть довольно быстрым с соответствующей индексацией.

1

Насколько я знаю, Sybase также поддерживает функции окна. Лучшее, что я могу придумать это:

select transaction_date, 
     amount, 
     currency, 
     conversion_rate, 
     conversion_date as effective_conversion_date, 
     amount_usd 
from (
    select t.transaction_date, 
     t.amount, 
     t.currency, 
     cc.conversion_rate, 
     cc.conversion_date, 
     t.amount * cc.conversion_rate as amount_usd, 
     dense_rank() over (partition by transaction_date, currency order by cc.conversion_date desc) as rn, 
     max(cc.conversion_date) over (partition by currency) as max_conv_date 
    from transactions t 
    left join currency_conversion cc 
      on t.currency = cc.from_currency 
      and cc.to_currency = 'USD' 
) t 
where (conversion_date = max_conv_date or rn = 1) 
order by transaction_date; 

Хитрость заключается в том, чтобы оставить из TRANSACTION_DATE от внешнего соединения и вычислить последнюю CONVERSION_DATE на лету с помощью функции окна. В то же время dense_rank() применяет значение к «последней» дате преобразования. Это вернет несколько строк для одной транзакции, если для этой валюты существует несколько валют.

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

BUT: Я не думаю, что это очень эффективно. Если ваши таблицы действительно большие (что, как я полагаю, предполагается), это может не очень хорошо работать.

Вот немного SQL Скрипки пример: http://sqlfiddle.com/#!12/63ef4/1
(он использует Postgres и ANSI SQL, но я думаю, что запрос должен быть запущен на Sybase, а)

1

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

Если я правильно понял, вы хотите выбрать коэффициент конверсии для записи с самой последней датой, а затем использовать ее при умножении.

SELECT 
    t.transaction_date, 
    t.amount, 
    t.currency, 
    t.amount * (SELECT cc.conversion_rate FROM currency_conversion cc 
        WHERE cc.from_currency = t.from_currency 
        AND cc.to_currency = 'USD' 
        AND cc.conversion_date = (SELECT max(c1.conversion_date) FROM 
                FROM currency_conversion c1 
                WHERE c1.conversion_date <= 
                 t.transaction_date)) as amount_usd 
    FROM transaction t 

Если таблица currency_conversion были даты начала и окончания, вы можете избавиться от c1 подзапроса и просто фокусировать куб.см подзапрос на даты в пределах диапазона и < = в TRANSACTION_DATE, которая позволит упростить вещи.

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