【问题标题】:SQL: How to check a value in column is equal to the sum of any combination of the other columns in a rowSQL:如何检查列中的值是否等于行中其他列的任意组合的总和
【发布时间】:2017-07-28 18:40:32
【问题描述】:

我遇到了这样一个表结构的场景:

Column0 Column1 Column2 Column3 Column4 Column5 
100     U       V       X       Y       Z

我需要找出是否:

  • 100 = UVXYZ
  • 100 = X + Y + Z + V
  • 100 = X + Y
  • 100 = V + Z

关于如何实现这一目标的任何建议?

案例陈述将花费很长时间来编写。我不会写 PL/SQL 代码,但我对它有点熟悉。

【问题讨论】:

  • 您是否关心 what 列能达到 100 或是否有?如果所有整数都是 2 的幂,这会更容易
  • @Alex 不,我不在乎哪些列是 100。如果 column0 的值每行都发生变化怎么办?有没有我可以利用的数学函数?
  • 虽然编写一个接受 n 个数字数组并测试 1 到 n 个元素的任意组合的函数可能是一个有趣的练习匹配一个目标值,在这种情况下,只需对组合进行硬编码就可以减少工作量。

标签: sql oracle sum combinations


【解决方案1】:

编写Case 语句并不像您暗示的那么难。我在大约 20 秒内使用文本编辑器生成了以下 Case 语句,它应该可以处理您示例中的所有情况:

Select  Case
            When Column0 = Column1  Then 1
            When Column0 = Column2  Then 1
            When Column0 = Column3  Then 1
            When Column0 = Column4  Then 1
            When Column0 = Column5  Then 1

            When Column0 = Column1 + Column2    Then 1
            When Column0 = Column1 + Column3    Then 1
            When Column0 = Column1 + Column4    Then 1
            When Column0 = Column1 + Column5    Then 1

            When Column0 = Column2 + Column3    Then 1
            When Column0 = Column2 + Column4    Then 1
            When Column0 = Column2 + Column5    Then 1

            When Column0 = Column3 + Column4    Then 1
            When Column0 = Column3 + Column5    Then 1

            When Column0 = Column4 + Column5    Then 1

            When Column0 = Column1 + Column2 + Column3  Then 1
            When Column0 = Column1 + Column2 + Column4  Then 1
            When Column0 = Column1 + Column2 + Column5  Then 1

            When Column0 = Column1 + Column3 + Column4  Then 1
            When Column0 = Column1 + Column3 + Column5  Then 1

            When Column0 = Column1 + Column4 + Column5  Then 1

            When Column0 = Column2 + Column3 + Column4  Then 1
            When Column0 = Column2 + Column3 + Column5  Then 1

            When Column0 = Column2 + Column4 + Column5  Then 1

            When Column0 = Column3 + Column4 + Column5  Then 1

            When Column0 = Column1 + Column2 + Column3 + Column4    Then 1
            When Column0 = Column1 + Column2 + Column3 + Column5    Then 1

            When Column0 = Column1 + Column3 + Column4 + Column5    Then 1

            When Column0 = Column2 + Column3 + Column4 + Column5    Then 1

            When Column0 = Column1 + Column2 + Column3 + Column4 + Column5  Then 1

            Else 0
        End As SumOfOtherColumns
From    YourTable

【讨论】:

  • 这适用于我的问题!我提到 CASE 语句写得太多的原因是,如果我跨越一个包含更多列的表,因此我正在寻找一些数学的东西。
  • @Siyual 5 个案例丢失。 1+2+4、1+2+5、1+3+5、2+3+5、1+2+3+5
  • 抱歉漏了一个,1+2+4+5也漏了
  • @LauDec 啊,我明白了——当我使用电脑时,我必须纠正它。谢谢!
  • 如果以case column0 when ...开头,则无需每次都重复when column0 =
【解决方案2】:

即使答案已经被接受,这也是我的替代解决方案。就性能而言,它很可能更差(待测试),但我认为值得展示,原因如下:

如您所见,有 5 列已经很容易漏掉一些案例,只有 31 个案例需要检查。如果您添加一列,则为 63 次检查,下一列为 127 次...您不必在这里担心,因为它会动态生成所有案例

另一个有趣的地方是,如果您有兴趣查看每一行的详细信息以及它匹配的原因,它会随查询免费提供。你只需要选择子视图

最后一点是,我认为这在学术上很有趣。该解决方案包含 UNPIVOTing 数据、递归自连接、动态表达式评估。当然,我不客观,但这样做很有趣:)

--Table and data to test the query
create table my_table ( column0 number, column1 number, column2 number, 
                        column3 number, column4 number, column5 number );

INSERT INTO my_table values (100,20,20,10,40,10); -- must match on the sum of 5 columns
INSERT INTO my_table values (100,50,200,300,150,250); -- must not match
INSERT INTO my_table values (100,50,50,100,150,250); -- must match twice ( on col1+col2 and col3 )

-- If your table has a unique key, you can remove the datas_with_id and put
-- your table directly in the unpivoted_data subquery
with datas_with_id  as ( select rowid as row_id, t.* from my_table t),
     unpivoted_data as ( select row_id, column0 as sum_to_check, column_name, column_value 
                         from datas_with_id 
                         unpivot ( column_value for column_name in (column1,column2,column3,column4,column5))),
     calculated_sum as ( select row_id, xmlquery(sys_connect_by_path(u.column_value,'+')||' = '|| sum_to_check
                         returning content).getStringVal() result
                         from unpivoted_data u connect by nocycle prior column_name>column_name 
                         and prior row_id=row_id and level < 6)
select * from my_table where rowid in ( select row_id from calculated_sum where result = 'true' )

如果要添加另一列,在unpivot子句中添加,级别加1即可

如果你添加 sys_connect_by_path(u.column_name,'+')||' ='||在calculated_sum中sum_to_check,你可以看到每一个匹配的公式

【讨论】:

    【解决方案3】:

    这里我写了一些你可以动态使用的代码

    1-首先我创建表并为其生成一行

    create table my_table ( column0 number, column1 number, column2 number, 
                            column3 number, column4 number, column5 number );
    
    INSERT INTO my_table values (100,20,20,10,40,10);
    

    2-创建类型如下行

    create or replace type Key_Value_Typ as object
    (
    
      column_name   varchar2(100),
      column_val    varchar2(100)  
    )
    

    3-你可以在这里看到一个例子

    declare
      lv_count number;
      lv_row   my_table%rowtype;
    
      type my_list is table of Key_Value_Typ;
      list1 my_list;
    
      ------------------------------------------------------------
    
      -- function for control column condition
    
      function check_fun(
        lv_list my_list, 
          -- the function input is array of key value include column name and column value
          -- you can implement your code here, for example as input parameter declare your own parameters 
        -- for example
        --   lv_column0 my_table.column0%type;
        lv_where varchar2
        -- the condition that you want check it
        ) return number is
    
        lv_str   varchar2(200);
        lv_count number;
      begin
    
        lv_str := lv_where;
    
        for i in 1 .. lv_list.count Loop
    
          lv_str := replace(lv_str,
                            lv_list(i).column_name,
                            lv_list(i).column_val);
    
        ENd Loop;
    
        execute immediate 'select count(*) from dual where ' || lv_str
          into lv_count;
    
        -- if the function return 1 result is true and o means result is false
        return lv_count;
    
      end check_fun;
    
      ------------------------------------------------------------
    
    begin
      -- fetch data from my_table and get one of them 
      select * into lv_row from my_table where column0 = 100;
    
      -- create list of data include columns and values
      list1 := my_list(Key_Value_Typ('column0', lv_row.column0),
                       Key_Value_Typ('column1', lv_row.column1),
                       Key_Value_Typ('column2', lv_row.column2),
                       Key_Value_Typ('column3', lv_row.column3),
                       Key_Value_Typ('column4', lv_row.column4),
                       Key_Value_Typ('column5', lv_row.column5));
    
    
      lv_count := check_fun(list1, 'column0=column1+column2+column3+column4+column5');
    
      dbms_output.put_line('result ()' || lv_count);
    
    end;
    

    【讨论】:

      猜你喜欢
      • 2022-07-22
      • 2023-01-31
      • 2022-12-03
      • 1970-01-01
      • 2015-02-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多