从包含CSV的VARCHAR中选择具有新DISTINCT的行 查询选项1 查询选项2

问题描述

我有一个Oracle数据库表,其中包含一个名为分类的字段,即VARCHAR。 VARCHAR是CSV(使用半冒号)。示例:

;CHR;
;OTR;CHR;ROW;
;CHA;ROW;
;OTR;ROW;

我想拉出所有行,并且CSV中的行仅具有与其他行不同的值。只要行具有新的不同值,就可以具有先前找到的值。

例如,上述数据集中的数据为:

;CHR;
;OTR;CHR;ROW;
;CHA;ROW;

如果我只是这样做:

Select DISTINCT Classification from Table1

由于总体VARCHAR不同,我得到的行重叠了不同的值。

我可以使用以下方法获取所有不同的值:

select LISTAGG(val,',') WITHIN GROUP ( ORDER BY val ) as final
FROM
(
select distinct  trim(regexp_substr("Classification",'[^;]+',1,level) ) as val 
  from Table1
   connect by regexp_substr("Classification",'[^,]+',level) is not null
  ORDER BY val
 )

给我的

FINAL
CHA,CHR,OTR,ROW

但是无法建立链接以为每个唯一值提取一条记录

SQL可能吗?

编辑:这是由大公司创建的数据库,我的公司购买了该产品。现在,我负责为BI挖掘后端数据库的数据,并且绝对无法控制数据库结构。

没有冒犯,但我在研究的问题中看到许多答案,指出“做更好的数据库设计/规范化”,虽然我同意大多数,但我无法控制数据库,并且由于以下原因而寻求SO帮助:这,而不是对不良数据库设计的嘲笑。

如果我冒犯了任何人,我深表歉意

没有父母/子女关系。我看不到对象层,但是我假定在传播给客户端之前,这些值在对象层中已更改,因为在实际数据库中没有指向它们的链接

说明:

我看到两种解决方法:

1:一个基于VARCHAR CSV(分类)中新的唯一值提取1行的select语句

2:使用我的select语句循环遍历并在VARCHAR CSV(Classification)中拉出包含该值的一行

谢谢大家的投入。我赞成那些为我工作的人。最后,我将使用我开发的代码,因为我可以轻松地为分析师希望的结果操纵输出(到CSV)。

解决方法

这是一种处理方法:

  • 将行号分配给原始CSV数据
  • 拆分CSV->行
  • 现在分配拆分的CSV值行号,并按照第一步中的CSV顺序进行排序
  • 返回上一步的行号= 1的所有行
  • 返回CSV的唯一列表

例如:

with tab as (
  select ';CHR;' str from dual union all
  select ';OTR;CHR;ROW;' str from dual union all
  select ';CHA;ROW;' str from dual union all
  select ';OTR;ROW;' str from dual 
),ranks as (
  select row_number() over ( order by str ) rn,tab.* from tab
),rws as (
  select trim ( regexp_substr(str,'[^;]+',1,level ) ) as val,rn,str
  from   ranks
  connect by regexp_substr ( str,level ) is not null
  and prior rn = rn
  and prior sys_guid () is not null
),rns as (
  select row_number () over (
           partition by val
           order by rn
         ) val_rn,r.*
  from   rws r
)
  select distinct str
  from   rns
  where  val_rn = 1;
  
STR             
;CHA;ROW;        
;OTR;CHR;ROW;    
;CHR;      
,

这是一个临时解决方案建议,如果通用答案产生了次优的性能并且某些限制已得到满足:

  • 所有按键的长度都是固定的
  • 键的最大数量是已知的

除了解析CSV字符串外,您还可以使用此查询(为更长的字符串再添加UNION ALL

with tab as (
  select ';CHR;' str from dual union all
  select ';OTR;CHR;ROW;' str from dual union all
  select ';CHA;ROW;' str from dual union all
  select ';OTR;ROW;' str from dual 
),tab2 as (
select str,substr(str,2,3) val  from tab union all
select str,6,3) val  from tab where substr(str,3) is not null union all
select str,10,3) is not null)
select * from tab2;

结果

STR           VAL         
------------- ------------
;CHR;         CHR         
;OTR;CHR;ROW; OTR         
;CHA;ROW;     CHA         
;OTR;ROW;     OTR         
;OTR;CHR;ROW; CHR         
;CHA;ROW;     ROW         
;OTR;ROW;     ROW         
;OTR;CHR;ROW; ROW  

现在,您只需查找每个键的首次出现,并获得所有与第一次出现的不同的字符串

我正在重用Chris Saxon解决方案中的方法

with tab as (
  select ';CHR;' str from dual union all
  select ';OTR;CHR;ROW;' str from dual union all
  select ';CHA;ROW;' str from dual union all
  select ';OTR;ROW;' str from dual 
),3) is not null),tab3 as (
select STR,VAL,row_number() over (partition by val order by str) rn
from tab2)
select distinct str
from tab3
where rn = 1
,

您已经非常接近,因为您已经获得了不同值的列表。您可以使用该列表查找包含该唯一值的行,而不是将它们与LISTAGG组合在一起。下面是两个单独的查询,它们将为每个唯一值返回一个分类。您可以对它们进行尝试,然后根据表中的数据查看哪种方法效果更好。

查询选项1

WITH
    table1 (classification)
    AS
        (SELECT ';CHR;' FROM DUAL
         UNION ALL
         SELECT ';OTR;CHR;ROW;' FROM DUAL
         UNION ALL
         SELECT ';CHA;ROW;' FROM DUAL
         UNION ALL
         SELECT ';OTR;ROW;' FROM DUAL),dist_vals (val)
    AS
        (    SELECT DISTINCT TRIM (REGEXP_SUBSTR (classification,LEVEL))    AS val
               FROM Table1
         CONNECT BY LEVEL < REGEXP_COUNT (classification,';'))
SELECT val,classification
  FROM (SELECT dv.val,t.classification,ROW_NUMBER () OVER (PARTITION BY dv.val ORDER BY t.classification)     AS occurence
          FROM dist_vals dv,table1 t
         WHERE t.classification LIKE '%;' || dv.val || ';%')
 WHERE occurence = 1;

查询选项2

WITH
    table1 (classification)
    AS
        (SELECT ';CHR;' FROM DUAL
         UNION ALL
         SELECT ';OTR;CHR;ROW;' FROM DUAL
         UNION ALL
         SELECT ';CHA;ROW;' FROM DUAL
         UNION ALL
         SELECT ';OTR;ROW;' FROM DUAL),';'))
SELECT dv.val,(SELECT classification
          FROM table1
         WHERE classification LIKE '%;' || dv.val || ';%' AND ROWNUM = 1)
  FROM dist_vals dv;
,

我以这种方式解决了这个问题,并且运行速度很快(即使添加了我对其他表的所有联接)。将尽我所能测试其他答案并决定最佳答案(如果其他方法可以正常工作,则其他方法比我的看起来更好,因为我宁愿不使用dbms_output)

DECLARE
 v_search_string varchar2(4000);
 v_classification varchar2(4000);
 
 BEGIN
    select LISTAGG(val,',') WITHIN GROUP ( ORDER BY val ) as final
    INTO v_search_string
FROM
(
select distinct  trim(regexp_substr("Classification",level) ) as val
  from mytable
   connect by regexp_substr("Classification",'[^,]+',level) is not null
  ORDER BY val
 );
 
 FOR i IN
     (SELECT trim(regexp_substr(v_search_string,LEVEL)) l
     FROM dual
       CONNECT BY LEVEL <= regexp_count(v_search_string,')+1
     )
     LOOP
        SELECT   "Classification"
        INTO v_classification
       
        FROM mytable
 
        WHERE "Classification"  LIKE '%' || i.l || '%'
       
        FETCH NEXT 1 ROWS ONLY;
      dbms_output.put_line(v_classification);
    END LOOP;
 
 
 END;

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...