Sql Developer - 您可以在游标中使用case语句来返回多个值

时间:2014-08-22 18:11:08

标签: sql oracle cursor case

我一直致力于尝试将数百万行数据分类为各种不同的主题。数据涉及来自我们客户支持的电话,我们正试图找到一种方法将每个电话分类为109个主题之一。由于数据的机密性,我无法公开任何实际数据,但会尝试提供其他人可以比较的相关数据子集。

DATA:

Incident_Number | Call_Description
000123456 |问题与橘子和苹果
000987654 |桔子
004567891 |与苹果和猕猴桃
026589741 |问题与猕猴桃

SQL:

选择
Incident_Number,
Call_Description,
(案件 当call_description像'%oranges%'然后是橘子 当call_description喜欢'%apples%'然后是苹果 当call_descritpion喜欢'%kiwis%'然后' kiwis'
别的'未分类'结束)主题
来自DATA

问题

我希望将事故000123456分类为橘子和苹果,事件004567891分类为苹果和猕猴桃

期望输出

Incident_Number | Call_Description ...................... |主题
000123456 ........ |桔子和苹果的问题|橘子
000123456 ........ |桔子和苹果的问题|苹果
000987654 ........ |橘子....................................桔子
004567891 ......... |与苹果和猕猴桃............... |苹果
004567891 ......... |与苹果和猕猴桃............... |奇异果
026589741 ......... |问题与猕猴桃........................ |猕猴桃

结束语

从我有限的知识和我从研究中得到的一个简单的案例陈述不能做到这一点,因为它在找到第一个真值后发生短路。我的问题是,是否可以对我的代码进行一些更改,或者以某种方式设置光标来运行我的初始表并给出上面提到的所需输出。

我感谢任何帮助或建议,并希望我已经遵守本网站的规则(以前诚实地保存了我的屁股!)

此致 理查德

5 个答案:

答案 0 :(得分:0)

我使用Microsoft SQL Server而不是Oracle,因此我不确定Oracle语法,但我过去使用的一个解决方案是创建一个临时表:

CREATE GLOBAL TEMPORARY TABLE my_temp_table (
  groupName  varchar(50)
) ON COMMIT DELETE ROWS;
Insert Into my_temp_table (groupName) VALUES('oranges')
Insert Into my_temp_table (groupName) VALUES('apples')
Insert Into my_temp_table (groupName) VALUES('kiwis')

然后我会加入到表中以复制记录:

select
    Incident_Number,
    Call_Description,
    my_temp_table.groupName Topic
from DATA
inner join my_temp_table
    on Data.call_description like '%' + my_temp_table.groupName + '%'

此方法的一个问题是,如果记录不属于任何类别,则会完全排除。

答案 1 :(得分:0)

我的exaple适用于MS SQL Server:http://www.sqlfiddle.com/#!3/8e904/2

您可以创建一个包含详细信息的表格和一个简单的交叉连接

create table data(
Incident_Number varchar(9),
Call_Description varchar(50))

create table detail(detail varchar(20))

insert into data select

'000123456','Issue with oranges and apples'
union select
'000987654', 'oranges '
union select
'004567891', 'with apples and kiwis'
union select
'026589741', 'Issue with kiwis'

insert into detail select 'kiwis'
union select 'oranges'
union select 'apples'

select * from data a
 cross join detail b
where a.call_description like '%'||b.detail||'%'
order by a.Incident_Number

添加新版本http://www.sqlfiddle.com/#!3/b3378/1

它管理所有识别那些没有巧合的事件

select * from data a
 left join detail b
on a.call_description like '%'||b.detail||'%'

答案 2 :(得分:0)

一种选择是创建(物理或虚拟)关键字表,并使用它来连接到您的数据表。像

这样的东西
WITH keywords AS (
  SELECT 'apples' topic FROM dual UNION 
  SELECT 'oranges' FROM dual UNION 
  SELECT 'kiwis' FROM dual
)
SELECT data.incident_number,
       data.incident_description,
       keywords.topic
  FROM data
       JOIN keywords
         ON( data.incident_description LIKE '%' || keywords.topic || '%' )

这可行,但它不是世界上最有效或最灵活的方法。它不能很好地处理不同形式的单词(例如,如果描述引用单数“苹果”)。它不处理其他单词中出现的单词(例如,如果描述中谈到“crabapples”)。并且它基于扫描整个incident_description进行相对较慢的匹配。

另一种方法是使用Oracle Text索引数据。这可能是一个更复杂的解决方案,但它会更灵活,应该更有效率。

答案 3 :(得分:0)

我已经选择了CURSOR选项来遍历表格中的记录,并使用类别信息更新每一行。

因为您提到您的规则非常复杂,我认为每个规则都可能需要在此过程中手写,而不是存储在表格中。

很抱歉这是MSSQL,因此可能需要为Oracle转换一点。

/*BEGIN SETUP TEMP DATA*/
CREATE TABLE #tmp1 (
    idno int,
    title nvarchar(100),    
    Categories nvarchar(100) --I have added this row to my temp data, you could store this in another table if needed.
)

INSERT INTO #tmp1 (idno, title) VALUES (1, 'problem apples and oranges')
INSERT INTO #tmp1 (idno, title) VALUES (2, 'problem with apples')
INSERT INTO #tmp1 (idno, title) VALUES (3, 'problem with oranges')
INSERT INTO #tmp1 (idno, title) VALUES (4, 'problem with kiwis')
INSERT INTO #tmp1 (idno, title) VALUES (5, 'problem with something')
/*END SETUP TEMP DATA*/

/*SETUP VARIABLES TO USE IN CURSOR*/
DECLARE @idno int,
        @title nvarchar(100)

/*DECLARE CURSOR, OPEND IT AND FETCH DATA INTO INTO IT*/
DECLARE incident_cursor CURSOR FOR 
    SELECT idno, title
    FROM #tmp1 --You could add WHERE Categories IS NULL to only update records that have not been processed
OPEN incident_cursor

FETCH NEXT FROM incident_cursor 
INTO @idno, @title

/*LOOP THROUGH CURSOR*/
WHILE @@FETCH_STATUS = 0
BEGIN

    DECLARE @allCategories nvarchar(100)

    SET @allCategories = ''

    /*WRITE RULES HERE TO WORK OUT WHETHER CATEGORY NEEDS ADDING*/
    IF (@title LIKE '%apples%') BEGIN SET @allCategories = @allCategories + 'Apples ' END
    IF (@title LIKE '%oranges%') BEGIN SET @allCategories = @allCategories + 'Oranges ' END
    IF (@title LIKE '%kiwis%') BEGIN SET @allCategories = @allCategories + 'Kiwis ' END

    IF @allCategories = '' BEGIN SET @allCategories = 'Uncategorised' END

    /*UPDATE ORIGINAL TABLE WITH CATEGORY INFORMATION*/
    UPDATE #tmp1 SET Categories = @allCategories WHERE idno = @idno

FETCH NEXT FROM incident_cursor 
    INTO @idno, @title
END 
CLOSE incident_cursor;
DEALLOCATE incident_cursor;

/*THIS ARE JUST TO DISPLAY OUTPUT AND CLEAR UP TEST DATA*/
SELECT * FROM #tmp1

DROP TABLE #tmp1

您可以将其设置为在预定作业中运行,以便根据需要更新记录。

可能不是一个完美的解决方案,但希望是一个起点

答案 4 :(得分:0)

对不起,我花了多长时间回到这里。我被拉进了一些更紧迫的可交付成果。我最后通过将查询作为记录存储在一个表中,然后使用execute immediate命令运行它来弄清楚如何使其工作。我已经包含了以下代码,以防将来有人可以使用它。

create table text_kw_search_queries
(
incident_number varchar2(15),
description varchar2(100),
topic_level_1 varchar2(89),
topic_level_2 varchar2(89)
);

DECLARE 
V_SQL VARCHAR2(1000);
CURSOR UPTO IS
SELECT TOPIC_level_1, TOPIC_level_2, description FROM tcstopic
;

BEGIN
FOR i IN UPTO LOOP

V_SQL := 'INSERT /*+APPEND PARALLEL(TEXT_KW_SEARCH_LOOP,2)*/ INTO text_kw_search_queries
SELECT
t2.Incident_Number,
t2.description,
t2.TOPIC_level_1,
t2.TOPIC_level_2
FROM (
SELECT 
t1.Incident_number,
t1.description,
'''||i.TOPIC_level_1||''' AS TOPIC_level_1, 
'''||i.TOPIC_level_2||''' AS TOPIC_level_2,
(CASE WHEN '|| i.description ||' THEN 1 ELSE 0 END) as FLAG
FROM cso_text_query t1
) t2
WHERE t2.FLAG = 1';

EXECUTE IMMEDIATE V_SQL;
DBMS_OUTPUT.PUT_LINE(V_SQL);

COMMIT WORK;
END LOOP;
END;