我正在一个基于Java-Oracle的项目中工作,在那里我遇到了一个问题,在我看来这需要分析解决方案。
我正在寻找基于SQL查询或任何算法或任何免费分析工具的解决方案,我可以根据这些解决方案获得所需的结果。
问题陈述:
可以说我有一个下表,其中A-D列为列,最后一列为Score,我想为每个列的值找到一个标准,当在SQL中将where子句组合在一起时,我总会为Score列提供正值。那么,基本上,A-D列的哪种组合总能给我带来积极的成绩?
columnA|columnB|columnC|columnD|Score
1 40 10 3 -20
0 40 2 3 10
0 10 3 3 20
1 15 3 3 -5
0 10 2 2 -15
0 15 6 3 -10
以上数据集的预期结果:-
对以上数据集的直观解释为我提供了条件:“ColumnA = 0且columnB> 10且columnC 0”。 (从视觉上看,其清晰的columnD无效)。
请注意,以上数据集是为了简化。实际上,我的项目包含约40列和近2500行。确保每一列都有有限范围的值是一回事。
从OP复制的以下信息如下
这是我开始使用的算法(如果有人认为我的方向正确,则需要输入以进一步完善它):
准备:创建一个所有可能表达式的列表,例如A = 0,B> 10,C
让我们称之为“表达式”变量。
第一次运行的Alogrithm:
设置totalNegativeRows =从我的表中选择count(*),其中score
设置positivePercentage =查找满足此expr的totalPositiveRows的百分比; //就像如果总得分大于0的100行中的60行满足expr,则positivePercentage = 60%
set negativePercentage= find percentage of totalNegativeRows which satisfy this expr; //like if 40 rows out of total 100 rows having score<0 satisfy expr , then negativePercentage=40%
set diffPercentage=positivePercentage-negativePercentage;
设置initalPositivePercentage =选择相应的positivePercentage值;
设置initalNegativePercentage =选择相应的negativePercentage值;
我的想法是,我现在需要继续扩展initalexpr,直到initalNegativePercentage变为0。
后续运行的算法,直到initalNegativePercentage变为0:-
设置newexpr = initialexpr +“和” + expr;
设置positivePercentage =查找满足newexpr的totalPositiveRows的百分比;
设置negativePercentage =查找满足newexpr的totalNegativeRows的百分比;
//计算它减少了多少负百分比?
设置positiveReduction = initalPositivePercentage-positivePercentage;
设置negativeReduction = initalNegativePercentage-negativePercentage;
if(negativeReduction> = positiveReduction)
//记下来
其他
//丢弃它
设置initialexpr =选择具有负负最大值的expr
设置initalPositivePercentage =选择相应的值;
设置initalNegativePercentage =选择相应的值;
请评论。
最佳答案
以下是蛮力解决方案。对于理论计算机科学站点,这可能也是一个好问题。我认为这是一个类似于Boolean satisfiability的NP完全问题,但这只是一个疯狂的猜测。解决这个问题的方法可能更聪明,但我认为我不会找到。
基本思想是将每个表达式与列的每个不同值交叉连接,然后对所有列进行交叉连接。该表将与每个表达式列表一起查询,并为正分数和负分数生成一个计数。如果该表达式返回预期的正分数,但没有一个负分数,则该表达式有效。
假设您仅使用表达式>
,<
和=
。每个新的列或表达式都会使此问题成倍地变慢。
测试模式
drop table table1;
create table table1(a number, b number, c number, d number, score number);
insert into table1
select 1, 40, 10, 3, -20 from dual union all
select 0, 40, 2, 3, 10 from dual union all
select 0, 10, 3, 3, 20 from dual union all
select 1, 15, 3, 3, -5 from dual union all
select 0, 10, 2, 2, -15 from dual union all
select 0, 15, 6, 3, -10 from dual;
代码墙
declare
v_inline_view varchar2(32767);
v_inline_views clob;
v_inline_view_counter number := 0;
v_and_expression varchar2(32767);
v_query clob;
v_sqls sys.odcivarchar2list;
v_dynamic_query_counter number := 0;
begin
--#1: Create inline views.
--One for every combination of expression and distinct value, per column.
for inline_views in
(
--Inline view for every possible expression for each column.
select
replace(q'[
(
select *
from
(
--Possible expressions.
select distinct
case
when operator is null then null
else ' AND %%COLUMN%% '||operator||' '||%%COLUMN%%
end %%COLUMN%%_expression
from
--All operators.
(
select '>' operator from dual union all
select '<' operator from dual union all
select '=' operator from dual union all
select null operator from dual
)
--All distinct values.
cross join
(
select distinct %%COLUMN%% from table1
)
)
--where %%COLUMN%%_expression is null or %%COLUMN%%_expression %%EXPRESSION_PERFORMANCE_EXCLUSIONS%%
)
]', '%%COLUMN%%', column_name) inline_view
from user_tab_columns
where table_name = 'TABLE1'
and column_name <> 'SCORE'
order by column_name
) loop
--Assign to temorary so it can be modified.
v_inline_view := inline_views.inline_view;
--#1A: Optimize inline view - throw out expressions if they don't return any positive results.
declare
v_expressions sys.odcivarchar2list;
v_expressions_to_ignore varchar2(32767);
v_has_score_gt_0 number;
begin
--Gather expressions for one column.
execute immediate v_inline_view bulk collect into v_expressions;
--Loop through and test each expression.
for i in 1 .. v_expressions.count loop
--Always keep the null expression.
if v_expressions(i) is not null then
--Count the number of rows with a positive score.
execute immediate 'select nvl(max(case when score > 0 then 1 else 0 end), 0) from table1 where '||replace(v_expressions(i), ' AND ', null)
into v_has_score_gt_0;
--If the expression returns nothing positive then add it to exclusion.
if v_has_score_gt_0 = 0 then
v_expressions_to_ignore := v_expressions_to_ignore||','''||v_expressions(i)||'''';
end if;
end if;
end loop;
--Convert it into an IN clause.
if v_expressions_to_ignore is not null then
--Remove comment, replace placeholder with expression exclusions.
v_inline_view := regexp_replace(v_inline_view, '(.*)(--where)( .* )(%%EXPRESSION_PERFORMANCE_EXCLUSIONS%%)(.*)', '\1where\3 not in ('||substr(v_expressions_to_ignore, 2)||')');
end if;
end;
--Aggregate and count inline views.
if v_inline_view_counter <> 0 then
v_inline_views := v_inline_views||'cross join';
end if;
v_inline_views := v_inline_views||v_inline_view;
v_inline_view_counter := v_inline_view_counter + 1;
end loop;
--#2: Create an AND expression to combine all column expressions.
select listagg(column_name||'_expression', '||') within group (order by column_name)
into v_and_expression
from user_tab_columns
where table_name = 'TABLE1'
and column_name <> 'SCORE';
--#3: Create a that will create all possible expression combinations.
v_query :=
replace(replace(q'[
--8281 combinations
select '
select
'''||expressions||''' expression,
nvl(sum(case when score > 0 then 1 else 0 end), 0) gt_0_score_count,
nvl(sum(case when score <= 0 then 1 else 0 end), 0) le_0_score_count
from table1
where 1=1 '||expressions v_sql
from
(
--Combine expressions
select %%AND_EXPRESSION%% expressions
from
%%INLINE_VIEWS%%
) combined_expressions
]', '%%AND_EXPRESSION%%', v_and_expression), '%%INLINE_VIEWS%%', v_inline_views);
--TEST: It might be useful to see the full query here.
--dbms_output.put_line(v_query);
--#4: Gather expressions.
--With larger input you'll want to use a LIMIT
execute immediate v_query
bulk collect into v_sqls;
--#5: Test each expression.
--Look for any queries that return the right number of rows.
for i in 1 .. v_sqls.count loop
declare
v_expression varchar2(4000);
v_gt_0_score_count number;
v_le_0_score_count number;
begin
execute immediate v_sqls(i) into v_expression, v_gt_0_score_count, v_le_0_score_count;
v_dynamic_query_counter := v_dynamic_query_counter + 1;
--TODO: Dynamically generate "2".
if v_gt_0_score_count = 2 and v_le_0_score_count = 0 then
dbms_output.put_line('Expression: '||v_expression);
end if;
exception when others then
dbms_output.put_line('Problem with: '||v_sqls(i));
end;
end loop;
dbms_output.put_line('Queries executed: '||v_dynamic_query_counter);
end;
/
结果
结果显示正确。它们与您的稍有不同,因为“columnB> 10”不正确。
Expression: AND A = 0 AND C < 6 AND D = 3
Expression: AND A = 0 AND C < 6 AND D > 2
Expression: AND A < 1 AND C < 6 AND D = 3
Expression: AND A < 1 AND C < 6 AND D > 2
Queries executed: 441
问题
这种暴力方法至少在两种方面效率极低。即使对于这个简单的示例,它也需要6370个查询。并且结果可能包括重复的内容,这些重复内容很难减少。也许您会很幸运,并且解决方案很少,您可以盯着他们。
您可以采取一些措施来提高查询性能。最简单的方法是单独检查每个条件,如果没有“获得”任何计数,则将其丢弃。
优化
排除不返回任何正结果的单个表达式。使用样本数据,这会将查询执行的次数从6370减少到441。
并行运行该过程还可将性能提高一个数量级。它可能需要并行的流水线功能。
但是,即使性能提高100倍也可能无法解决NP完全问题。您可能需要根据样本数据找到其他一些“捷径”。
通过取消注释
dbms_output.put_line
语句之一,可能有助于打印出生成测试查询的查询。添加count(*)
以查看将执行多少个查询,并使用较小的数据集运行以估计所需的时间。如果估计时间是十亿年,并且您想不出任何使暴力破解方法更快工作的技巧,那么可能是时候在https://cstheory.stackexchange.com/上问这个问题了