0

我有一个可以在其中列出一个或多个状态的字段(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。非常感谢您的帮助。

4

2 回答 2

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. 任何其他的,它的BothCASE无论您如何编写它,这都会导致一个相当大且令人困惑的声明。我比较了前后的长度,如下所示:

  • 替换后的长度 = 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,希望有助于解释计算。

于 2013-06-10T16:10:33.317 回答
2

达到预期结果的方法之一是使用多集运算符
但首先我们需要将字符串分隔,成行。其中一种方法是使用以下技巧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
  ) 
)

链接到 SQLFiddle

于 2013-06-10T17:55:47.513 回答