【问题标题】:return first match given threshold value返回给定阈值的第一个匹配项
【发布时间】:2015-02-27 04:27:26
【问题描述】:

以下代码返回多个匹配项:

    IF OBJECT_ID('tempdb..#Thresholds') IS NOT NULL DROP TABLE #Thresholds

    CREATE TABLE #Thresholds(
        [Id] [INT] IDENTITY(1,1) NOT NULL,
        [Threshold] [FLOAT] NOT NULL
    )
    INSERT INTO #Thresholds ([Threshold]) 
    SELECT 0.076923 UNION 
    SELECT 0.153846 UNION
    SELECT 0.230769 UNION
    SELECT 0.307692 UNION
    SELECT 0.384615 UNION
    SELECT 0.461538 UNION
    SELECT 0.538461 UNION
    SELECT 0.615384 UNION
    SELECT 0.692307 UNION
    SELECT 0.76923 UNION
    SELECT 0.846153 UNION
    SELECT 0.923076 UNION
    SELECT 1

    IF OBJECT_ID('tempdb..#DataToBeJoined') IS NOT NULL DROP TABLE #DataToBeJoined

    CREATE TABLE #DataToBeJoined(
        [Value] [FLOAT] NOT NULL
    )
    INSERT INTO #DataToBeJoined ([Value]) 
    SELECT 0.25 UNION ALL
    SELECT 0.5 UNION ALL
SELECT 0.5 UNION ALL
    SELECT 0.1

SELECT 
    * 
FROM #DataToBeJoined AS a
INNER JOIN #Thresholds AS b ON a.Value >= b.Threshold

如下:

Value   Id  Threshold
0.1     1   0.076923
0.25    1   0.076923
0.25    2   0.153846
0.25    3   0.230769
0.5     1   0.076923
0.5     2   0.153846
0.5     3   0.230769
0.5     4   0.307692
0.5     5   0.384615
0.5     6   0.461538

我感兴趣的只是返回最接近的匹配,如下所示:

 Value  Id  Threshold
    0.1     1   0.076923
    0.25    3   0.230769
    0.5     6   0.461538
0.5     6   0.461538

有什么想法吗?

PS:

找到了这个初步解决方案:

select *, 
   (select top 1 Threshold 
    from #Thresholds 
    where #Thresholds.Threshold >= t.Value
    order by ABS(t.Value - #Thresholds.Threshold)  desc) as Threshold
from #DataToBeJoined t

【问题讨论】:

  • 很简单,row_number() over (partition by value order by threshold desc) as rn ,,,,,, where rn = 1
  • 谢谢请回复。我想我也找到了解决方案...
  • 你想要最接近的匹配是在上面还是下面?
  • 如果在上面或下面最接近,那么您的示例输出和初步解决方案不正确。

标签: tsql sql-server-2014


【解决方案1】:

我认为这会得到你想要的(至少我的查询的答案符合预期)。

SELECT Value, Id, Threshold FROM (
  SELECT *, ROW_NUMBER() 
    OVER (PARTITION BY Value ORDER BY ABS(Value - Threshold)) as rn
  FROM #DataToBeJoined AS a
  CROSS JOIN #Thresholds AS b
) as subselect
WHERE rn = 1

【讨论】:

  • 但问题是
  • 这看起来很有希望,但如果我添加另一行则不起作用:SELECT 0.5 UNION 所以即使我输入 4 行,它也会返回 3 行结果......
  • Jonny - 我已经调整了我的解决方案,它似乎对我有用!使用 ABS(Value - Threshold) 似乎可以解决问题。感谢您为我指明正确的方向!
【解决方案2】:
;WITH CTE AS (
SELECT * 
     ,ROW_NUMBER() OVER (PARTITION BY Value ORDER BY Threshold DESC) rn 
FROM  #Thresholds )
SELECT * 
FROM CTE 
WHERE rn = 1

【讨论】:

    【解决方案3】:

    如果你按升序插入阈值,那么可以快捷一点

    select #DataToBeJoined.value,  max(#Thresholds.ID), max(#Thresholds.Threshold)
      from #DataToBeJoined
      join #Thresholds 
        on #Thresholds.value <= #DataToBeJoined.Value
     group by #DataToBeJoined.value  
    

    如果不按顺序

    select * 
    from
    (
    select #DataToBeJoined.value,  #Thresholds.ID, #Thresholds.Threshold 
         , row_number() over (partition by #DataToBeJoined.value order by #Thresholds.Threshold desc) as rn
      from #DataToBeJoined
      join #Thresholds 
        on #Thresholds.value <= #DataToBeJoined.Value
    ) st 
    where st.rn = 1
    

    【讨论】:

    • 抱歉,“如果您按升序输入阈值”是什么意思 - 我一直认为基于集合的方法在您使用 ORDER BY 之前不知道顺序
    • “如果按升序输入”哪一部分不清楚?插入值不是基于集合的操作。您的插入是按升序排列的。您是否测试了解决方案?
    • 是的,但不起作用 - 抱歉。即使您在 SQL Server 中按顺序插入某些内容,恕我直言,这确实意味着我对您的查询感到困惑。
    • 你真的认为那些IDENTITY不按插入顺序吗?您认为 max(#Thresholds.ID) 与 max(#Thresholds.Threshold) 对齐吗?
    • 我可以翻出讨论这个的书——抱歉,目前找不到任何东西来解释我的意思。 IDENTITY,我无论如何都没有使用,只是一个数字,但这并不意味着 SQL 在内部按顺序存储它,这就是我想说的......无论如何,这离主题很远,并没有真正帮助任何人。
    猜你喜欢
    • 1970-01-01
    • 2022-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    • 2018-09-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多