我正在一个基于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:
  • set totalPositiveRows =从我的表中选择count(*),其中score> 0;

    设置totalNegativeRows =从我的表中选择count(*),其中score
  • 对于表达式中的每个expr,请计算以下三个变量
    设置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;
    
  • 设置initialexpr =选择具有diffPercentage最大值的expr
    设置initalPositivePercentage =选择相应的positivePercentage值;
    设置initalNegativePercentage =选择相应的negativePercentage值;
    我的想法是,我现在需要继续扩展initalexpr,直到initalNegativePercentage变为0。

  • 后续运行的算法,直到initalNegativePercentage变为0:-
  • 对于表达式中的每个expr,请计算以下三个变量
    设置newexpr = initialexpr +“和” + expr;
    设置positivePercentage =查找满足newexpr的totalPositiveRows的百分比;
    设置negativePercentage =查找满足newexpr的totalNegativeRows的百分比;

    //计算它减少了多少负百分比?
    设置positiveReduction = initalPositivePercentage-positivePercentage;
    设置negativeReduction = initalNegativePercentage-negativePercentage;
    if(negativeReduction> = positiveReduction)
    //记下来
    其他
    //丢弃它
  • 选择给出最大负减少量的expr,它成为新的初始expr。
    设置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/上问这个问题了

    07-24 22:24