oracle 编写查询以返回基于级别的父位置

zed5wv10  于 2023-05-28  发布在  Oracle
关注(0)|答案(3)|浏览(144)

我有一个像这样的位置表结构
| id|姓名|活跃|1级|2级|三级|4级|
| - -----|- -----|- -----|- -----|- -----|- -----|- -----|
| 一个|一个|真的|一个|零|零|零|
| 2| A>B|真的|一个|B|零|零|
| 3| A>B>C|真的|一个|B| C类|零|
| 4| A>B>C>D|真的|一个|B| C类|D|
如果给定位置id = 3,则查询应返回id=(1,2,3)但为4的记录
如果给定位置id = 2,则查询应返回id=(1,2)但为2和4的记录
如果给定位置id = 4,则查询应返回id= 1、2、3和4的所有记录
感谢任何帮助;)

xiozqbni

xiozqbni1#

您可以比较名称:

SELECT *
FROM   table_name
START WITH id = 3
CONNECT BY name = SUBSTR(PRIOR name, 1, INSTR(PRIOR name, '>', -1) - 1)

或:

SELECT *
FROM   table_name
START WITH id = 3
CONNECT BY PRIOR name
           = name || '>' || COALESCE(PRIOR level4, PRIOR level3, PRIOR level2)

或:

SELECT *
FROM   table_name t
WHERE  EXISTS(
         SELECT 1
         FROM   table_name x
         WHERE  x.name LIKE t.name || '%'
         AND    x.id = 3
       )

或者你可以比较一下水平:

SELECT *
FROM   table_name t
WHERE  EXISTS(
         SELECT 1
         FROM   table_name x
         WHERE  ( t.level4 = x.level4 OR t.level4 IS NULL )
         AND    ( t.level3 = x.level3 OR t.level3 IS NULL )
         AND    ( t.level2 = x.level2 OR t.level2 IS NULL )
         AND    t.level1 = x.level1
         AND    x.id = 3
       )

其中,对于样本数据:

CREATE TABLE table_name (id, Name, is_active, level1, level2, level3, level4) AS
SELECT 1, 'A',       'true', 'A', null, null, null FROM DUAL UNION ALL
SELECT 2, 'A>B',     'true', 'A', 'B',  null, null FROM DUAL UNION ALL
SELECT 3, 'A>B>C',   'true', 'A', 'B',  'C',  null FROM DUAL UNION ALL
SELECT 4, 'A>B>C>D', 'true', 'A', 'B',  'C',  'D'  FROM DUAL UNION ALL
SELECT 5, 'A>E',     'true', 'A', 'E',  NULL, NULL FROM DUAL UNION ALL
SELECT 6, 'A>E>C',   'true', 'A', 'E',  'C',  NULL FROM DUAL UNION ALL
SELECT 7, 'A>E>C>D', 'true', 'A', 'E',  'C',  'D'  FROM DUAL;

所有输出:
| ID|名称|激活|1级|2级|3级|4级|
| - -----|- -----|- -----|- -----|- -----|- -----|- -----|
| 一个|一个|真的|一个|联系我们|联系我们|联系我们|
| 2| A>B|真的|一个|B|联系我们|联系我们|
| 3| A>B>C|真的|一个|B| C类|联系我们|
fiddle

ttisahbt

ttisahbt2#

**编辑:**愚蠢的我...看到另一个答案,我意识到CONCAT enating所有这些COALESCE s结果都会导致name,导致:

SELECT * FROM locations
  WHERE id = referenceId
  OR STARTS_WITH((SELECT name FROM locations WHERE id = referenceId), name)
  OR (SELECT name FROM locations WHERE id = referenceId) LIKE CONCAT(name, '%')

当然,只需要一个OR
我的方法:

SELECT * FROM locations
  WHERE id = referenceId OR
  STARTS_WITH
  (
    (SELECT CONCAT(level1, COALESCE(CONCAT('>', level2), ''), COALESCE(CONCAT('>', level3), '')) FROM locations WHERE id = referenceId),
    name
  )

交替地

SELECT * FROM locations
  WHERE id = referenceId OR
    (SELECT CONCAT(level1, COALESCE(CONCAT('>', level2), ''), COALESCE(CONCAT('>', level3), '')) FROM locations WHERE id = referenceId)
    LIKE CONCAT(name, '%')

两者都工作正常(demo/demo),目前最好的,出现在我的脑海。
必须承认,这两种方法都需要在查询中插入id两次,这可以通过join来避免,但代价可能会更高。
如果其中任何一个在你的数据库上不起作用(只用PostgreSQL测试),不管出于什么原因,我以前的尝试是:

WITH levels AS (
  SELECT
    level1 AS l1,
    CONCAT(level1, COALESCE(CONCAT('>', level2), '')) AS l2,
    CONCAT(level1, COALESCE(CONCAT('>', level2), ''), COALESCE(CONCAT('>', level3), '')) AS l3
  FROM locations WHERE id = referenceId
)
SELECT * FROM locations
  WHERE id = referenceId
  OR name = (SELECT l1 FROM levels)
  OR name = (SELECT l2 FROM levels)
  OR name = (SELECT l3 FROM levels)
)

Works,但需要计算更多的字符串和更多的比较,因此效率较低。
对于WITH可能不受支持的情况,我也设法这样做了:

SELECT * from location WHERE id = referenceId OR name IN
(
    SELECT level1
      FROM location WHERE id = referenceId
  UNION
    SELECT CONCAT(level1, COALESCE(CONCAT('>', level2), ''))
      FROM location WHERE id = referenceId
  UNION
    SELECT CONCAT(level1, COALESCE(CONCAT('>', level2), ''), COALESCE(CONCAT('>', level3), ''))
      FROM location WHERE id = referenceId
)

这是我最初的尝试--尽管有了所有这些子查询,它肯定不会太有效了……

n1bvdmb6

n1bvdmb63#

假设Name中的最后一个字符表示当前级别,最后一个字符之前的字符表示父级别,您可以使用递归查询的典型方法来解决这个问题。

with cte(id, parent_lvl, current_lvl) as
(
  select id, substr(reverse(name), 3, 1) parent_lvl, substr(name, -1) current_lvl
  from tbl
  where id = 3
  union all
  select t.id, substr(reverse(t.name), 3, 1), substr(t.name, -1)
  from tbl t join cte c
  on c.parent_lvl = substr(t.name, -1)
)
select id from cte

demo

相关问题