2

XML スタイルの階層を返すクエリを作成するのが非常に困難です。

Web サイトの URL の階層を含むデータベース テーブルがあります。テーブルには次の列が含まれます: ID、URL、DisplayName、ParentID、ItemOrder

親 ID は、現在のアイテムとその親の間に再帰的な関係を形成します。アイテムは階層内でその親の下に配置する必要があり、階層内の同じレベルにあるアイテムに対してアイテム順序を使用して順序付けする必要もあります。

再帰クエリを機能させることができたので、階層を順番にドリルダウンしますが、アイテムの順序でこれを注文することもできません。

私の現在のクエリは以下のとおりです。

WITH Parents AS
(
SELECT MenuItemId, URL, ParentItemId, ItemOrder
FROM CambsMenu

UNION ALL

SELECT si.MenuItemId, si.URL, si.ParentItemId, si.ItemOrder
FROM CambsMenu si INNER JOIN Parents p
ON si.ParentItemId = p.MenuItemId
)

SELECT DISTINCT *
FROM Parents
4

6 に答える 6

11

通常の階層的アプローチ:

select *
into emp
from 
(values
(1, 'President', NULL),
(2, 'Vice President', 1),
(3, 'CEO', 2),
(4, 'CTO', 2),
(5, 'Group Project Manager', 4),
(6, 'Project Manager 1', 5),
(7, 'Project Manager 2', 5),
(8, 'Team Leader 1', 6),
(9, 'Software Engineer 1', 8),
(10, 'Software Engineer 2', 8),
(11, 'Test Lead 1', 6),
(12, 'Tester 1', 11),
(13, 'Tester 2', 11),
(14, 'Team Leader 2', 7),
(15, 'Software Engineer 3', 14),
(16, 'Software Engineer 4', 14),
(17, 'Test Lead 2', 7),
(18, 'Tester 3', 17),
(19, 'Tester 4', 17),
(20, 'Tester 5', 17)
) as x(emp_id, emp_name, mgr_id)

クエリ:

with recursive org(emp_id, emp_name, emp_level, mgr_id, sort) as
(
 select  
  a.emp_id, a.emp_name, 0, a.mgr_id,  
  a.emp_name
 from emp a
 where a.mgr_id is null

 union all

 select 
  b.emp_id, b.emp_name, emp_level + 1, b.mgr_id, 

  sort || ' : ' || b.emp_name 

 from emp b
 join org on org.emp_id = b.mgr_id
)
select 
 emp_id, repeat(' ', emp_level * 2) || emp_name as emp_name, sort 
from org
order by sort

出力:

 emp_id |            emp_name             |                                                        sort                                                        
--------+---------------------------------+--------------------------------------------------------------------------------------------------------------------
      1 | President                       | President
      2 |   Vice President                | President : Vice President
      3 |     CEO                         | President : Vice President : CEO
      4 |     CTO                         | President : Vice President : CTO
      5 |       Group Project Manager     | President : Vice President : CTO : Group Project Manager
      6 |         Project Manager 1       | President : Vice President : CTO : Group Project Manager : Project Manager 1
      8 |           Team Leader 1         | President : Vice President : CTO : Group Project Manager : Project Manager 1 : Team Leader 1
      9 |             Software Engineer 1 | President : Vice President : CTO : Group Project Manager : Project Manager 1 : Team Leader 1 : Software Engineer 1
     10 |             Software Engineer 2 | President : Vice President : CTO : Group Project Manager : Project Manager 1 : Team Leader 1 : Software Engineer 2
     11 |           Test Lead 1           | President : Vice President : CTO : Group Project Manager : Project Manager 1 : Test Lead 1
     12 |             Tester 1            | President : Vice President : CTO : Group Project Manager : Project Manager 1 : Test Lead 1 : Tester 1
     13 |             Tester 2            | President : Vice President : CTO : Group Project Manager : Project Manager 1 : Test Lead 1 : Tester 2
      7 |         Project Manager 2       | President : Vice President : CTO : Group Project Manager : Project Manager 2
     14 |           Team Leader 2         | President : Vice President : CTO : Group Project Manager : Project Manager 2 : Team Leader 2
     15 |             Software Engineer 3 | President : Vice President : CTO : Group Project Manager : Project Manager 2 : Team Leader 2 : Software Engineer 3
     16 |             Software Engineer 4 | President : Vice President : CTO : Group Project Manager : Project Manager 2 : Team Leader 2 : Software Engineer 4
     17 |           Test Lead 2           | President : Vice President : CTO : Group Project Manager : Project Manager 2 : Test Lead 2
     18 |             Tester 3            | President : Vice President : CTO : Group Project Manager : Project Manager 2 : Test Lead 2 : Tester 3
     19 |             Tester 4            | President : Vice President : CTO : Group Project Manager : Project Manager 2 : Test Lead 2 : Tester 4
     20 |             Tester 5            | President : Vice President : CTO : Group Project Manager : Project Manager 2 : Test Lead 2 : Tester 5
(20 rows)

次に、グループ プロジェクト マネージャーの並べ替えをオーバーライドし、プロジェクト マネージャー 2 をプロジェクト マネージャー 1 の前に配置し、プロジェクト マネージャー 1 をプロジェクト マネージャー 2 の後に配置します。また、テスター 4 を 3 の前に配置し、テスター 3 をテスター 4 の後に配置します。

alter table emp add column order_override int null;

update emp set order_override = 1 where emp_id = 7; -- PM 2
update emp set order_override = 2 where emp_id = 6; -- PM 1

update emp set order_override = 1 where emp_id = 19; -- Tester 4
update emp set order_override = 2 where emp_id = 18; -- Tester 3

クエリ:

with recursive org(emp_id, emp_name, emp_level, mgr_id, sort) as
(
 select  
  a.emp_id, a.emp_name, 0, a.mgr_id,  
  a.emp_name
 from emp a
 where a.mgr_id is null

 union all

 select 
  b.emp_id, b.emp_name, emp_level + 1, b.mgr_id, 

  sort || ' : ' || coalesce( lpad(order_override::text, 10, '0'), b.emp_name )
 from emp b
 join org on org.emp_id = b.mgr_id
)
select 
 emp_id, repeat(' ', emp_level * 2) || emp_name as emp_name, sort 
from org
order by sort

出力:

 emp_id |            emp_name             |                                                    sort                                                     
--------+---------------------------------+-------------------------------------------------------------------------------------------------------------
      1 | President                       | President
      2 |   Vice President                | President : Vice President
      3 |     CEO                         | President : Vice President : CEO
      4 |     CTO                         | President : Vice President : CTO
      5 |       Group Project Manager     | President : Vice President : CTO : Group Project Manager
      7 |         Project Manager 2       | President : Vice President : CTO : Group Project Manager : 0000000001
     14 |           Team Leader 2         | President : Vice President : CTO : Group Project Manager : 0000000001 : Team Leader 2
     15 |             Software Engineer 3 | President : Vice President : CTO : Group Project Manager : 0000000001 : Team Leader 2 : Software Engineer 3
     16 |             Software Engineer 4 | President : Vice President : CTO : Group Project Manager : 0000000001 : Team Leader 2 : Software Engineer 4
     17 |           Test Lead 2           | President : Vice President : CTO : Group Project Manager : 0000000001 : Test Lead 2
     19 |             Tester 4            | President : Vice President : CTO : Group Project Manager : 0000000001 : Test Lead 2 : 0000000001
     18 |             Tester 3            | President : Vice President : CTO : Group Project Manager : 0000000001 : Test Lead 2 : 0000000002
     20 |             Tester 5            | President : Vice President : CTO : Group Project Manager : 0000000001 : Test Lead 2 : Tester 5
      6 |         Project Manager 1       | President : Vice President : CTO : Group Project Manager : 0000000002
      8 |           Team Leader 1         | President : Vice President : CTO : Group Project Manager : 0000000002 : Team Leader 1
      9 |             Software Engineer 1 | President : Vice President : CTO : Group Project Manager : 0000000002 : Team Leader 1 : Software Engineer 1
     10 |             Software Engineer 2 | President : Vice President : CTO : Group Project Manager : 0000000002 : Team Leader 1 : Software Engineer 2
     11 |           Test Lead 1           | President : Vice President : CTO : Group Project Manager : 0000000002 : Test Lead 1
     12 |             Tester 1            | President : Vice President : CTO : Group Project Manager : 0000000002 : Test Lead 1 : Tester 1
     13 |             Tester 2            | President : Vice President : CTO : Group Project Manager : 0000000002 : Test Lead 1 : Tester 2
(20 rows)

データ プロジェクションで並べ替え列を使用しない場合:

with recursive org(emp_id, emp_name, emp_level, mgr_id, sort) as
(
 select  
  a.emp_id, a.emp_name, 0, a.mgr_id,  
  a.emp_name
 from emp a
 where a.mgr_id is null

 union all

 select 
  b.emp_id, b.emp_name, emp_level + 1, b.mgr_id, 

  sort || ' : ' || coalesce( lpad(order_override::text, 10, '0'), b.emp_name )
 from emp b
 join org on org.emp_id = b.mgr_id
)
select 
 emp_id, repeat(' ', emp_level * 2) || emp_name as emp_name
from org
order by sort

出力:

 emp_id |            emp_name             
--------+---------------------------------
      1 | President
      2 |   Vice President
      3 |     CEO
      4 |     CTO
      5 |       Group Project Manager
      7 |         Project Manager 2
     14 |           Team Leader 2
     15 |             Software Engineer 3
     16 |             Software Engineer 4
     17 |           Test Lead 2
     19 |             Tester 4
     18 |             Tester 3
     20 |             Tester 5
      6 |         Project Manager 1
      8 |           Team Leader 1
      9 |             Software Engineer 1
     10 |             Software Engineer 2
     11 |           Test Lead 1
     12 |             Tester 1
     13 |             Tester 2
(20 rows)

プロジェクト マネージャ 2 はプロジェクト マネージャ 1 の前に配置されます。テスター 4 はテスター 3 の前に配置されます。

order_override(null 以外) がある場合、テクニックは b.name の数値テキスト置換にあります。

sort || ' : ' || coalesce( lpad(order_override::text, 10, '0'), b.emp_name )

上記のコードは Postgres です。Sql Server に変換するには、単語を削除し、にRECURSIVE変更REPEATします。REPLICATE||+

相当する...

lpad(order_override::text, 10, '0')

...は:

RIGHT( REPLICATE('0',10) + CONVERT(VARCHAR, order_override), 10)
于 2010-05-14T15:37:07.257 に答える
6

ご回答ありがとうございます。

完全を期すために、これが私が思いついた最終的な解決策です。ドットでサブセクションに区切られた文字列を作成します。以下のソリューションでは、ルート ノードで最大 9999 個のアイテムしかサポートされませんが、STR(ItemOrder,4) コマンドの数を変更するだけで先行ゼロの数を増やすことで、これを簡単に拡張できます。

WITH Parents AS
(
SELECT MenuItemId,
    URL,
    ParentItemId,
    DisplayName,
    OpenInNewWindow,
    ItemOrder,
    CAST((REPLACE(STR(ItemOrder,4),' ','0')) AS nvarchar(max)) AS OrderString
FROM CambsMenu
WHERE ParentItemId IS NULL

UNION ALL

SELECT si.MenuItemId,
    si.URL,
    si.ParentItemId,
    si.DisplayName,
    si.OpenInNewWindow,
    si.ItemOrder,
    (p.OrderString + '.' + CAST((REPLACE(STR(si.ItemOrder,4),' ','0')) AS nvarchar(max))) AS OrderString
FROM CambsMenu si INNER JOIN Parents p
ON si.ParentItemId = p.MenuItemId
)

SELECT * FROM Parents ORDER BY OrderString ASC
于 2010-05-14T15:36:06.843 に答える
3

兄弟の数は既知の値ですか? レベル数はわかっていますか?その場合、ItemOrder に対して操作を実行して、すべてのアイテムが一意の ItemOrder を持つことを保証し、その値で並べ替えることができます。

たとえば、どのアイテムも 10 を超える子を持つことはできず (ItemOrder の範囲は 0 から 9 まで)、最大で 5 つのレベルがあるとします。私がこれからやろうとしているのは、最初の親 ItemOrder を現在のアイテム注文の 10000 倍にすることです。それよりも子の ItemOrder は、現在の ItemOrder の 1000 倍に親の ItemOrder を加えたものになります。というように、毎回 0 を削除します。あなたはレベルを下げます。

WITH Parents AS
(
SELECT MenuItemId,
    URL,
    ParentItemId,
    (ItemOrder * 10000) AS ItemOrder,
    10000 AS Multiplier
FROM CambsMenu
WHERE ParentItemId IS NULL

UNION ALL

SELECT si.MenuItemId,
    si.URL,
    si.ParentItemId,
    (p.ItemOrder + si.ItemOrder * p.Multiplier/ 10) as ItemOrder,
    (p.Multiplier / 10) as Multiplier
FROM CambsMenu si INNER JOIN Parents p
ON si.ParentItemId = p.MenuItemId
)

SELECT * FROM Parents ORDER BY ItemOrder

レベルまたは子の数が不明な場合は、同様のアプローチを使用できますが、数値の ItemOrder を構築する代わりに、文字列 '1.10.20' が文字列 '2.1' よりも小さいことを保証する文字列 ItemOrder を構築できます。

于 2010-05-14T13:29:20.217 に答える
1
WITH Parents AS
(
SELECT MenuItemId, URL, ParentItemId, ItemOrder, 0 AS Level, Cast((ItemOrder+1000) as Varchar(MAX)) as MatPath
FROM CambsMenu
WHERE ParentItemId IS NULL

UNION ALL

SELECT si.MenuItemId, si.URL, si.ParentItemId, si.ItemOrder, Level + 1, MathPath + '.' CAST((si.ItemOrder+1000) as Varchar(MAX)
FROM CambsMenu si INNER JOIN Parents p
ON si.ParentItemId = p.MenuItemId
)

SELECT DISTINCT *
FROM Parents
ORDER BY MatPath

編集:回答が更新されました。もともとはレベルでソートされていましたが、これは求められていませんでした。また、答えはテストされていません。再度更新すると、シード クエリは IS NULL でフィルタリングされませんでした

EDIT2: これは、フロートとサブクエリを使用してリーフ/ブランチの最大数を取得する更新です。ItemOrder は 1 から始まり、穴がなく昇順であり、親ごとに再開されると想定されています。これは、整数を使用するように変換し直すことができます。これにより、並べ替えがレベル数でオーバーフロー/精度を失う可能性があることがより明確になります。

WITH Hierarchy AS
(
  SELECT MenuItemID, 
         URL, 
         ParentItemId, 
         ItemOrder,
         0 as level, 
         cast(1 as float) as hord
  FROM CambsMenu
  WHERE ParentItemId IS NULL 
UNION ALL
  SELECT r.MenuItemId, 
         r.URL, 
         r.PrentItemId,
         r.ItemOrder, 
         h.level + 1, 
         h.hord + r.ItemOrder/power(
           (SELECT MAX(n)+1 
            FROM (SELECT count(*) AS n 
                  FROM CambsMenu 
                  GROUP BY ParentItemId) t), h.level+1)
  FROM CambsMenu r INNER JOIN Hierarchy h
  ON r.ParentItemId = h.MenuItemId
)
SELECT *
FROM Hierarchy
ORDER BY hord;
于 2010-05-14T12:56:03.403 に答える
0

SQL は「階層」、「ツリー」、または「グラフ」タイプをサポートしていません。SQL/リレーショナル モデルは本質的に、これらのタイプを廃止する (必要とする) 目的で発明されたからです。

数学用語で「推移閉包」と呼ばれるものを計算するクエリを作成しました。これが本当にあなたが望んでいるものかどうかは疑わしい. リレーション (「テーブル」) に (1 2) と (2 3) のペアがある場合、クエリには結果のペア (1 3) が含まれます。ただし、(この例では) XML スタイルの結果に、番号 1 の直接の子として番号 3 を保持するタグを含めたくないのではないかと思います...

リレーショナル代数の GROUP 演算子を使用することで、あなたが望むことがより達成される可能性が高いと思います。警告: これは実際には「GROUP BY」と同じではありません (リレーショナル代数の GROUP 演算子は、値自体がテーブルである列を含むテーブルを生成します。たとえば、親の直接のすべての子を保持するテーブルなど)。あなたの特定の DBMS がそれをサポートしていない可能性が非常に高く、その場合、あなたはほとんど「あなたの DBMS に見捨てられ」、「おかしなことをすべてコーディングする以外に選択肢はありません (これは特に再帰を意味します)」あなた自身」。

于 2010-05-14T14:52:07.937 に答える