我有一个可以在其中列出一个或多个状态的字段(callcenter.stateimpact)。如果 callcenter.stateimpact 包含 "OK","TX","AK","TN","NC","SC","GA","FL","AL","MS" 或 "LA" I需要 SQL 的输出字段说“南”,如果不是,输出需要说“北”。如果 callcenter.stateimpact 有 South 和 North 两个州,它需要在输出中说“BOTH”。如何在 Select 语句中执行此操作?此表中的字段为 callcenter.callid、callcenter.stateimpact、callcenter.callstart 和 callcenter.callstop。非常感谢您的帮助。
2 回答
这很难解释,所以这里有一个 SQL Fiddle列出了所涉及的值。
我能想出的最好方法(除了标准化StateImpact
值)是用来REGEXP_REPLACE
从字符串中吸出所有“南”状态,然后查看剩下的长度。首先,REGEXP_REPLACE(StateImpact, '(OK|TX|AK|TN|NC|SC|GA|FL|AL|MS|LA)')
以下是对一些示例值的处理:
StateImpact REGEXP_REPLACE(StateImpact, '(OK|TX|AK|TN|NC|SC|GA|FL|AL|MS|LA)')
----------------------------- -----------------------------------------------------------------
OK,TX,AK,TN,NC,SC,GA,FL,AL,MS ,,,,,,,,,
MI,MA MI,MA
TX null
TX,MI,MA ,MI,MA
因此,如果您留下所有逗号或空值,则所有状态都是South
. 如果您留下原始字符串,则所有状态都是North
. 任何其他的,它的Both
。CASE
无论您如何编写它,这都会导致一个相当大且令人困惑的声明。我比较了前后的长度,如下所示:
- 替换后的长度 = 0(或 null):
South
- 替换后的长度 = (之前的长度 + 1) * 3 - 1:
South
- 替换后的长度 = 替换前的长度:
North
- 还要别的吗:
Both
上面的第二个只是一些数学,以说明如果(例如)有五个状态StateImpact
并且它们都是South
,那么您将留下四个逗号。很难解释,但它有效:)
这是查询:
SELECT
StateImpact,
CASE NVL(LENGTH(REGEXP_REPLACE(StateImpact, '(OK|TX|AK|TN|NC|SC|GA|FL|AL|MS|LA)')), 0)
WHEN LENGTH(StateImpact) THEN 'North'
WHEN (LENGTH(StateImpact) + 1) / 3 - 1 THEN 'South'
ELSE 'Both'
END AS RegionImpact
FROM CallCenter
上面引用的 SQL Fiddle 还显示了 之前和之后的长度REGEXP_REPLACE
,希望有助于解释计算。
达到预期结果的方法之一是使用多集运算符。
但首先我们需要将字符串分隔,
成行。其中一种方法是使用以下技巧connect by
:
-- Trick with building resultset from tokenized string
with dtest_string as (
select 'OK,TX,AK,TN,NC,SC,GA,FL,AL,MS' StateImpact from dual
)
select
level lvl,
substr( -- Extract part of source string
StateImpact,
-- from N-th occurence of separator
decode( level, 1, 1, instr(StateImpact,',',1,level-1)+1 ),
-- with length of substring from N-th to (N+1)-th occurence of separator or to the end.
decode( instr(StateImpact,',',1,level), 0, length(StateImpact)+1, instr(StateImpact,',',1,level) )
-
decode( level, 1, 1, instr(StateImpact,',',1,level-1)+1 )
) code
from test_string
start with
StateImpact is not null -- no entries for empty string
connect by
instr(StateImpact,',',1,level-1) > 0 -- continue if separator found on previous step
只是为了好玩:与SQLFiddle上的 ANSI 语法相同的技巧
接下来,我们需要声明可用于存储集合的类型:
create or replace type TCodeList as table of varchar2(100);
之后可以构建查询:
with all_south_list as (
-- prepare list of south states
select 'OK' as code from dual union all
select 'TX' as code from dual union all
select 'AK' as code from dual union all
select 'TN' as code from dual union all
select 'NC' as code from dual union all
select 'SC' as code from dual union all
select 'GA' as code from dual union all
select 'FL' as code from dual union all
select 'AL' as code from dual union all
select 'MS' as code from dual union all
select 'LA' as code from dual
)
select
StateImpact,
-- Make decision based on counts
case
when total_count = 0 then 'None'
when total_count = south_count then 'South'
when south_count = 0 then 'North'
else 'Both'
end RegionImpact,
total_count,
south_count,
north_count
from (
select
StateImpact,
-- count total number of states in StateImpact
cardinality(code_list) total_count,
-- count number of south states in StateImpact
cardinality(code_list multiset intersect south_list) south_count,
-- count number of non-south states in StateImpact
cardinality(code_list multiset except south_list) north_count
from (
select
StateImpact,
(
cast(multiset( -- Convert set of values into collection which acts like a nested table
select -- same trick as above
substr(
StateImpact,
decode( level, 1, 1, instr(StateImpact,',',1,level-1)+1 ),
decode( instr(StateImpact,',',1,level), 0, length(StateImpact)+1, instr(StateImpact,',',1,level) )
-
decode( level, 1, 1, instr(StateImpact,',',1,level-1)+1 )
) code
from dual
start with StateImpact is not null
connect by instr(StateImpact,',',1,level-1) > 0
) as TCodeList
)
) code_list,
-- Build collection from south states list
cast(multiset(select code from all_south_list) as TCodeList) south_list
from
CallCenter
)
)