Динамическое / условное соединение SQL? - PullRequest
3 голосов
/ 19 февраля 2009

У меня есть данные в таблице MSSQL (TableB), где [dbo] .tableB.myColumn меняет формат после определенной даты ...

Я делаю простое Присоединение к этой таблице ..

Select [dbo].tableB.theColumnINeed from [dbo].tableA 
left outer join [dbo].tableB on [dbo].tableA.myColumn = [dbo].tableB.myColumn

Однако мне нужно присоединиться, используя другое форматирование, основанное на столбце даты в Таблице A ([dbo] .tableA.myDateColumn).

Что-то вроде ...

Select [dbo].tableB.theColumnINeed from [dbo].tableA 
left outer join [dbo].tableB on [dbo].tableA.myColumn = 
    IF [dbo].tableA.myDateColumn > '1/1/2009'
        BEGIN
            FormatColumnOneWay([dbo].tableB.myColumn)
        END
    ELSE
        BEGIN
            FormatColumnAnotherWay([dbo].tableB.myColumn)
        END

Мне интересно, есть ли способ сделать это .. или лучший способ, о котором я не думаю, чтобы приблизиться к этому ..

Ответы [ 8 ]

8 голосов
/ 19 февраля 2009
SELECT [dbo].tableB.theColumnINeed
FROM   [dbo].tableA 
LEFT OUTER JOIN [dbo].tableB
ON [dbo].tableA.myColumn = 
   CASE
    WHEN [dbo].tableA.myDateColumn <= '1/1/2009' THEN FormatColumnOneWay([dbo].tableB.myColumn)
    ELSE FormatColumnAnotherWay([dbo].tableB.myColumn)
   END
5 голосов
/ 19 февраля 2009

Вместо того, чтобы использовать оператор CASE в JOIN, что предотвратит запрос с использованием индексов, вы можете рассмотреть возможность использования UNION

SELECT [dbo].tableB.theColumnINeed 
FROM   [dbo].tableA 
    LEFT OUTER JOIN [dbo].tableB 
         ON [dbo].tableA.myDateColumn > '1/1/2009'
        AND [dbo].tableA.myColumn = FormatColumnOneWay([dbo].tableB.myColumn)
UNION ALL
SELECT [dbo].tableB.theColumnINeed 
FROM   [dbo].tableA 
    LEFT OUTER JOIN [dbo].tableB 
         ON [dbo].tableA.myDateColumn <= '1/1/2009'
        AND [dbo].tableA.myColumn = FormatColumnAnotherWay([dbo].tableB.myColumn)

но если FormatColumnOneWay / FormatColumnAnotherWay являются функциями или выражениями полей, это, вероятно, исключит использование индексных индексов в [myColumn], хотя любой индекс в myDateColumn все равно следует использовать

Тем не менее, это может помочь понять, что такое логика FormatColumnOneWay / FormatColumnAnotherWay, а также знание, которое может обеспечить лучшую оптимизацию

Несколько вещей на заметку:

UNION ALL не удаляет дубликаты (в отличие от UNION). Поскольку два подзапроса являются взаимоисключающими, это нормально и сохраняет шаг SORT, который UNION сделает для удаления дубликатов.

Вы не должны использовать стиль '01.01.2009' для строковых дат, вы должны использовать стиль 'yyyymmdd' без косых черт или дефисов (вы также можете использовать CONVERT с параметром, чтобы явно указать, что строка находится в d / m / y или m / d / y style

0 голосов
/ 19 февраля 2009

Хорошо, подожди. Каков фактический тип данных столбца? Я предполагаю, что это не DateTime, потому что вы не контролируете форматирование ... он просто хранит дату. Это может быть CAST или CONVERTed к DateTime все же?

Так что вы можете

left outer join tableb on tableA.myColumn = CAST(tableb.MyColumn as DateTime)

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

0 голосов
/ 19 февраля 2009

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

«Правильно» сделать, на самом деле, это сделать заново и начать все правильно. Ваши даты должны храниться в datetime столбцах, и вам, вероятно, придется много выиграть при переносе всех ваших дат в tableB в столбец datetime. Вы можете сделать это следующим образом:

  1. Добавить столбец пустышки к TableB с типом datetime.
  2. Запустить запрос, который берет значение даты из текущего столбца и помещает его в столбец даты и времени.
  3. Переименование и удаление столбцов в соответствии с предыдущей структурой данных.
0 голосов
/ 19 февраля 2009

Что ж, вы могли бы использовать подзапрос для правильного форматирования данных в любой таблице перед объединением.

SELECT
  newB.columnINeed
FROM
  tableA AS A
LEFT OUTER JOIN (
  SELECT
    columnINeed
  , CASE WHEN myColumn > '1/1/2009' THEN FormatColumnOneWay(myColumn)
    ELSE FormatColumnAnotherWay(myColumn)
    END AS myColumn
  FROM
    tableB
) AS NewB ON A.myColumn = B.myColumn

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

0 голосов
/ 19 февраля 2009

Вы знаете, что это плохо сказывается на производительности, поскольку вы не сможете использовать индексы, верно?

Вы можете использовать оператор CASE kludge или ... вы можете пойти и исправить данные так, что вы МОЖЕТЕ использовать индекс, и он будет во много раз быстрее

0 голосов
/ 19 февраля 2009

В SQL Server вы должны использовать CASE, например:

SELECT * 
FROM TableA
INNER JOIN TableB on TableA.Column=
CASE WHEN TableA.RecordDate>'1/2/08'
       THEN FormatCoumn(TableB.Column) 
     ELSE FormatColumnOtherWat(TableB.Column)
END
0 голосов
/ 19 февраля 2009

Из префикса [dbo] я полагаю, что вы используете SQL Server. Хотя у меня нет особого опыта, вы можете преобразовать оба поля в определенный формат даты:

select * from tableA
  Left Outer join tableB
       On CONVERT(CHAR(8), tableA.myColumn, 112) = CONVERT(CHAR(8), tableB.myColumn, 112)

То же самое должно работать на любой СУБД, используя соответствующие функции форматирования даты.

Я не знаю о SQL Server, но в Oracle вы можете создать индекс для выражения объединения.

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