动态/条件SQL加入?

时间:2009-02-19 18:42:59

标签: sql sql-server join

我在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 个答案:

答案 0 :(得分: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

答案 1 :(得分:5)

您可以考虑使用UNION

而不是在JOIN中使用CASE语句(这将阻止使用索引进行查询)
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]上排除使用inxdex,尽管仍然应该使用myDateColumn上的任何索引

然而,它可能有助于理解FormatColumnOneWay / FormatColumnAnotherWay逻辑是什么,因为已知可以实现更好的优化

需要注意的事项:

UNION ALL不会删除任何重复项(与UNION不同)。由于这两个子查询是互斥的,因此可以保存UNION将使其删除重复项的SORT步骤。

你不应该使用'1/1/2009'样式用于字符串日期,你应该使用'yyyymmdd'样式而不使用斜杠或连字符(你也可以使用带参数的CONVERT来明确指示字符串在d中/ m / y或m / d / y样式

答案 2 :(得分: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中,您可以为连接表达式创建索引。

答案 3 :(得分: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

答案 4 :(得分:0)

您知道这对性能有害,因为您无法正确使用索引吗?

你可以使用CASE语句kludge或...你可以去修复数据,这样你就可以使用索引,它会快很多倍

答案 5 :(得分: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

如果性能很重要,您可以使用索引视图(基于子查询),而不是将子查询硬编码到整个查询中。

答案 6 :(得分:0)

我同意CASE语法更适合阅读目的,但我不知道运行时间是否有任何显着差异。

“正确”的事情,实际上,是重新做,并做正确的事情。您的日期应存储在datetime列中,将tableB中的所有日期迁移到datetime列可能会有很多好处。你可以这样做,其中包括:

  1. 将dummie列添加到TableB的{​​{1}}。
  2. 运行一个查询,该查询从当前列获取日期值并将其放入datetime列。
  3. 重命名和删除列以匹配以前的数据结构。

答案 7 :(得分:0)

好的,举起来。该列的实际数据类型是什么?我猜它不是DateTime,因为你没有真正控制格式......它只是存储一个日期。它可以是CAST还是CONVERT到日期时间?

所以你可能想要

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

这样你就不会匹配一个字符串,但实际的日期应该更可靠。它也更简单,更容易阅读。真正的问题是为什么日期没有作为DateTime存储在第一个地方......