SQL 对多行/列进行反透视,但将行分组在一起,并以相同的顺序选择它们

gui*_*nty 5 oracle pivot

这是一个复杂的解释,很抱歉,这将是一堵文字墙。我正在处理预先存在的代码并试图找出解决方案,所以我无法改变其中任何一个工作的核心方式。

本质上,我在一个项目中有一系列表单,所有这些表单都有一个分配给它们的状态。状态具有指定的优先顺序,这将逐渐向上级访问。当前的工作方式使用以下 sql 语句:

select state_code
 from (select ' || v_priorstr || ' 
    from m_object mo, m_form mf
      where mo.id = mf.id
        and mf.formlayout_id = :1
        and mo.v = :2)
 unpivot
   (
    state_counts
    for state_code in (' || v_priorstr || ')
   ) where state_counts = 1 and rownum = 1
Run Code Online (Sandbox Code Playgroud)

其中 v_priorstr 是逗号分隔的状态字符串,按优先顺序排列。例如,考虑以下字符串:“Blank,Error,InProgress,Completed”。m_object 表包含这些列中的每一列,用 0 或 1 显示适用的状态。(表单可以有多个状态)此查询选择最高优先级的状态,然后将用于显示该表单的状态图标。

处理重复表单时会出现问题(在一个链接下可以有多次迭代)。它们都在导航系统中共享一个状态图标。

虽然通常这很好用,因为 SQL 会自然地按照您请求的顺序显示列,并且它们在未旋转时保持该顺序。但是,对于多个表单,因此也有多个行,第一个表单/行的所有状态都绝对优先于所有表单的状态。

例如,如果我有两个这样的重复形式,第一个是“已完成”,第二个是“错误”,上面的查询(没有 rownum = 1)将返回以下两行:

Completed
Error
Run Code Online (Sandbox Code Playgroud)

到目前为止,我尝试了以下解决方案:

没有 Order By 的 Group By 将取消订单。我不能使用基于案例的 Order By,因为状态名称可能会改变。

简而言之,我需要它按照通过该字符串传入的顺序对状态名称进行排序或分组。

Lei*_*fel 1

这使用与 jonearles (+1)(已删除)相同的基本技术,但消除了PIVOTand ,只需要一个将字符串转换为CASE语句的函数。

SELECT substr(
      MIN (
         CASE WHEN Blank=1 THEN '1 Blank' 
              WHEN Error=1 THEN '2 Error' 
              WHEN InProgress=1 THEN '3 InProgress' 
              WHEN Completed=1 THEN '4 Completed' 
         END
         )
   ,3) state_code
FROM m_object mo
JOIN m_form mf ON mo.id = mf.id
WHERE mo.v = :2 AND mf.formlayout_id = :1;  
Run Code Online (Sandbox Code Playgroud)

我假设数据看起来像这样:

drop table m_object;
create table m_object as 
   (select 10 id, 0 blank, 0 error, 0 inprogress, 1 completed, 99 v from dual);
insert into m_object values (11,1,0,0,0,99);
insert into m_object values (12,0,0,0,1,99);
insert into m_object values (13,0,1,1,0,99);

drop table m_form;
create table m_form as (select 10 id, 20 formlayout_id from dual);
insert into m_form values (11,21);
insert into m_form values (12,22);
insert into m_form values (13,20);
Run Code Online (Sandbox Code Playgroud)

如果该函数是在 PL/SQL 中完成的,则该函数可能如下所示。它只是拼凑在一起,仅用于说明如何构建 SQL 语句,而不是良好编码的示例。

set serveroutput on format wrapped

DECLARE
vPassed Varchar2(500) := 'Blank,Error,InProgress,Completed';

   Function MakeCase(pOriginal In Varchar2) Return Varchar2 Is
      vBuilt     Varchar2(500);
      vWord      Varchar2(100);
      vChar      Char(1);
      vWordCount Number(1) := 1;
   Begin
      For vLoop In 1..Length(pOriginal) Loop
         vChar := substr(pOriginal,vLoop,1);
         If (vChar = ',') Then
            If (vBuilt IS NULL) Then
               vBuilt := 'CASE WHEN ';
            End If;
            vBuilt := vBuilt || vWord || '=1 THEN '''
               || to_char(vWordCount,'FM0') || ' ' || vWord || ''' WHEN ';
            vWord := '';
            vWordCount := vWordCount + 1;            
         Else                  
            vWord := vWord || vChar;            
         End If;
      End Loop;
      vBuilt := vBuilt || vWord || '=1 THEN ''' 
         || to_char(vWordCount,'FM0') || ' ' || vWord || ''' ';
      vBuilt := vBuilt || ' END ';
      Return vBuilt;
   End;

BEGIN
   vPassed := MakeCase(vPassed);
   DBMS_Output.Put_Line(vPassed);

   --Use vPassed here to build SQL statement.
END;
/
Run Code Online (Sandbox Code Playgroud)