英文:
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,',') WITHIN GROUP (ORDER BY table2.colB) value_list
FROM table1,
table2
WHERE ','||table1.col2||',' LIKE '%,'||table2.colA||',%'
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,',') 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
答案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 |
英文:
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, ',') 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
Which, for the sample data:
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;
Outputs:
COL1 | VALUE2 |
---|---|
First | XXXX,ZZZZ,YYYY |
Second | ZZZZ |
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论