我有一个问题,我希望有人可以帮助我。事实上,我在一个设计糟糕的数据库上工作,我无法控制其中的内容。我有一个表“书籍”,每本书可以有一个或多个作者。不幸的是,数据库并不是完全关系的(请不要问我为什么,因为我从一开始就问同样的问题)。在“书籍”表中有一个名为“Author_ID”和“Author_Name”的字段,因此当一本书由2或3位作者撰写时,他们的ID和他们的名字将在由星号分隔的同一记录中连接。这是一个演示:
ID_BOOK | ID_AUTHOR | NAME AUTHOR | Adress | Country |
----------------------------------------------------------------------------------
001 |01 | AuthorU | AdrU | CtryU |
----------------------------------------------------------------------------------
002 |02*03*04 | AuthorX*AuthorY*AuthorZ | AdrX*NULL*AdrZ | NULL*NULL*CtryZ |
----------------------------------------------------------------------------------
我需要创建一个针对此表的视图,它会给我这个结果:
ID_BOOK | ID_AUTHOR | NAME AUTHOR | Adress | Country |
----------------------------------------------------------------------------------
001 |01 | AuthorU | AdrU | CtryU |
----------------------------------------------------------------------------------
002 |02 | AuthorX | AdrX | NULL |
----------------------------------------------------------------------------------
002 |03 | AuthorY | NULL | NULL |
----------------------------------------------------------------------------------
002 |04 | AuthorZ | AdrZ | CtryZ |
----------------------------------------------------------------------------------
我会继续尝试这样做,我希望有人可以帮助我至少提供一些提示。非常感谢你们。
在我应用你们给出的解决方案后,我遇到了这个问题。我想解决它,希望你能帮助我。实际上,当sql查询运行时,CLOB字段在其中一些包含NULL值时是无组织的。 reslut应该如上所述,但我得到了以下结果:
ID_BOOK | ID_AUTHOR | NAME AUTHOR | Adress | Country |
----------------------------------------------------------------------------------
001 |01 | AuthorU | AdrU | CtryU |
----------------------------------------------------------------------------------
002 |02 | AuthorX | AdrX | CtryZ |
----------------------------------------------------------------------------------
002 |03 | AuthorY | AdrZ | NULL |
----------------------------------------------------------------------------------
002 |04 | AuthorZ | NULL | NULL |
----------------------------------------------------------------------------------
为什么将NULL值放在最后?谢谢。
答案 0 :(得分:2)
在11g中你可以使用一个因子递归子查询:
with data (id_book, id_author, name, item_author, item_name, i)
as (select id_book, id_author, name,
regexp_substr(id_author, '[^\*]+', 1, 1) item_author,
regexp_substr(name, '[^\*]+', 1, 1) item_name,
2 i
from books
union all
select id_book, id_author, name,
regexp_substr(id_author, '[^\*]+', 1, i) item_author,
regexp_substr(name, '[^\*]+', 1, i) item_name,
i+1
from data
where regexp_substr(id_author, '[^\*]+', 1, i) is not null)
select id_book, item_author, item_name
from data;
答案 1 :(得分:1)
几周前,我回答了类似的问题here。这个答案有一个解释(我希望)的一般方法,所以我将跳过这里的解释。这个查询可以解决问题;它使用REGEXP_REPLACE
并利用其“occurrence”参数来选择单个作者ID和名称:
SELECT
ID_Book,
REGEXP_SUBSTR(ID_Author, '[^*]+', 1, Counter) AS AuthID,
REGEXP_SUBSTR(Name_Author, '[^*]+', 1, Counter) AS AuthName
FROM Books
CROSS JOIN (
SELECT LEVEL Counter
FROM DUAL
CONNECT BY LEVEL <= (
SELECT MAX(REGEXP_COUNT(ID_Author, '[^*]+'))
FROM Books))
WHERE REGEXP_SUBSTR(Name_Author, '[^*]+', 1, Counter) IS NOT NULL
ORDER BY 1, 2
您的数据加上另一行here。
附录:OP有Oracle 9,而不是11,所以正则表达式不起作用。以下是没有正则表达式执行相同任务的说明...
没有REGEXP_COUNT
,计算作者的最佳方法是计算星号并添加一个星号。要计算星号,请取出字符串的长度,然后在将所有星号从中抽出时减去其长度:LENGTH(ID_Author) - LENGTH(REPLACE(ID_Author, '*'))
。
如果没有REGEX_SUBSTR
,您需要使用INSTR
来查找星号的位置,然后SUBSTR
来提取作者ID和名称。这有点复杂 - 请考虑原始帖子中的这些作者列:
Author U
Author X*Author Y*Author Z
AuthorX
位于字符串的开头和第一个星号之间。AuthorY
被星号包围AuthorZ
位于最后一个星号和字符串末尾之间。AuthorU
一个人而且没有被任何东西包围。因此,开头(下面的WITH AuthorInfo AS...
)在开头和结尾添加一个星号,因此每个作者姓名(和ID)都用星号包围。它还会抓取每一行的作者计数。对于原始帖子中的样本数据,开头文章将产生以下结果:
ID_Book AuthCount ID_Author Name_Author
------- --------- ---------- -------------------------
001 1 *01* *AuthorU*
002 3 *02*03*04* *AuthorX*AuthorY*AuthorZ*
然后加入“Counter”表和SUBSTR
阴谋来提取个人姓名和ID。最终查询如下所示:
WITH AuthorInfo AS (
SELECT
ID_Book,
LENGTH(ID_Author) -
LENGTH(REPLACE(ID_Author, '*')) + 1 AS AuthCount,
'*' || ID_Author || '*' AS ID_Author,
'*' || Name_Author || '*' AS Name_Author
FROM Books
)
SELECT
ID_Book,
SUBSTR(ID_Author,
INSTR(ID_Author, '*', 1, Counter) + 1,
INSTR(ID_Author, '*', 1, Counter+1) - INSTR(ID_Author, '*', 1, Counter) - 1) AS AuthID,
SUBSTR(Name_Author,
INSTR(Name_Author, '*', 1, Counter) + 1,
INSTR(Name_Author, '*', 1, Counter+1) - INSTR(Name_Author, '*', 1, Counter) - 1) AS AuthName
FROM AuthorInfo
CROSS JOIN (
SELECT LEVEL Counter
FROM DUAL
CONNECT BY LEVEL <= (SELECT MAX(AuthCount) FROM AuthorInfo))
WHERE AuthCount >= Counter
ORDER BY ID_Book, Counter
小提琴是here
答案 2 :(得分:0)
如果你有authors
表,你可以这样做:
select b.id_book, a.id_author, a.NameAuthor
from books b left outer join
authors a
on '*'||NameAuthor||'*' like '%*||a.author||'*%'
答案 3 :(得分:0)
另外:
SELECT distinct id_book,
, trim(regexp_substr(id_author, '[^*]+', 1, LEVEL)) id_author
, trim(regexp_substr(author_name, '[^*]+', 1, LEVEL)) author_name
FROM yourtable
CONNECT BY LEVEL <= regexp_count(id_author, '[^*]+')
ORDER BY id_book, id_author
/
ID_BOOK ID_AUTHOR AUTHOR_NAME
------------------------------------
001 01 AuthorU
002 02 AuthorX
002 03 AuthorY
002 04 AuthorZ
003 123 Jane Austen
003 456 David Foster Wallace
003 789 Richard Wright
没有REGEXP:
SELECT str, SUBSTR(str, substr_start_pos, substr_end_pos) final_str
FROM
(
SELECT str, substr_start_pos
, (CASE WHEN substr_end_pos <= 0 THEN (Instr(str, '*', 1)-1)
ELSE substr_end_pos END) substr_end_pos
FROM
(
SELECT distinct '02*03*04' AS str
, (Instr('02*03*04', '*', LEVEL)+1) substr_start_pos
, (Instr('02*03*04', '*', LEVEL)-1) substr_end_pos
FROM dual
CONNECT BY LEVEL <= length('02*03*04')
)
ORDER BY substr_start_pos
)
/
STR FINAL_STR
---------------------
02*03*04 02
02*03*04 03
02*03*04 04