Oracle SQL Left join same table unknown amount of times

南楼画角 提交于 2019-12-22 00:45:30

问题


I have this table

| old  | new   | 
|------|-------| 
| a    | b     | 
| b    | c     | 
| d    | e     | 
| ...  | ...   | 
| aa   | bb    | 
| bb   | ff    | 
| ...  | ...   | 
| 11   | 33    | 
| 33   | 523   | 
| 523  | 4444  | 
| 4444 | 21444 | 

The result I want to achieve is

| old  | newest | 
|------|--------| 
| a    | e      | 
| b    | e      | 
| d    | e      | 
| ...  |        | 
| aa   | ff     | 
| bb   | ff     | 
| ...  |        | 
| 11   | 21444  | 
| 33   | 21444  | 
| 523  | 21444  | 
| 4444 | 21444  | 

I can hard code the query to get the result that I want.

SELECT 
   older.old,
   older.new,
   newer.new firstcol,
   newer1.new secondcol,
   …
   newerX-1.new secondlastcol,
   newerX.new lastcol

from Table older

Left join Table newer 
on older.old = newer.new

Left join Table newer1 
on newer.new = newer1.old

…

Left join Table newerX-1 
on newerX-2.new = newerX-1.old

Left join Table newerX 
on newerX-1.new = newerX.old;

and then just take the first value from the right that is not null.

Illustrated here:

| old  | new   | firstcol | secondcol | thirdcol | fourthcol |     | lastcol | 
|------|-------|----------|-----------|----------|-----------|-----|---------| 
| a    | b     | c        | e         | null     | null      | ... | null    | 
| b    | c     | e        | null      | null     | null      | ... | null    | 
| d    | e     | null     | null      | null     | null      | ... | null    | 
| ...  | ...   | ...      | ...       | ...      | ...       | ... | null    | 
| aa   | bb    | ff       | null      | null     | null      | ... | null    | 
| bb   | ff    | null     | null      | null     | null      | ... | null    | 
| ...  | ...   | ...      | ...       | ...      | ...       | ... | null    | 
| 11   | 33    | 523      | 4444      | 21444    | null      | ... | null    | 
| 33   | 523   | 4444     | 21444     | null     | null      | ... | null    | 
| 523  | 4444  | 21444    | null      | null     | null      | ... | null    | 
| 4444 | 21444 | null     | null      | null     | null      | ... | null    | 

The problem is that the length of "the replacement chain" is always changing (Can vary from 10 to 100).

There must be a better way to do this?


回答1:


What you are looking for is a recursive query. Something like this:

with cte (old, new, lev) as
(
  select old, new, 1 as lev from mytable
  union all
  select m.old, cte.new, cte.lev + 1
  from mytable m
  join cte on cte.old = m.new
)
select old, max(new) keep (dense_rank last order by lev) as new
from cte
group by old
order by old;

The recursive CTE creates all iterations (you can see this by replacing the query by select * from cte). And in the final query we get the last new per old with Oracle's KEEP LAST.

Rextester demo: http://rextester.com/CHTG34988




回答2:


I'm trying to understand how you group your rows to determine different "newest" values. Are these the groupings you want based on the old field?

Group 1 - one letter (a, b, d)
Group 2 - two letters (aa, bb)
Group 3 - any number (11, 33, 523, 4444)

Is this correct? If so, you just need to group them by an expression and then use a window function MAX(). Something like this:

SELECT 
  "old",
  MAX() OVER(PARTITION BY MyGrouping) AS newest
FROM (
  SELECT 
    "old",
    CASE 
      WHEN NOT IS_NUMERIC("old") THEN 'string' || CHAR_LENGTH("old") -- If string, group by string length
      ELSE 'number' -- Otherwise, group as a number
    END AS MyGrouping
  FROM MyTable
) src

I don't know if Oracle has equivalents of the IS_NUMERIC and CHAR_LENGTH functions, so you need to check on that. If not, replace that expression with something similar, like this:

https://www.techonthenet.com/oracle/questions/isnumeric.php



来源:https://stackoverflow.com/questions/48989579/oracle-sql-left-join-same-table-unknown-amount-of-times

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!