2009-02-19 53 views
3

我有一個MSSQL表(表B),其中[DBO] .tableB.myColumn在特定日期後改變格式的數據...動態/有條件SQL加入?

我做一個簡單的加入到該表..

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
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 
0

[dbo]前綴,我相信你正在使用SQL Server。雖然我沒有與它太多的經驗,你既可以字段轉換到一個特定的日期格式:

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

同樣應該在任何DBMS工作,使用適當的日期格式的功能。

我不知道SQL Server,但在Oracle中可以爲聯接表達式創建一個索引。

0

在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

我的建議是修正數據,因爲優化器會忽略JOIN條件中的這些函數的索引 – SQLMenace 2009-02-19 18:52:03

+0

是的,但有時您無法修復數據;-) – JoshBerke 2009-02-19 18:52:55

+0

它是同一列,我會修復它,把一個CHECK CONSTRAINT放在它上面,這樣它就不會再發生了,因爲遲早有人會尖叫,表現是不可接受的,然後呢? – SQLMenace 2009-02-19 18:54:18

0

你知道這是不好的性能,因爲你將無法使用索引嗎?

您可以使用CASE語句雜牌或者......你可以去修正數據,這樣就可以使用索引,這將是快很多倍

0

嗯,你可以使用子查詢正確格式加入前任一表中的數據。

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

您可能無法做到這一點。我注意到你正在根據A來格式化B。我的猜測是你可以格式化B而不涉及A,然後進行連接? – alphadogg 2009-02-19 19:00:14

0

我同意CASE語法更適合閱讀目的,但我不知道運行時間是否有任何顯着差異。

要做的「正確」事情,實際上是重新做到這一點,並開始做正確的事情。您的日期應該存儲在datetime列中,並且您可能需要將tableB中的所有日期遷移到datetime列。你可以做這種方式,其中包括:

  1. TableB添加dummie柱datetime類型。
  2. 運行一個查詢,該查詢從當前列中獲取日期值並將其放入datetime列中。
  3. 重命名和刪除列以匹配先前的數據結構。
0

好的,抱起來。列的實際數據類型是什麼?我猜這不是DateTime,因爲你不能真正控制格式化...它只是存儲一個日期。它可以是CAST或CONVERTED到DateTime嗎?

所以,你可能想

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

這應該是更可靠的這樣,你不匹配一個字符串,但實際日期。它也更簡單,更易於閱讀。真正的問題是爲什麼日期不是以日期時間的形式存儲在第一位...

5

而不是在JOIN中有一個CASE語句,這將阻止使用索引的查詢,您可以考慮使用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] inxdexes的,雖然myDateColumn任何指數應該仍然可以使用

但是,它可能有助於瞭解FormatColumnOneWay/FormatColumnAnotherWay邏輯是什麼,正如已知的那樣可以使一個更好的優化

幾件事情需要注意:

UNION ALL不會刪除任何重複的(不像UNION)。因爲這兩個子查詢是相互排斥的,所以這並沒有問題,並且保存了UNION爲使它能夠刪除重複項而進行的SORT步驟。

對於字符串日期,您不應該使用'1/1/2009'樣式,您應該使用'yyyymmdd'樣式不帶和連字符或連字符(也可以使用帶有參數的CONVERT來顯式指示該字符串在d/m/y或m/d/y樣式