web-dev-qa-db-ja.com

SQLテーブルからのネストされた/階層データの取得

「タグ」と「タスク」の間にテーブルの関係があります。 M:Mです。関係はテーブル 'TagTaskMapping'にマップされます。

TagAssignmentテーブルは、タグと日付の関係を格納します。したがって、タグを単一の日付(期間)にマップできます。

SQLからネストされた階層を出力したい。

SQLテーブル:

-- tag assigned to a specific date
CREATE TABLE [dbo].[TagAssignment](
    [TagAssignmentID] [int] IDENTITY(1,1) NOT NULL,
    [TagID] [int] NOT NULL,
    [Period] [date] NOT NULL
);

-- task(s) mapped to tag(s)
CREATE TABLE [dbo].[TagTaskMapping](
    [TagID] [int] NOT NULL,
    [TaskID] [int] NOT NULL
);

-- tag table
CREATE TABLE [dbo].[Tag](
    [TagID] [int] IDENTITY(1,1) NOT NULL,
    [TagName] [nvarchar](150) NOT NULL
)

TagAssignmentマッピングテーブルデータ:

TagAssignmentID TagID   Period
24                3    31/05/2017
14                2    31/05/2017

TagTaskMappingテーブルデータ:

TagID   TaskID
  2       1
  2       2
  2       3
  3       1
  3       3

これが私のquery ...です.

DECLARE @Period datetime = '2017-05-31'    
;WITH CTE_TagAssignment
AS
(
    -- GET TAG(S) Assigned to selected PERIOD
    SELECT 
         ta.TagID
        ,t.TagName
        ,null as 'Task'
    FROM dbo.TagAssignment ta
    INNER JOIN 
        dbo.Tag t
         ON t.TagID = ta.TagID
    WHERE ta.Period = @Period

    UNION ALL

    /**USING RECURSION!!!!**/
    -- foreach above tag assigned to a period, get it's associated task(s)
    SELECT 
        ttm.TagID
        ,null AS 'TagName'
        ,ttm.TaskID as 'Task'
    FROM CTE_TagAssignment cta
    INNER JOIN 
        dbo.TagTaskMapping ttm
        ON cta.TagID = ttm.TagID
)  
SELECT *
FROM CTE_TagAssignment  
OPTION (MAXRECURSION 100);

ただし、次のエラーが発生します:ステートメントが終了しました。ステートメントが完了する前に、最大再帰100を使い果たしました。

これが階層ですoutput希望します...

TagID   TagName   Task
2       Level 5 
                   1
                   2
                   3
3       Level 3 
                   1
                   3
3
K09

再帰的なソリューションを必要としない私見では、単純なJOINを使用してそれを得ることができます。

DECLARE @Period datetime = '20170531';

SELECT     t.TagID, t.TagName, tm.TaskID
FROM       TagAssignment ta
INNER JOIN TagTaskMapping tm
ON         tm.TagID = ta.TagID
INNER JOIN Tag t
ON         t.TagID = tm.TagID
WHERE      ta.Period = @period
ORDER BY   tm.TagID, tm.TaskID;
GO
 TagID | TagName | TaskID 
 ----:| :--- -----:
 2 |レベル5 | 1 
 2 |レベル5 | 2 
 2 |レベル5 | 3 
 3 |レベル3 | 1 
 3 |レベル3 | 3 

dbfiddle ---(ここ

2
McNets

注:これは基本的に、McNetsのソリューションのバリエーションであり、要求したフォーマットとCTEに関する問題の説明が含まれています。

McNetsが指摘しているように、実際には再帰クエリはありません。再帰CTEでは、通常、クエリの再帰部分に何らかの条件があり、強制的に停止します。最終的には、行を生成しない一連の値に遭遇します。あなたはそのような停止条件を持っていません。新しい行を繰り返し生成し続けるだけです。

あなたの本当の問題はフォーマットの問題です。

これにより、必要な結果が必要な形式で取得されます。

SELECT CASE WHEN Header = 1 THEN CAST(TagID as varchar(20)) ELSE '' END as TagID
      ,CASE WHEN Header = 1 THEN TagName ELSE '' END as TagName
      ,CASE WHEN Header = 1 THEN '' ELSE CAST(TaskId as varchar(20)) END as TaskID
  FROM (
        SELECT DISTINCT
               t.TagID, t.TagName, CAST(NULL as int) as TaskID, 1 as Header
          FROM TagAssignment ta
                 INNER JOIN TagTaskMapping tm ON ta.TagID = tm.TagID
                 INNER JOIN Tag t ON ta.TagID = t.TagID
         WHERE ta.Period = @period
        UNION ALL
        SELECT t.TagID, t.TagName, tm.TaskID, 0 as Header
          FROM TagAssignment ta
                 INNER JOIN TagTaskMapping tm ON ta.TagID = tm.TagID
                 INNER JOIN Tag t ON ta.TagID = t.TagID
         WHERE ta.Period = @period
       ) sq
 ORDER BY sq.TagID, Header DESC, sq.TaskID;

dbfiddle を確認します(ここでも、McNetsによって行われた作業に基づいています)。

1
RDFozz