SQL Server 查询优化器问题

Posted

技术标签:

【中文标题】SQL Server 查询优化器问题【英文标题】:SQL Server Query optimiser problem 【发布时间】:2011-03-09 16:38:38 【问题描述】:

SQL Server 优化查询的方式似乎导致它中断。下面用两个例子来说明这一点:

SELECT distinct ET.ElementName, ET.Shared, CONVERT(float,ED.Value), ED.SheetSetVersionID, ED.SheetDataID
FROM tElementData ED 
INNER JOIN tElementTemplate ET 
ON ED.ElementTemplateID = ET.ElementTemplateID 
AND ET.ElementName like 'RPODCQRated'

上述查询工作正常,但不是我需要运行的查询。

SELECT distinct ET.ElementName, ET.Shared, CONVERT(float,ED.Value),    ED.SheetSetVersionID, ED.SheetDataID
FROM tElementData ED 
INNER JOIN tElementTemplate ET 
ON ED.ElementTemplateID = ET.ElementTemplateID 
AND ET.ElementName like 'RPODCQRated'
AND CONVERT(float,ED.Value) = 0.006388

上面的查询抛出一个异常,指出它不能将 nvarchar 值转换为浮点数。 tElementData.Value 是一个 nvarchar(500) 字段,一些记录确实没有数值,但是 tElementTemplate = 'RPODCQRated' 的所有值都可以转换为浮点数,正如顶部查询所证明的那样。似乎 SQL 服务器在尝试加入之前正在应用 CONVERT(float,ED.Value) 。我需要第二个查询以某种方式工作,我可以重写它,但是在不重写现有应用程序的整个数据层的情况下我可以做些什么。

我尝试过但无济于事的事情:将最后一个条件移动到 where 子句而不是 join,将第一个查询放入 CTE 并将 where 子句应用于 CTE,创建一个调用 IsNumeric 的标量函数尝试进行转换之前的数据。

我唯一能做的就是将所有数据插入临时表中,然后将 where 子句应用于临时表。不幸的是,将其作为解决方案实施将涉及对应用程序的数据层进行大量重构,以便在搜索某些记录时解决一个晦涩难懂的错误。

有什么想法吗?

【问题讨论】:

您能否发布一些来自第一个查询的示例数据? 这个答案很好地解释了这一点(这也解释了为什么试图通过使用 CTE 来避免这种情况是行不通的)***.com/questions/5191701/… 谢谢马丁,我确实知道为什么它会按照您链接中的答案中概述的那样执行此操作,我的意思是我该如何绕过它。 @Ben - 根据 Brian 的回答保证案例陈述评估的顺序。 我要做的是修复糟糕的设计。您不应将浮点数据和字符串数据存储在同一列中。您似乎正在使用 EAV 表,这本身就是一个糟糕的设计选择。这将很难查询并且通常会执行得很差(特别是如果您总是必须将数据转换为正确的类型)并且无法维护并且会导致数据完整性问题永无止境。首先修复糟糕的设计。 EAV 应该很少使用,并且只能用于无法提前设计的东西。为了灵活性,您放弃了可维护性和性能。 【参考方案1】:

SQL 中确保线性评估的唯一方法是使用 Case 语句

SELECT distinct ET.ElementName, ET.Shared, CONVERT(float,ED.Value), ED.SheetSetVersionID, ED.SheetDataID 
FROM tElementData ED  
INNER JOIN tElementTemplate ET  
ON ED.ElementTemplateID = ET.ElementTemplateID  
AND ET.ElementName like 'RPODCQRated' 
AND CASE(WHEN ET.ElementName like 'RPODCQRated' then CONVERT(float,ED.Value) else 0 end) = 0.006388 

这可能会导致对 ElementName 进行重复检查,但据我所知,这是确保评估顺序的唯一方法。

当然,除非您将整个 eval 移出查询并将结果嵌套在 CTP 中并对结果进行强制转换。

【讨论】:

我将把它标记为 asnwer。尽管它确实有效,但我没有使用此解决方案,前提是您删除了 CASE 和 WHEN 之间以及结束后的括号。奇怪的是,我已经尝试过如下类似的方法,但没有奏效。 AND CASE WHEN IsNumeric(ED.Value) = 1 then CONVERT(float,ED.Value) else null end = 0.006388 @Ben - IsNumeric 只是检查它是否可以转换为至少一种数字数据类型,不一定可以转换为浮点数。例如select isnumeric('+'),isnumeric('.'),isnumeric('$')都返回1但不能转成float。【参考方案2】:

我会尝试把它分解成这样的:

;with a as
(

SELECT distinct
    ET.ElementName,
    ET.Shared,
    CONVERT(float, ED.Value),
    ED.SheetSetVersionID,
    ED.SheetDataID
FROM
    tElementData ED
    INNER JOIN tElementTemplate ET
        ON ED.ElementTemplateID = ET.ElementTemplateID
           AND ET.ElementName like 'RPODCQRated'
)
select *
from a
where CONVERT(float, ED.Value) = 0.006388

或者,您是否尝试过“ED.Value='0.006388' 或 varchar 等价物是什么?

【讨论】:

是的,使用像您的示例这样的 CTE,但没有帮助。我无法进行字符串比较,因为运算符是动态生成的,对于 = 没问题,但对于 > 会进行字母比较。【参考方案3】:

我已经通过使用表函数解决了这个问题。最后一个连接子句的元素名称、运算符和右手值都是动态生成的。我创建了下面的 tvf 并将 select 语句的相关部分替换为对 tvf 的调用。

CREATE FUNCTION tvfAdvancedSearch
(
    @TemplateType nvarchar(500)
)
RETURNS 
@Results TABLE 
(   
    ElementName nvarchar(50),
    Shared tinyint, 
    Value NVARCHAR(500), 
    SheetSetVersionID int, 
    SheetDataID int
)
AS
BEGIN
    INSERT INTO @Results
    SELECT distinct ET.ElementName, ET.Shared, ED.Value, ED.SheetSetVersionID, ED.SheetDataID
    FROM tElementData ED 
    INNER JOIN tElementTemplate ET 
        ON ED.ElementTemplateID = ET.ElementTemplateID 
        AND ET.ElementName like @TemplateType   
    RETURN 
END
GO

我还想提一下,Brian Rudolph 的回答也有效,但在我看到他的帖子之前我已经实施了这个解决方案。

【讨论】:

以上是关于SQL Server 查询优化器问题的主要内容,如果未能解决你的问题,请参考以下文章

SQL -- SQL Server 查询优化器(Query Optimizers)

从 SQL Server 查询优化器生成多个脚本

为啥 SQL Server 查询优化器有时会忽略明显的聚集主键?

SQL Server查询优化器执行计划“语句提前终止的原因:超时”

SQL Server 查询优化器执行不必要的连接

SQL Server的优化器会缓存标量子查询结果集吗