【问题标题】:Use CASE or AND/OR in WHERE Clause To Limit Results在 WHERE 子句中使用 CASE 或 AND/OR 来限制结果
【发布时间】:2021-05-02 22:11:10
【问题描述】:

我知道有很多类似的问题,我经历了大约 20 个问题来寻找答案。大多数设置单个变量,或使用我目前拥有的 AND/OR 格式。

我正在尝试创建一个函数,在其中传递城市、州、邮编和国家/地区的组合,它会定位并返回所有信息。如果我使用 OR 方法,如果 Zip 和 Country 不为 NULL,则第一个选项将返回结果,但是只有 Zip 不为 NULL 的选项也可以返回结果。这些是我希望它们评估的顺序(从上到下)。我只重调了一个值,但我不能保证结果的顺序相同,所以我想消除冗余结果。

这是我当前的查询:

SELECT TOP(1) City, State_abbr, State_long, Zip, Country, County, Longitude, Latitude
      FROM [ULTRA].[dbo].[CityStateInfo]
      WHERE
        (   -- Zip Code, Country: Will find City and State  /* BEST RESULTS */
            @inZip IS NOT NULL AND 
            @inCountry IS NOT NULL 
            AND 
            Zip = @inZip AND 
            Country = @inCountry 
        )
        OR
        (   -- City, State, Country:  Will find first Zip Code in City, State
            @inCity IS NOT NULL AND 
            @inState IS NOT NULL AND 
            @inCountry IS NOT NULL 
            AND 
            City = @inCity AND 
            (State_abbr = @inState OR State_long = @inState) AND 
            Country = @inCountry 
        )
        OR
        (   -- City, State: Will find first Zip Code in City, State. Will find Country
            @inCity IS NOT NULL AND 
            @inState IS NOT NULL 
            AND 
            City = @inCity AND 
            (State_abbr = @inState OR State_long = @inState) 
        )
        OR          
        (   -- City, State, Zip: Will find Country
            @inState IS NOT NULL AND 
            @inCountry IS NOT NULL 
            AND 
            (State_abbr = @inState AND State_long = @inState) AND 
            Country = @inCountry 
        )
        OR
        (   -- City, Zip:  Will find State and Country
            @inCity IS NOT NULL AND 
            @inState IS NOT NULL 
            AND 
            City = @inCity AND 
            (State_abbr = @inState AND State_long = @inState) 
        )
        OR
        (   -- State, Zip:  Will find City and Country
            @inCity IS NOT NULL AND 
            @inCountry IS NOT NULL 
            AND 
            City = @inCity AND 
            Country = @inCountry 
        )
        OR
        (
            -- Zip Code: Will find City, State and Country  ** Possible error if Country is not US or Canada
            @inZip IS NOT NULL 
            AND 
            Zip = @inZip 
        )

这就是我用 CASE 尝试过的

SELECT TOP(1) City, State_abbr, State_long, Zip, Country, County, Longitude, Latitude
      FROM [ULTRA].[dbo].[CityStateInfo]
      WHERE
        CASE
            -- Zip Code, Country: Will find City and State  /* BEST RESULTS */
            WHEN ( @inZip IS NOT NULL AND @inCountry IS NOT NULL )
                THEN Zip = @inZip AND Country = @inCountry 

            -- City, State, Country:  Will find first Zip Code in City, State
            WHEN ( @inCity IS NOT NULL AND @inState IS NOT NULL AND @inCountry IS NOT NULL )
                THEN City = @inCity AND (State_abbr = @inState OR State_long = @inState) AND Country = @inCountry 
        END

我收到了

“=”附近的语法不正确。

对应于= in "THEN Zip = @inZip AND Country = @inCountry

如果无法使用 CASE,是否可以对我的 OR 语句做一些不同的事情来实现我正在寻找的东西?

  • MS SQL Server v15.0.2070
  • SSMS v18.5

【问题讨论】:

  • 另一种方法是根据应用程序(.net 或其他)中的值构建查询,并将准备好的带有简单条件的查询发送到 sql 服务器。请注意,OR 查询的性能很差。
  • 如果性能不好,甚至可以使用动态 SQL。
  • @DaleK 我喜欢动态 SQL 的想法。我试过了,但在声明变量时遇到了一个奇怪的错误。
  • 那将是一个单独的问题。由于这个问题有答案,您应该看看它是否解决了所提出的问题,如果是,请接受它。
  • 您可以在where 子句中使用case,就像this 答案中所示的on 子句一样。性能可能会受到影响,但使用 recompile 提示可能会有所帮助。

标签: sql sql-server tsql case


【解决方案1】:

我们可以对每组条件做一个UNION ALL,添加一个排序列并取第一个。

这应该非常高效,因为它使用MERGE CONCATENATION,如Paul White describes

SELECT TOP(1) City, State_abbr, State_long, Zip, Country, County, Longitude, Latitude
    (SELECT TOP(1) 1 AS Ordering, City, State_abbr, State_long, Zip, Country, County, Longitude, Latitude
      FROM [ULTRA].[dbo].[CityStateInfo]
      WHERE
        (   -- Zip Code, Country: Will find City and State  /* BEST RESULTS */
            @inZip IS NOT NULL AND 
            @inCountry IS NOT NULL 
            AND 
            Zip = @inZip AND 
            Country = @inCountry 
        )
    UNION ALL
    SELECT TOP(1) 2, City, State_abbr, State_long, Zip, Country, County, Longitude, Latitude
      FROM [ULTRA].[dbo].[CityStateInfo]
      WHERE
        (   -- City, State, Country:  Will find first Zip Code in City, State
            @inCity IS NOT NULL AND 
            @inState IS NOT NULL AND 
            @inCountry IS NOT NULL 
            AND 
            City = @inCity AND 
            (State_abbr = @inState OR State_long = @inState) AND 
            Country = @inCountry 
        )
    UNION ALL
    SELECT TOP(1) 3, City, State_abbr, State_long, Zip, Country, County, Longitude, Latitude
      FROM [ULTRA].[dbo].[CityStateInfo]
      WHERE
        (   -- City, State: Will find first Zip Code in City, State. Will find Country
            @inCity IS NOT NULL AND 
            @inState IS NOT NULL 
            AND 
            City = @inCity AND 
            (State_abbr = @inState OR State_long = @inState) 
        )
    UNION ALL
    SELECT TOP(1) 4, City, State_abbr, State_long, Zip, Country, County, Longitude, Latitude
      FROM [ULTRA].[dbo].[CityStateInfo]
      WHERE
        (   -- City, State, Zip: Will find Country
            @inState IS NOT NULL AND 
            @inCountry IS NOT NULL 
            AND 
            (State_abbr = @inState AND State_long = @inState) AND 
            Country = @inCountry 
        )
    UNION ALL
    SELECT TOP(1) 5, City, State_abbr, State_long, Zip, Country, County, Longitude, Latitude
      FROM [ULTRA].[dbo].[CityStateInfo]
      WHERE
        (   -- City, Zip:  Will find State and Country
            @inCity IS NOT NULL AND 
            @inState IS NOT NULL 
            AND 
            City = @inCity AND 
            (State_abbr = @inState AND State_long = @inState) 
        )
    UNION ALL
    SELECT TOP(1) 6, City, State_abbr, State_long, Zip, Country, County, Longitude, Latitude
      FROM [ULTRA].[dbo].[CityStateInfo]
      WHERE
        (   -- State, Zip:  Will find City and Country
            @inCity IS NOT NULL AND 
            @inCountry IS NOT NULL 
            AND 
            City = @inCity AND 
            Country = @inCountry 
        )
    UNION ALL
    SELECT TOP(1) 7, City, State_abbr, State_long, Zip, Country, County, Longitude, Latitude
      FROM [ULTRA].[dbo].[CityStateInfo]
      WHERE
        (
            -- Zip Code: Will find City, State and Country  ** Possible error if Country is not US or Canada
            @inZip IS NOT NULL 
            AND 
            Zip = @inZip 
        )
) t
ORDER BY ordering;

【讨论】:

    【解决方案2】:

    根据您的 CASE 示例,我猜您正在追求这样的事情(?):

    SELECT TOP(1) City, State_abbr, State_long, Zip, Country, County, Longitude, Latitude
          FROM [ULTRA].[dbo].[CityStateInfo]
          WHERE
            1 = CASE
                    -- Zip Code, Country: Will find City and State  /* BEST RESULTS */
                    WHEN ( @inZip IS NOT NULL AND @inCountry IS NOT NULL )
                        THEN 
                            CASE WHEN Zip = @inZip AND Country = @inCountry THEN 1 ELSE 0 END
    
                    -- City, State, Country:  Will find first Zip Code in City, State
                    WHEN ( @inCity IS NOT NULL AND @inState IS NOT NULL AND @inCountry IS NOT NULL )
                        THEN 
                            CASE WHEN City = @inCity AND (State_abbr = @inState OR State_long = @inState) AND Country = @inCountry 
                                THEN 1 ELSE 0 END
                    ELSE 0          
                END 
    

    请注意,“ELSE 0”部分是可选的,如果您愿意,可以将其删除,因为如果 CASE 不匹配任何条件并一直到最后,它将返回 NULL,它永远不会等于 1。

    【讨论】:

    • 这种方法的问题是你几乎肯定会失去 sargability。
    【解决方案3】:

    为了确保该行只返回一次,添加额外的逻辑以确保它在已经找到时被忽略,例如替换

    (
        -- Zip Code: Will find City, State and Country  ** Possible error if Country is not US or Canada
        @inZip IS NOT NULL 
        AND Zip = @inZip 
    )
    

    (
        -- Zip Code: Will find City, State and Country  ** Possible error if Country is not US or Canada
        @inZip IS NOT NULL 
        -- Only go through this branch if country is null because otherwise we have handled it elsewhere
        AND @inCountry IS NULL 
        AND Zip = @inZip 
    )
    

    并使用类似的逻辑来处理其他类似的情况。

    【讨论】:

    • 这可行,但我需要添加更多选项,因为我现在对变量非常具体。
    猜你喜欢
    • 1970-01-01
    • 2021-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-20
    • 2016-03-24
    • 1970-01-01
    相关资源
    最近更新 更多