创建一个包含具有多个值的列的表视图

huangapple go评论64阅读模式
英文:

Create a view of a table with a column that has multiple values

问题

我有一个类似以下的表(Table1):

Col1 Col2
First Code1,Code2,Code3
Second Code2

因此,Col2可以包含多个逗号分隔的值。我有另一个包含如下内容的表(Table2):

ColA ColB
Code1 Value1
Code2 Value2
Code3 Value3

我需要创建一个视图,将这两个表(Table1和Table2)联接起来,返回类似以下的结果:

Col1 Col2
First Value1,Value2,Value3
Second Value2

这是可能的吗?(如果有帮助的话,我在Oracle数据库上。)

英文:

I have a table (Table1) like the following:

Col1 Col2
First Code1,Code2,Code3
Second Code2

So Col2 can contain multiple values comma separated, I have another table (Table2) that contains this:

ColA ColB
Code1 Value1
Code2 Vaue2
Code3 Vaue3

I need to create a view that joins the two tables (Table1 and Table2) and returns something like this:

Col1 Col2
First Value1,Value2,Value3
Second Value2

Is that possible? (I'm on Oracle DB if that helps.)

答案1

得分: 1

这是一种违反第一范式的方式,将列表存储在列值中会在关系数据库中引发许多问题,就像您现在所遇到的问题一样。

然而,您可以使用 LIKE 运算符来查找 colA 值是 Col2 列的子字符串的方式来实现您想要的效果。在之前和之后添加分隔符以捕获第一个和最后一个。然后使用 LISTAGG 聚合回到单个列表。

SELECT table1.col1,
       LISTAGG(table2.colB, ',') WITHIN GROUP (ORDER BY table2.colB) value_list
  FROM table1,
       table2
 WHERE ',' || table1.col2 || ',' LIKE '%,' || table2.colA || ',%'
 GROUP BY table1.col1

这种方法在处理大量数据时性能可能不佳,因为没有等值连接,它将使用嵌套循环,并且不能在以 % 开头的 LIKE 谓词上使用索引。嵌套循环 + 全表扫描在处理大量数据时效果不佳。因此,如果您的情况是如此,您需要通过将 table1 转换为正常的关系格式来解决第一范式问题,然后再使用等值连接将其与 table2 连接,这将使其能够使用哈希连接。

SELECT table1.col1,
       LISTAGG(table2.colB, ',') WITHIN GROUP (ORDER BY table2.colB) value_list
  FROM (
    SELECT t.col1,
           SUBSTR(t.col2, INSTR(t.col2, ',', 1, seq) + 1, INSTR(t.col2, ',', 1, seq + 1) - (INSTR(t.col2, ',', 1, seq) + 1)) col2_piece
      FROM (
        SELECT col1,
               ',' || col2 || ',' col2
          FROM table1
      ) t,
      (SELECT ROWNUM seq FROM dual CONNECT BY LEVEL < 10) x
  ) table1,
  table2
 WHERE table1.col2_piece IS NOT NULL
   AND table1.col2_piece = table2.colA
 GROUP BY table1.col1
英文:

It's a violation of first normal form to have a list in a column value like that. It causes a lot of difficulties in a relational database, like the one you are encountering now.

However, you can get what you want by using the LIKE operator to find colA values that are substrings of the Col2 column. Add delimiters before and after to catch the first and last ones. Then aggregate back up to a single list using LISTAGG.

SELECT table1.col1,
       LISTAGG(table2.colB,&#39;,&#39;) WITHIN GROUP (ORDER BY table2.colB) value_list
  FROM table1,
       table2
 WHERE &#39;,&#39;||table1.col2||&#39;,&#39; LIKE &#39;%,&#39;||table2.colA||&#39;,%&#39;
 GROUP BY table1.col1

This will not perform well on large volumes, because without an equijoin it's going to use nested loops, and you can't use an index on a LIKE predicate with % at the beginning. The combination of nested loops + FTS is not pleasant with large volumes of data. Therefore, if this is your situation, you will need to fix the 1NF problem by transforming table1 into normal relational format, and then join it to table2 with an equijoin, which will enable it to use a hash join instead. So:

SELECT table1.col1,
       LISTAGG(table2.colB,&#39;,&#39;) WITHIN GROUP (ORDER BY table2.colB) value_list
  FROM (SELECT t.col1,
               SUBSTR(t.col2,INSTR(t.col2,&#39;,&#39;,1,seq)+1,INSTR(t.col2,&#39;,&#39;,1,seq+1)-(INSTR(t.col2,&#39;,&#39;,1,seq)+1)) col2_piece
          FROM (SELECT col1,
                       &#39;,&#39;||col2||&#39;,&#39; col2
                  FROM table1) t,
               (SELECT ROWNUM seq FROM dual CONNECT BY LEVEL &lt; 10) x) table1,
       table2
 WHERE table1.col2_piece IS NOT NULL
   AND table1.col2_piece = table2.colA
 GROUP BY table1.col1

答案2

得分: 0

如果您希望列表中的值与术语的顺序相同,那么您可以使用以下 SQL 代码:

SELECT t1.col1,
       LISTAGG(t2.colb, ',') WITHIN GROUP (
         ORDER BY INSTR(','||t1.col2||',', ','||t2.colA||',')
       ) AS value2
FROM   table1 t1
       INNER JOIN table2 t2
       ON INSTR(','||t1.col2||',', ','||t2.colA||',') > 0
GROUP BY 
       t1.col1

对于示例数据:

CREATE TABLE Table1 (Col1, Col2) AS
SELECT 'First',  'Code1,Code2,Code3' FROM DUAL UNION ALL
SELECT 'Second', 'Code2' FROM DUAL;

CREATE TABLE Table2 (ColA, ColB) AS
SELECT 'Code1', 'XXXX' FROM DUAL UNION ALL
SELECT 'Code2', 'ZZZZ' FROM DUAL UNION ALL
SELECT 'Code3', 'YYYY' FROM DUAL;

输出结果如下:

COL1 VALUE2
First XXXX,ZZZZ,YYYY
Second ZZZZ

fiddle

英文:

If you want the values in the same order in the list as the terms then you can use:

SELECT t1.col1,
       LISTAGG(t2.colb, &#39;,&#39;) WITHIN GROUP (
         ORDER BY INSTR(&#39;,&#39;||t1.col2||&#39;,&#39;, &#39;,&#39;||t2.colA||&#39;,&#39;)
       ) AS value2
FROM   table1 t1
       INNER JOIN table2 t2
       ON INSTR(&#39;,&#39;||t1.col2||&#39;,&#39;, &#39;,&#39;||t2.colA||&#39;,&#39;) &gt; 0
GROUP BY 
       t1.col1

Which, for the sample data:

CREATE TABLE Table1 (Col1, Col2) AS
SELECT &#39;First&#39;,  &#39;Code1,Code2,Code3&#39; FROM DUAL UNION ALL
SELECT &#39;Second&#39;, &#39;Code2&#39; FROM DUAL;

CREATE TABLE Table2 (ColA, ColB) AS
SELECT &#39;Code1&#39;, &#39;XXXX&#39; FROM DUAL UNION ALL
SELECT &#39;Code2&#39;, &#39;ZZZZ&#39; FROM DUAL UNION ALL
SELECT &#39;Code3&#39;, &#39;YYYY&#39; FROM DUAL;

Outputs:

COL1 VALUE2
First XXXX,ZZZZ,YYYY
Second ZZZZ

fiddle

huangapple
  • 本文由 发表于 2023年2月7日 03:23:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/75365707.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定