【问题标题】:Evaluate several expression, continue to next id when true评估几个表达式,当为真时继续下一个id
【发布时间】:2021-07-18 22:47:15
【问题描述】:

我有 2 个 SQL 表,如下所示:

1.噪音

Noise_Id Value
2 73
7 57
8 78
12 86
14 89

2。颜色

|ColorInt |Criteria       |SortOrder|
|---------|---------------|---------|
|-1       | [Value] < 75  | 75      |
|-16711936| [Value] < 80  | 80      |
|-128     | [Value] < 85  | 85      |
|-10496   | [Value] < 90  | 90      |
|-23296   | [Value] < 95  | 95      |
|-65536   | [Value] < 100 | 100     |
|-38476   | [Value] < 105 | 105     |
|-8388480 | [Value] < 110 | 110     |
|-11861886| [Value] >= 110| 111     |

此 [Criteria] 列可以包含其他表达式,例如:

[Value]=1
[Value]=2
[Value]=3
[Value]=4
[Value]=5
FindingsCnt_Overdue > 0
FindingsCnt_Completed = FindingsCnt - FindingsCnt_Deleted
[Value] = -1
[Value] < 30
[Value] < 50
[Value] < 70
[Value] < 90
[Value] < 110
[Value] < 150
[Value] >= 150

我想根据 [Criteria] 为每个 [Noise_Id] 计算 [ColorInt],如下所示:

预期结果

Noise_Id Value ColorInt
2 73 -1
7 57 -1
8 78 -16711936
12 86 -10496
14 89 -10496

我如何创建一个高效的存储过程或函数来评估每个 [Noise_Id] 的 [Criteria] 列中的表达式,并在满足 [Criteria] 时转到下一个 [Noise_Id]?

这是我尝试过的代码:

CREATE TABLE #Results (
Noise_Id int,
Value varchar(MAX)) 

DECLARE @SQL varchar(MAX)=''

SELECT @SQL = 
@SQL+' Insert Into #Results(Noise_Id, Value) Select '+cast(Noise_Id AS varchar(50))+', case when '+Replace([Criteria],'[Value]',Value)+' then [ColorInt] else null end as Value FROM [Noise] where Noise_Id = '+cast(Noise_Id AS varchar(50))
FROM [Noise] 

exec (@sql)

select * from #Results

drop table #Results

非常感谢您的帮助!

【问题讨论】:

  • 标准是什么?预期的输出是什么?
  • 根据问题指南,请展示您的尝试并告诉我们您发现了什么(在本网站或其他地方)以及为什么它不能满足您的需求。
  • 你的专栏Criteria真的有N'[Value] &lt; 75'的价值吗?如果是这样,唯一的方法是使用动态 SQL,我强烈建议不要这样做。如果您确实走这条路(不要),我希望您有非常好的流程来确保列中的表达式不能执行任何形式的注入。如果你不这样做,那么就不要靠近动态解决方案;改为修复设计(无论如何这是真正的解决方案)。
  • 如果您还有其他操作,那么您也应该将其包含在问题中,但是,这只会使我的观点更有说服力;这是需要在这里改变的设计。除非您真的知道您正在使用动态 SQL 做什么,并且您对进入列的数据采取了安全措施,否则使用动态 SQL 只会是一个(巨大的)安全漏洞。
  • @Larnu 流浪球门柱的案例。我认为这些人已经离开了球场。我放弃了。

标签: sql-server tsql dynamic-sql


【解决方案1】:

您需要将Criteria 拆分为单独的列:Op 定义比较应该是什么,Criteria 定义要比较的值。

然后您可以分别比较每个可能的运算符。

之后,每组查询只是一个简单的TOP 1

SELECT
    n.Noise_Id
    n.[Value],
    c.ColorInt
FROM Noise n
CROSS APPLY (
    SELECT TOP 1 c.ColorInt
    FROM Color c
    WHERE
        (c.Op = '<'  AND n.[Value] <  c.Criteria) OR
        (c.Op = '<=' AND n.[Value] <= c.Criteria) OR
        (c.Op = '='  AND n.[Value] =  c.Criteria) OR
        (c.Op = '>'  AND n.[Value] >  c.Criteria) OR
        (c.Op = '>=' AND n.[Value] >= c.Criteria)
    ORDER BY c.SortOrder
) c

您说您不能修改Criteria 列。那我们就得自己拆分了。

我假设Value 总是在开头,比较器在中间,并且有空格。否则会失败。

SELECT
    n.Noise_Id
    n.[Value],
    c.ColorInt
FROM Noise n
CROSS APPLY (
    SELECT TOP 1 c.ColorInt
    FROM Color c
    CROSS APPLY (VALUES (
        CAST(RIGHT(c.Criteria, CHARINDEX(' ', REVERSE(c.Criteria)) - 1) AS int)  -- same data type as n.Value
    ) ) v(CriteriaNum)
    WHERE
        (c.Criteria LIKE '%<%'  AND n.[Value] <  v.CriteriaNum) OR
        (c.Criteria LIKE '%<=%' AND n.[Value] <= v.CriteriaNum) OR
        (c.Criteria LIKE '%=%'  AND n.[Value] =  v.CriteriaNuma) OR
        (c.Criteria LIKE '%>%'  AND n.[Value] >  v.CriteriaNum) OR
        (c.Criteria LIKE '%>=%' AND n.[Value] >= v.CriteriaNum)
    ORDER BY c.SortOrder
) c

【讨论】:

  • 提示 OP 说列的名称(在本例中为 Value)可以是动态的,并且像 BETWEEN 这样的运算符也可以在其中。 ?
  • @Charlieface 和 Larnu 感谢您的反馈!我刚刚更新了问题并发布了我尝试过的代码。
  • 太棒了!感谢@Charlieface 的帮助!我对代码打了个嗝,因此需要将 CAST(RIGHT(c.Criteria, CHARINDEX(' ', REVERSE(c.Criteria)) - 1) AS int) 调整为 CAST(RIGHT(c.Criteria,LEN(c.Criteria))标准)-cast(patINDEX('%[0-9]%', c.Criteria) as int)+1) AS int)
猜你喜欢
  • 1970-01-01
  • 2013-12-22
  • 2011-05-04
  • 2015-03-31
  • 2021-06-03
  • 1970-01-01
  • 1970-01-01
  • 2013-06-18
  • 1970-01-01
相关资源
最近更新 更多