【问题标题】:Outer query is running before inner query外部查询在内部查询之前运行
【发布时间】:2022-04-04 16:28:53
【问题描述】:

为什么这不起作用?

select *
from
(
    select membership_number
    from members
    where membership_number not like '%[^0-9]%'
) mem
where cast(membership_number as int) > 2

SQL Fiddle Demo

子查询应该过滤掉非数字的数据,外部查询将其转换为整数,以便我可以查找任何大于 2 的数据。

似乎它首先运行外部查询的 where 子句。我该如何解决这个问题?

【问题讨论】:

  • 您是否自行运行内部查询以确保它返回您期望的结果?
  • 太酷了。我以前从未听说过 SQL Fiddle。
  • 如果您查看带有稍微修改的WHERE 的执行计划,它会在主WHERE 中滚动子查询where 子句:Execution Plan 我仍在考虑如何防止这种行为。
  • 请检查这个 - T-SQL 函数不暗示特定的执行顺序rusanu.com/2011/08/10/…
  • 对于您的问题,您可以使用 WHERE ISNUMERIC(membership_number) = 1 AND cast(membership_number as int) > 2

标签: sql sql-server tsql


【解决方案1】:

也许:

select *
from
(
    select
        membership_number
    from
        members
    where
        membership_number not like '%[^0-9]%'
) mem
where Try_Convert(int, membership_number) > 2

【讨论】:

  • @EthanLi 是的,但有问题没有指定版本。
【解决方案2】:

非常有趣,我尝试在 SQL Server 上重现这一点,然后发现了下一个。我将您的查询更改为简单,以确保查询不会失败并且我可以看到执行计划:

select *
from
(
    select membership_number
    from members
    where membership_number not like '%[^0-9]%'
) mem
where membership_number > '2'

执行计划是有表扫描和谓词:

[master].[dbo].[members].[membership_number]>'2' 
    AND NOT [master].[dbo].[members].[membership_number] like '%[^0-9]%'

所以这是因为 SQL 优化引擎以这种方式工作(正如有人所说 - 没有人可以保证 where 子句的顺序)。修复它的一种方法可能是在

之前使用 ISNUMERIC
select *
from
(
    select membership_number
    from members
    where membership_number not like '%[^0-9]%'
) mem
where ISNUMERIC(mem.membership_number) = 1 and cast(mem.membership_number as int) > 2

【讨论】:

    【解决方案3】:

    我之前遇到过这个问题。我所做的是:

    1,你可以有一个视图:

    select membership_number
        from members
        where membership_number not like '%[^0-9]%'
    

    2,或者使用临时表

    3,或用例子句:

    select *
    from
    (
        select membership_number
        from members
        where membership_number not like '%[^0-9]%'
    ) mem
    where (CASE WHEN ISNUMERIC(membership_number) THEN cast(membership_number as int) ELSE 0 END) > 2
    

    没有一个优雅的解决方案,但希望这会有所帮助

    【讨论】:

      【解决方案4】:

      cmets 解释了执行计划如何(有时)选择在like 之前评估castcase 声明可以帮助评估顺序,但正如 Adams 所提到的,即使这种方法也不是 100%。

      select  *
      from    members
      where   case
                  when membership_number like '%[^0-9]%' then 0
                  when cast(membership_number as int) > 2 then 1
                  else 0
              end = 1
      

      【讨论】:

      • CASE 语句并不总是短路:Connect item
      • @AdamWenger 在帖子中注明。 +1
      • @AdamWenger:在文章中,它提到了聚合函数的特定情况,它们被指定为始终首先评估。在 OP 的问题中,没有聚合函数,所以这应该不是问题。并且一如既往地进行测试以验证。
      【解决方案5】:

      你可以试试这个,在下面的查询中,第一个条件被执行,如果它失败了,那么它不会执行第二个条件

      select
          membership_number
        from
          members
      where 
      isnumeric(membership_number) = 1 and 
      cast(membership_number as int) > 2
      

      要回答为什么您的查询不起作用,请查看explanation here

      【讨论】:

        【解决方案6】:

        旧查询,但仍然相关。

        发现这个技巧可以强制子查询首先运行。试试这个:

        select *
        from
        (
            select TOP (9223372036854775807) membership_number
            from members
            where membership_number not like '%[^0-9]%'
        ) mem
        where cast(membership_number as int) > 2
        

        9223372036854775807 是 BIGINT 最大值,对于大多数情况来说可能足够大。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-07-07
          • 2017-07-22
          • 1970-01-01
          • 2021-09-26
          相关资源
          最近更新 更多