为什么加入不同的数据类型会产生不一致的转换类型?

时间:2018-01-15 18:44:44

标签: sql sql-server select join

当我尝试将表连接在一起表示在不同数据类型中表示的值时,我得到了非常奇怪的错误。请考虑以下事项:

我有两张桌子;让我们说一个人在数据库" CoffeeWarehouse,"另一个是数据库" CoffeeAnalytics":

表1:CoffeeWarehouse.dbo.BeanInfo

表2:CoffeeAnalytics.dbo.BeanOrderRecord

现在,两个表都有一个名为OrderNumber的字段(尽管在表2中,它的拼写为[order number]);在表1中,它表示为string,在表2中,它表示为float

我继续加入桌子:

SELECT ordernumber,
   bor.*
FROM CoffeeWarehouse.dbo.BeanInfo AS bni
     LEFT JOIN CoffeeAnalytics.dbo.BeanOrderRecord AS bor ON bor.[order number] = bni.ordernumber;

如果我通过添加以下内容来指定订单号:

WHERE bni.ordernumber = '48911'

然后我看到了我想要的完整表格 - 我加入的表格中的所有字段都已正确填充。

如果我添加更多订单号,它也会起作用:

WHERE bni.ordernumber IN ('48911', '83716', '98811', ...)

现在出现问题:

假设我想在表格中选择另一个字段(即CountryOfOrigin)不为空的所有内容。我不打算输入数千个订单号 - 我只想使用where子句清除不完整数据的行。

所以我将以下内容添加到原始查询中:

WHERE bor.CountryOfOrigin IS NOT NULL

当我执行时,我收到此错误:

Msg 8114, Level 16, State 5, Line 1 Error converting data type varchar to float.

如果我只是将其用作where子句,我会得到同样的错误:

WHERE bni.ordernumber IS NOT NULL

为什么会这样?当我指定ordernumber时,连接工作正常 - 当我想选择许多ordernumbers时,我得到转换错误。

任何帮助/见解?

1 个答案:

答案 0 :(得分:3)

SQL Server查询优化器可以选择不同的路径来获取结果,即使是从一分钟到一分钟的相同查询。

在此查询中,请说:

SELECT ordernumber,
   bor.*
FROM CoffeeWarehouse.dbo.BeanInfo AS bni
     LEFT JOIN CoffeeAnalytics.dbo.BeanOrderRecord AS bor ON bor.[order number] = bni.ordernumber
WHERE bni.ordernumber = '48911';

例如,查询优化器可以采用以下两种方式之一:

  • 它可以选择使用BeanInfo作为“驱动”表,使用索引将该表中的行缩小到例如订单号为48911的单行,然后 只使用那个订单号加入BeanOrderRecord
  • 它可以选择使用BeanOrderRecord作为驱动表,通过订单号将两个表连接在一起以获得完整的结果集,然后然后按订单号过滤结果集

查询优化器采用的路径取决于各种因素,包括已定义的索引,表中的行数,基数等。

现在,如果碰巧您的某个订单号无法转换为浮动 - 有人偶然输入“!2345” - 第一个优化选项可能始终有效,并且第二个可能总是失败。但是您无法选择优化器采用的路径。

这就是为什么你看到你认为奇怪的结果。在您的一个查询中,正在分析所有订单号并触发错误,在另一个订单号中,可转换为浮动的订单号正在被分析,因此没有错误。但它基本上只是运气,它正在以它的方式运作。它也可能是相反的方式,或者两种查询都不会起作用。

这是将内容存储在不适当的数据类型中的一个原因。修复这将是明显的解决方案。

然而,一个肮脏而可怕的修复方法可能是在进行订单号比较时始终将FLOAT投射到VARCHAR,因为我相信从FLOAT投出总是安全的到VARCHAR。虽然您可能需要进行试验,以确保生成的VARCHAR值的格式与您的订单号相同(或首先转换为INTEGER ...)

但是,你必须采取一些非常繁琐的技巧才能从你现有的设置中获得任何性能。如果它们都是VARCHAR值,您可以通过索引每个订单号列来轻松地使表加入非常快,但是因为它是您必须执行的转换将使正常索引无法用于连接。

如果您使用的是最新版本的SQL Server,则可以使用TRY_CAST查找问题行:

SELECT * FROM BeanOrderRecord WHERE TRY_CAST([order number] AS VARCHAR) IS NULL

...会找到任何FLOAT [order number]的行,这些行无法转换为VARCAHR