复杂选择循环(基于以前的解决方案)

时间:2015-09-28 13:46:51

标签: plsql oracle-sqldeveloper

我在这里问了一个问题: Identify phone numbers in a PL/SQL iteration

我得到了一个完美的答案,但现在我需要在一个复杂的选择语句中使用它。

目标与加入查询的列中的结果相同。例如:

  SELECT t1.phone_number
       , t2.origin_country
       , sum(t1.volume) Total_vol
       , sum(t1.charge) Total_chg
       from t2
     LEFT JOIN t1 ON t1.item_no = t2.item_no
     LEFT JOIN t3 ON t3.vcode = t2.vcode
     LEFT JOIN /*<<Here should be a subquery which attach the column with
     the countries to my query>>*/
       +many WHERE and GROUP BY clauses

问题是列和源表的数量可能会有所不同,因此我正在寻找一种灵活的解决方案,我可以使用任何存在phone_number列的复杂查询。我已经尝试过了:

  • 将整个选择放入循环并加入

    SELECT ic.country 
    FROM int_codes ic 
    WHERE ic.int_code = substr(t1.phone_number, 1, i)
    

    作为子查询,但它显然无法正常工作,因为它没有存储,没有要填写的字段

  • 在程序中创建一个视图并将国家/地区加入其中,但它不灵活

  • 没有尝试,但想过一个有很多UNION和NOT EXISTS的脚本,但它会非常复杂而且运行缓慢
  • 使用CURSOR,但错误消息说我必须在架构级别定义TYPE,但由于确定的表结构,它仍然不灵活。

那我该怎么做呢?

(如果有人采用完全不同的方法在表格中灵活地识别和显示电话号码,我们欢迎他们)

- UPDATE -

解决方案:

  select ... PHONE_NUMBER, XY, ZZ, ... ,
         case
         when i.INT_CODE = substr(PHONE_NUMBER,1,4) then i.COUNTRY

         when i.INT_CODE = substr(PHONE_NUMBER,1,3) 
         and i.INT_CODE = substr(PHONE_NUMBER,1,4) then i.COUNTRY

         when i.INT_CODE = substr(PHONE_NUMBER,1,2) 
         and i.INT_CODE = substr(PHONE_NUMBER,1,3) then i.COUNTRY

         when i.INT_CODE = substr(PHONE_NUMBER,1,1) 
         and i.INT_CODE = substr(PHONE_NUMBER,1,2) then i.COUNTRY

         else 'Unidentified location'
         end TARGET_COUNTRY

   from  (
          select ... t1.phone_number ,sum(xy) Total XY, sum(zz) ZZ, ... 
             /*same fields like in the main query above*/
          left join t2 on ...
          left join t3 on ...

          where date = 'some date' and country in ('country1','country2') ...
             /*many conditions*/
          group by t2.phone_number,
                   t3.account ... 
          ) MainQuery

  left join int_codes i
  on (    i.INT_CODE = substr(MainQuery.PHONE_NUMBER, 1, 1)
       or i.INT_CODE = substr(MainQuery.PHONE_NUMBER, 1, 2)
       or i.INT_CODE = substr(MainQuery.PHONE_NUMBER, 1, 3)
       or i.INT_CODE = substr(MainQuery.PHONE_NUMBER, 1, 4)
     );

1 个答案:

答案 0 :(得分:1)

如果您使用的是Oracle 12c,则可以使用LATERAL VIEW
使用子查询来确定正确的国家/地区代码:

SELECT .........
FROM T_NUMBERS t
LEFT JOIN ..............
LEFT JOIN ................
, /* this comma is required by the syntax */
LATERAL (
  SELECT * FROM int_codes i
  WHERE i.INT_CODE = substr(t.PHONE_NUMBER, 1, 1 )
     OR i.INT_CODE = substr(t.PHONE_NUMBER, 1, 2 )
     OR i.INT_CODE = substr(t.PHONE_NUMBER, 1, 3 )
     OR i.INT_CODE = substr(t.PHONE_NUMBER, 1, 4 )
  ORDER BY length( i.INT_CODE ) DESC
  FETCH FIRST 1 ROWS ONLY
)
GROUP BY .....;

此外,假设T_NUMBERS具有唯一标识每一行的主键列,则可以将此子查询与MERGE语句一起使用以更新T_NUMBERS表
使用正确的国家/地区代码和名称(假设PK是主键列):

MERGE INTO T_NUMBERS  t
USING (
  SELECT t.pk, i.country, i.int_code
  FROM T_NUMBERS t,
  LATERAL (
    SELECT * FROM int_codes i
    WHERE i.INT_CODE = substr(t.PHONE_NUMBER, 1, 1 )
       OR i.INT_CODE = substr(t.PHONE_NUMBER, 1, 2 )
       OR i.INT_CODE = substr(t.PHONE_NUMBER, 1, 3 )
       OR i.INT_CODE = substr(t.PHONE_NUMBER, 1, 4 )
    ORDER BY length( i.INT_CODE ) DESC
    FETCH FIRST 1 ROWS ONLY ) i
) i
ON ( t.pk = i.pk )
WHEN MATCHED THEN UPDATE SET t.COUNTRY = i.COUNTRY, t.COUNTRY_CODE = i.int_CODE;
;

-----编辑-----

在Oracle 11g上,您可以尝试类似下面的内容 - 但仍然假设T_NUMBERS表有一些主键列(在此列下面的查询中名为pk):

SELECT ........
 FROM (
      SELECT t.pk, t.phone_number,
             i.int_code, i.country,
             aaa.*, bbb.*
             row_number() over( partition by pk order by length(i.int_code) desc ) xxxx
      FROM T_NUMBERS t
      LEFT JOIN aaa   ......
      LEFT JOIN bbb   ......
      LEFT JOIN int_codes i
      ON (    i.INT_CODE = substr(t.PHONE_NUMBER, 1, 1 )
           OR i.INT_CODE = substr(t.PHONE_NUMBER, 1, 2 )
           OR i.INT_CODE = substr(t.PHONE_NUMBER, 1, 3 )
           OR i.INT_CODE = substr(t.PHONE_NUMBER, 1, 4 )
        ) 
)
WHERE xxxx = 1
GROUP BY ........ ;

我不确定它会如何表现,可能会很慢。

以下版本可能会稍微好一点:

SELECT ........
 FROM (
      SELECT t.pk, t.phone_number,
             i.int_code, i.country,
             row_number() over( partition by pk order by length(i.int_code) desc ) xxxx
      FROM T_NUMBERS t

      LEFT JOIN int_codes i
      ON (    i.INT_CODE = substr(t.PHONE_NUMBER, 1, 1 )
           OR i.INT_CODE = substr(t.PHONE_NUMBER, 1, 2 )
           OR i.INT_CODE = substr(t.PHONE_NUMBER, 1, 3 )
           OR i.INT_CODE = substr(t.PHONE_NUMBER, 1, 4 )
        ) 
)
LEFT JOIN aaa   ......
LEFT JOIN bbb   ......
WHERE xxxx = 1
GROUP BY ........ ;