【问题标题】:Ignore null in search query using udt使用 udt 忽略搜索查询中的 null
【发布时间】:2021-09-18 11:49:52
【问题描述】:

我正在研究过滤器功能,但我正在努力使用 Sql。

我有一个符合这个逻辑的过程。

    RIGHT JOIN @filterQuery as fq
        ON (fq.Bpm = c.BPM OR fq.Bpm IS NULL) 
        AND (fq.Price  >= c.Price OR fq.Price IS NULL)
        AND (fq.GenreTypeId = g.Id OR fq.GenreTypeId IS NULL)
        AND (fq.KeyTypeId = k.Id OR fq.KeyTypeId IS NULL)
        AND (fq.TrackTypeId = t.Id OR fq.TrackTypeId IS NULL)

我的测试代码是这样的

DECLARE     @filters AS dbo.FilterTable
                    ,@PageIndex int = 0
                    ,@PageSize int = 10


        INSERT INTO @filters (Bpm,Price,GenreTypeId,KeyTypeId,TrackTypeId)
                
        VALUES (126,null,null,null,null),(76,null,null,null,2)

        EXECUTE dbo.PROC     @filters
                            ,@PageIndex
                            ,@PageSize

现在我的问题是,当我运行我的代码时,我的结果集看起来像这样。

Bpm  Price  GenreTypeId  KeyTypeId  TrackTypeId
126  5.99       1            1           1 
126  4.99       1            1           1

这是有道理的,因为没有 BPM 76TrackTypeId 2 的记录,但因为第一组BPM 126 的值在 TrackTypeId 处为 NULL,它不会从第二组值中过滤出 TrackTypeId 2 .

我需要我的结果集为 empty,因为数据库中没有 任何 TrackTypeId 2记录 >。这可能吗?

编辑:我希望我的结果集为空,因为没有 trackTypeId 为 2 的数据。但目前它给了我所有 126 BPM,因为这个特定过滤器的 trackTypeId 为空。

【问题讨论】:

  • 这没有意义?您正在传递 2 个完整的过滤条件,1 个用于 BPB 126 和 TrackType null,一个用于 BPM 76 和 TrackTypeId 2。您没有在任何地方解释应如何组合 2 个单独的过滤条件。为什么不直接将 TrackTypeId 2 传递给第一个过滤条件?
  • 过滤条件来自前端,用户可能会选择过滤 126 BPM,然后点击 76 BPM AND TrackTypeId 2。此时,结果集基本上都是 126我不想要的 BPM。
  • 那你想要什么?您想如何组合 2 个单独的搜索条件?为什么他们会同时选择 76 和 126?结果应该是什么?
  • @DaleK 我怀疑 OP 实际上需要为每个单独的列条件创建一个单独的过滤表,换句话说,每个要搜索的列的 AND 语义
  • 您能给我们您想要的结果集吗?我不知道你想要什么

标签: sql sql-server tsql search filter


【解决方案1】:

从你试图实现的逻辑来看,你需要的是每个过滤列都有一个单独的表类型。你还应该有以下风格的逻辑

WHERE (NOT EXISTS (SELECT 1 FROM @columnFilters) OR column IN (SELECT * FROM @columnFilters))

出于这种目的,我通常在我的数据库中保留一些标准的单列表类型的各种数据类型。

WHERE (NOT EXISTS (SELECT 1 FROM @Bpm) OR
    c.Bpm IN (SELECT * FROM @Bpm))
  AND (c.Price >= @Price OR @Price IS NULL)
  AND (NOT EXISTS (SELECT 1 FROM @GenreTypeId) OR
    g.Id IN (SELECT * FROM @GenreTypeId))
  AND (NOT EXISTS (SELECT 1 FROM @KeyTypeId) OR
    k.Id IN (SELECT * FROM @KeyTypeId))
  AND (NOT EXISTS (SELECT 1 FROM @TrackTypeId) OR
    t.Id IN (SELECT * FROM @TrackTypeId))

注意@Price 使用标量变量,因为您可以输入所需的最小值,因为条件是不等式>=

如果您确实也想为此使用表格,您可以这样做:

(NOT EXISTS (SELECT 1 FROM @Price) OR
    c.Price >= ANY(SELECT * FROM @GenreTypeId))

如果您尝试命中索引,我强烈建议您查看OPTION(RECOMPILE) 或动态 SQL 来进行此类查询。

【讨论】:

  • 对于价格,如果我仍想将其保留为表而不是标量变量,我将如何重新格式化 IN 语句以匹配 >=?
  • 已经为你添加了这个
  • 像您一样使用 ANY 与我最终使用它的方式之间是否存在巨大差异?此外,我收到有关“子查询返回超过 1 个值”的错误,因此我添加了第二个 WHERE 语句。 c.Price <= (SELECT * FROM @Price AS p WHERE p.Price IS NOT NULL))
  • 正是我使用ANY 的原因,因为多值问题。你也可以这样写,在 EXISTS (SELECT 1 FROM @GenreTypeId t WHERE c.Price >= t.val) 的底层完全一样。但正如我所说,用不等式做任何事情都是没有意义的:只需选择最大值并将其放入标量变量中
猜你喜欢
  • 2015-05-12
  • 1970-01-01
  • 2017-11-20
  • 1970-01-01
  • 1970-01-01
  • 2021-10-03
  • 1970-01-01
  • 2019-12-03
  • 2021-07-10
相关资源
最近更新 更多