如何强制 SQL Server 2005 在 where 之前运行连接?

Posted

技术标签:

【中文标题】如何强制 SQL Server 2005 在 where 之前运行连接?【英文标题】:How do I force SQL Server 2005 to run a join before the where? 【发布时间】:2010-12-11 06:34:37 【问题描述】:

我有一个 SQL 查询,它将定价表连接到包含用户提供的答案的表。我的查询用于根据输入的数量获取价格。下面是我的 SQL 语句:

SELECT JobQuestion.Value, Price.Min, Price.Max, Price.Amount FROM Price
    INNER JOIN JobQuestion 
        ON Price.QuestionFK=JobQuestion.QuestionFK
        AND JobQuestion.JobFK=1
WHERE Price.Min <= JobQuestion.Value 
    AND Price.Max >= JobQuestion.Value

问题是 SQL Server 在 JOIN 之前运行 where 子句并抛出错误:

转换时转换失败 varchar 值 'TEST' 到数据类型 int。

因为它在连接之前进行了最小和最大比较('TEST' 是 JobQuestion 表中用户输入的有效值,但不应在 JobQuestion 连接到 Price 时返回)。我相信 SQL Server 选择运行 WHERE 是因为由于某种原因解析器认为这将是一个更有效的查询。如果我只是跑

SELECT JobQuestion.Value, Price.Min, Price.Max, Price.Amount FROM Price
    INNER JOIN JobQuestion 
        ON Price.QuestionFK=JobQuestion.QuestionFK
        AND JobQuestion.JobFK=1

我得到了这些结果:

500 1       500     272.00
500 501     1000    442.00
500 1001    2000    782.00

所以,添加 WHERE 应该过滤掉最后两条,只返回第一条记录。如何强制 SQL 先运行 JOIN 或使用其他技术过滤掉我需要的记录?

【问题讨论】:

【参考方案1】:

尝试按如下方式“重新措辞”查询:

SELECT *
FROM   (
          SELECT JobQuestion.Value, 
                 Price.Min, 
                 Price.Max, 
                 Price.Amount 
          FROM   Price
          INNER 
          JOIN   JobQuestion 
                 ON Price.QuestionFK = JobQuestion.QuestionFK
                 AND JobQuestion.JobFK = 1
       ) SQ
WHERE  SQ.Min <= SQ.Value 
AND    SQ.Max >= SQ.Value

根据 Christian Hayter 的回答,如果您有选择,请更改桌子设计 =)

【讨论】:

这里需要将 JobQuestion.Value 转换为 int(一些数字)吗? 我没有尝试过,但有趣的是它会产生相同的错误。同样,SQL Server Optimizer 必须将其转换为与原始执行路径相同的执行路径 如果它“尴尬”,那么您可以考虑尝试将内部部分(SQ)转换为视图,或创建存储过程,将“SQ”的结果存储在临时表,然后在该表上执行 WHERE 子句..【参考方案2】:

您不应该将字符串与整数进行比较。如果您对表格设计有任何影响,请将JobQuestion.Value 列的两种不同用途分成两个不同的列。

【讨论】:

我确实可以控制表格设计,但是 JobQuestion 表格被设计成一个愚蠢的表格,它包含动态创建的问题的值。将来我会考虑到这一点,当我必须询问问题答案时,它只会增加几层复杂性,因为我必须查看 varchar 和 int 字段。 您能否解释一下您为什么要尝试将问题与价格进行比较?这对我来说没有意义。 我将问题的答案与价格表的最小值和最大值列进行比较,以确定选择哪个价格水平。因此,如果他们的用户选择了 500 的数量(存储在 JobQuestion 表中),我在 Price 表中查找具有 = 500 的最大值的价格记录,这给出了我的价格水平。 您可以尝试一些技巧,例如CTE 或表值 UDF。最终,这取决于您的餐桌设计。【参考方案3】:

首先,这很可能是设计不佳的标志。 如果您无法更改架构,那么也许您可以使用hints 强制执行此行为。引用:

提示是 SQL Server 查询处理器在 SELECT、INSERT、UPDATE 或 DELETE 语句上为强制执行而指定的选项或策略。提示会覆盖查询优化器可能为查询选择的任何执行计划。

还有更多:

注意: 由于 SQL Server 查询优化器通常会为查询选择最佳执行计划,因此我们建议只有经验丰富的开发人员和数据库管理员在不得已的情况下才使用

【讨论】:

我不喜欢使用 SQL 提示的想法,但它似乎是(使用当前模式)处理查询而不会出错的唯一方法。我尝试使用“OPTION (FORCE ORDER)”提示并且有效!【参考方案4】:

如果您对表格设计没有影响 - 您可以尝试使用 ISNUMERIC() 过滤掉那些具有数值的记录吗?我猜想将此添加到您的 where 子句中可能会有所帮助。

【讨论】:

我试过 ISNUMERIC,但有人输入的数值会溢出 int 并且会抛出错误 @Austin:你评论中的“它”是什么? IsNumeric 会假设它是一个不同的数据类型并且仍然返回 true。 对不起,“它”指的是 SQL Server。当我尝试使用 ISNUMERIC 时,SQL Server 过滤掉了所有非数字值,但是当它比较 Price.Min 好的,我不认为你使用的数字很大。但在这种情况下,您仍然可以使用 is_numeric 过滤掉字符串,然后将整数列转换为小数。当然这并不理想,但它可能比使用提示阻塞优化器要快得多。【参考方案5】:

您可能会删除 where... 并将它们作为谓词添加到您的联接中。因为它是一个内部连接,所以应该可以工作

SELECT JobQuestion.Value, Price.Min, Price.Max, Price.Amount 
FROM Price    
INNER JOIN JobQuestion
ON Price.QuestionFK=JobQuestion.QuestionFK       
AND JobQuestion.JobFK=1
AND Price.Min <= JobQuestion.Value
AND Price.Max >= JobQuestion.Value

【讨论】:

结果相同,因为 SQL Server 优化器以相同的顺序运行它们。 hmm ands 没有按顺序测试?似乎不太可能。 我同意,但这就是返回的内容。 也许尝试在价格检查之前将 fk=1 移动到 where 子句。这必须有效,否则它会违反所有逻辑,我的脑袋会爆炸。 不,还是没用。我认为问题在于 SQL Server 优化器可以做任何想做的事情来创建最有效的查询,在我的情况下,当它进行比较时会炸毁查询【参考方案6】:

您可以在字符串列上使用 TRY_PARSE 将其转换为数字,如果 SQL 无法转换,它将为您返回 NULL 而不是错误消息。

附:这个东西是 SQL 2012 中第一次引入,所以可能会有所帮助。

【讨论】:

以上是关于如何强制 SQL Server 2005 在 where 之前运行连接?的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 2005 登录密码更改差异

内存中的 SQL Server 2005“固定”数据

SQL SERVER2005遇到的问题:无法将类型为“System.__ComObject”的 COM 对象强制转换为接口类型

在Sql2000 sql2005 sql2008 下已能实现事务复制的强制订阅,但请求订阅始终不能实现总有下列错误提示

如何转换SQL Server 2008数据库到SQL Server 2005

如何在 sql server 2005 中编写 sql server 数据库图表脚本?