【问题标题】:Oracle Self Join to retrieve recursive dataOracle Self Join 检索递归数据
【发布时间】:2015-07-09 08:19:10
【问题描述】:

我有以下架构和数据

segmentid  paramid  paramvalue
103 1   4418

101 1   4834

102 1   5587

104 1   7413

105 1   9965

106 1   7421

107 1   7782

103 2   1990|2000

102 2   2005|2010

105 2   1985|1990

104 2   1981

101 3   F

103 3   M

101 4   M

103 4   S

102 5   SUKKHUR

105 5   LAHORE

106 5   HYDRABAD

107 5   KHAIRPUR

101 5   ISLAMABAD

现在我将输入不同的参数值,例如卡拉奇 M 和出生日期范围。我只想检索所有参数都返回为 true 的段 id。 如果任何参数失败,则段应该失败。 以下是我的想法,但如果任何参数值为真,则返回,因为我使用了or,但是当我使用and 时,未检索到数据。

select tpv.* from tblsegment ts , tblsegmentparameter tsp , tblsegmentparamvalue tpv
where ts.segmentid = tpv.segmentid and tsp.parameterid = tpv.paramid
and 
(
(lower(tsp.paramname) like 'city' and tpv.paramvalue = 'KARACHI' and tsp.parameterid = tpv.paramid) 
or
(lower(tsp.paramname) like 'gender' and tpv.paramvalue = 'M') 
or
(lower(tsp.paramname) like 'maritalstatus' and tpv.paramvalue = 'S') 
or
(lower(tsp.paramname) like 'product' and tpv.paramvalue = (select distinct ta.productid  from tblcustchannelacct ta ,tblcustomer tc, tblaccount tta 
where ta.relationship_id = '5327016301000015=5311' and ta.channel_id = '0001' and ta.account_id = tta.account_id and ta.customer_id = tc.customerid )
)
or
(lower(tsp.paramname) like 'dob' and 
  (
   (
    to_char( '1985') between
    to_char( REGEXP_SUBSTR ( tpv.paramvalue, '^[^|]*')) 
    and   

    to_char(REGEXP_SUBSTR( tpv.paramvalue, '*[^|]*$'))   
   ) or
   (
   to_char( '1986') = tpv.paramvalue
   )
  )
)
)
order by tsp.sortorder;

【问题讨论】:

  • 您是否被这个特定的数据模型所困扰?因为(恕我直言)这是一个非常垃圾的设计 - 当您尝试查询任何内容时,您现在就会发现。

标签: oracle join inner-join self-join


【解决方案1】:

SQL Fiddle

Oracle 11g R2 架构设置

CREATE TABLE tblsegmentparamvalue ( segmentid,  paramid,  paramvalue ) AS
          SELECT 103, 1,   '4418' FROM DUAL
UNION ALL SELECT 101, 1,   '4834' FROM DUAL
UNION ALL SELECT 102, 1,   '5587' FROM DUAL
UNION ALL SELECT 104, 1,   '7413' FROM DUAL
UNION ALL SELECT 105, 1,   '9965' FROM DUAL
UNION ALL SELECT 106, 1,   '7421' FROM DUAL
UNION ALL SELECT 107, 1,   '7782' FROM DUAL
UNION ALL SELECT 103, 2,   '1990|2000' FROM DUAL
UNION ALL SELECT 102, 2,   '2005|2010' FROM DUAL
UNION ALL SELECT 105, 2,   '1985|1990' FROM DUAL
UNION ALL SELECT 104, 2,   '1981' FROM DUAL
UNION ALL SELECT 101, 3,   'F' FROM DUAL
UNION ALL SELECT 103, 3,   'M' FROM DUAL
UNION ALL SELECT 101, 4,   'M' FROM DUAL
UNION ALL SELECT 103, 4,   'S' FROM DUAL
UNION ALL SELECT 102, 5,   'SUKKHUR' FROM DUAL
UNION ALL SELECT 105, 5,   'LAHORE' FROM DUAL
UNION ALL SELECT 106, 5,   'HYDRABAD' FROM DUAL
UNION ALL SELECT 107, 5,   'KHAIRPUR' FROM DUAL
UNION ALL SELECT 101, 5,   'ISLAMABAD' FROM DUAL;

查询 1

WITH segments AS (
  SELECT segmentid
  FROM   tblsegmentparamvalue
  GROUP BY segmentid
  HAVING (   COUNT( CASE WHEN paramid = 1 THEN 1 END ) = 0
         OR  COUNT( CASE WHEN paramid = 1 AND paramvalue = '4834'      THEN 1 END ) > 0 )
  AND    (   COUNT( CASE WHEN paramid = 2 THEN 1 END ) = 0
         OR  COUNT( CASE WHEN paramid = 2 AND '1985' BETWEEN SUBSTR( paramvalue, 1, 4 ) AND SUBSTR( paramvalue, -4 ) THEN 1 END ) > 0 )
  AND    (   COUNT( CASE WHEN paramid = 3 THEN 1 END ) = 0
         OR  COUNT( CASE WHEN paramid = 3 AND paramvalue = 'F'         THEN 1 END ) > 0 )
  AND    (   COUNT( CASE WHEN paramid = 4 THEN 1 END ) = 0
         OR  COUNT( CASE WHEN paramid = 4 AND paramvalue = 'M'         THEN 1 END ) > 0 )
  AND    (   COUNT( CASE WHEN paramid = 5 THEN 1 END ) = 0
         OR  COUNT( CASE WHEN paramid = 5 AND paramvalue = 'ISLAMABAD' THEN 1 END ) > 0 )
)
SELECT t.*
FROM   tblsegmentparamvalue t
       INNER JOIN
       segments s
       ON ( t.segmentid = s.segmentid )
ORDER BY
       t.segmentid,
       paramid

Results

| SEGMENTID | PARAMID | PARAMVALUE |
|-----------|---------|------------|
|       101 |       1 |       4834 |
|       101 |       3 |          F |
|       101 |       4 |          M |
|       101 |       5 |  ISLAMABAD |

【讨论】:

    【解决方案2】:

    这是一个如何获取三个参数的结果的示例:

    select segmentid, paramid, paramvalue 
      from (
        select tpv.*, count(distinct paramid) over (partition by tpv.segmentid) cnt
          from tblsegmentparamvalue tpv 
            join tblsegment ts on ts.segmentid = tpv.segmentid
            join tblsegmentparameter tsp on tsp.parameterid = tpv.paramid
          where (lower(tsp.paramname) like 'city' and tpv.paramvalue = 'KARACHI')
             or (lower(tsp.paramname) like 'gender' and tpv.paramvalue = 'M')
             or (lower(tsp.paramname) like 'martialstatus' and tpv.paramvalue = 'S') )
      where cnt = 3
    

    SQLFiddle demo

    按照您在查询中的操作添加其余参数(dob、product),并将最后一行更改为 where cnt=5 以获得完整结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-15
      • 1970-01-01
      • 1970-01-01
      • 2016-05-10
      • 2021-10-20
      • 1970-01-01
      • 2014-12-12
      • 1970-01-01
      相关资源
      最近更新 更多