工作是在查询中参数化过滤条件,以便用户可以动态选择过滤条件并生成查询。 表中有3列,col2有过滤值,col3有AND / OR过滤条件。请帮帮我。
col1 col2 col3
------------------------------
v1 sal1 > 10
v2 sal2 > 500
v3 v1 OR v2 (comments : col2 is NULL, col3 is v1 OR v2)
v4 v1 AND v2 (comments : col2 is NULL, col3 is v1 AND v2)
v5 v3 AND v6 (comments: col2 is NULL, col3 is v1 AND v2)
v6 amt > 20
选择v3时
v3 (sal1> 10 OR sal2 > 500)
选择v4时
v4 (sal1> 10 AND sal2 > 500)
选择v5时
v5 ((sal1> 10 OR sal2 > 500) AND amt > 20)
附加了示例脚本,它将使用动态SQL根据用户选择生成SQL。
CREATE TABLE TEST_2
(
COL1 VARCHAR2(4000),
COL2 VARCHAR2(4000),
COL3 VARCHAR2(4000)
);
insert into TEST_2 values ('v1', 'sal1 > 10', null);
insert into TEST_2 values ('v2', 'sal2 > 500', null);
insert into TEST_2 values ('v3', null, 'v1 OR v2');
insert into TEST_2 values ('v4', null, 'v1 AND v2');
insert into TEST_2 values ('v5', null, 'v3 AND v6');
insert into TEST_2 values ('v6', 'amt > 20', null);
commit;
col1 col2 col3
v1 sal1 > 10
v2 sal2 > 500
v3 v1 OR v2
v4 v1 AND v2
v5 v3 AND v6
v6 amt > 20
答案 0 :(得分:0)
这是一个使用Regex构建递归解析器的C#解决方案。我创建了一个类QueryTemplate
来存储表的内容。
public class QueryTemplate
{
public string Id { get; set; }
public string Term { get; set; }
public string Expression { get; set; }
}
作为测试设置,我按如下方式初始化数据:
private Dictionary<string, QueryTemplate> _templates;
public void Test()
{
_templates = new Dictionary<string, QueryTemplate> {
["v1"] = new QueryTemplate { Id = "v1", Term = "sal1 > 10" },
["v2"] = new QueryTemplate { Id = "v2", Term = "sal2 > 500" },
["v3"] = new QueryTemplate { Id = "v3", Expression = "v1 OR v2" },
["v4"] = new QueryTemplate { Id = "v4", Expression = "v1 AND v2" },
["v5"] = new QueryTemplate { Id = "v5", Expression = "v3 AND v6" },
["v6"] = new QueryTemplate { Id = "v6", Term = "amt > 20" },
};
Console.WriteLine(CreateSqlCondition("v5"));
}
控制台输出:
<(>(sal1> 10)OR(sal2> 500))AND(amt> 20)
条件以递归方式构建:
public string CreateSqlCondition(string id)
{
if (_templates.TryGetValue(id, out QueryTemplate qt)) {
if (qt.Term != null) {
return qt.Term;
} else { // Expression
// Assuming that all Ids start with "v", but not column names.
var idMatches = Regex.Matches(qt.Expression, @"\bv\w+\b");
string expr = qt.Expression;
foreach (var match in idMatches.Cast<Match>().Reverse()) {
// Replace in reverse order to preserve the match positions.
string idName = match.Value;
string cond = CreateSqlCondition(idName);
string prefix = (expr.Substring(0, match.Index));
string suffix = expr.Substring(match.Index + match.Length);
expr = $"{prefix}({cond}){suffix}";
}
return expr;
}
} else {
throw new ArgumentException($"Query template \"{id}\" not found.");
}
}
我还在每个术语周围添加括号以保留AND / OR运算符的优先级。
问题是区分ID和列名称。也许你应该使用Id名称,如$ 1,$ 2,$ 3 ......来避免混淆。此外,对术语和表达式仅使用一列,将允许您组合ID和列名称。示例(使用&#34; $&#34; ID):$5 AND amt <= 20
。当然,我的CreateSqlCondition
方法必须适应它。
正则表达式\bv\w+\b
表示
\b word boundary (beginning of word)
v the character v
\w+ all following word characters (letters, digits and underscores)
\b word boundary (end of word)
如果您选择使用&#34; $&#34;而不是&#34; v&#34;,用&#34; \&#34;:
来逃避它@"\b\$\w+\b"
答案 1 :(得分:0)
好的,我这样做很有趣,因为我从未在SQL中实现过binary expression tree。但它只会让你在未来的路上受苦。
select listagg(dyn_where) within group (order by bintree_order) as dynamic_where from
(select c.*, rpad(c.syspath, max(lvl) over (), '1') as bintree_order from (
select level as lvl,
replace(sys_connect_by_path(case when sign(nvl(instr(prior col3, col1)-floor(length(prior col3)/2),0)) <= 0 then '0' else '2' end, ' '), ' ') as syspath,
col2 || substr(col3, 3, length(col3)-4) as dyn_where
from TEST_2
connect by instr(prior col3, col1) > 0
start with col1 = 'v5'
) c
);
此查询当前依赖于col1为2个字符长(用于在col3上执行substr)。如果您更改表结构以将COL3
拆分为3列,则不需要这样做 - 一个用于第一个操作数,一个用于布尔运算符(例如OR),另一个用于第二个操作数。在这个例子中,col3和col5实际上可能是col1的外键,你可以更容易。哈,比较。还是个坏主意。 :)
CREATE TABLE TEST_2
(
COL1 VARCHAR2(4000), -- primary key
COL2 VARCHAR2(4000), -- binary tree leaf node value
COL3 VARCHAR2(4000), -- operator node - link to left child
COL4 VARCHAR2(4000), -- operator node - operand (e.g. AND)
COL5 VARCHAR2(4000) -- operator node - link to right child
);