7

ユーザーツリーが獲得した各レベルのポイントを合計する必要があります。レベル1は、ユーザーの1レベル下のユーザーのユーザーポイントの合計です。レベル2は、ユーザーの2レベル下のユーザーのレベル1ポイントなどです。

計算は非本番サーバーで月に1回行われ、パフォーマンスの心配はありません。

SQLはそれをどのように行うのでしょうか?

あなたが混乱していても心配しないでください、私もそうです!

ユーザーテーブル:

ID    ParentID    Points
1     0           230
2     1           150
3     0           80
4     1           110
5     4           54
6     4           342

Tree:
0
|---\
1    3
| \
2  4---
    \  \
     5  6

出力は次のようになります。

ID    Points    Level1     Level2
1     230       150+110    150+110+54+342
2     150
3     80
4     110       54+342
5     54
6     342

SQLServerの構文と関数が望ましい...

4

9 に答える 9

2

OracleDBMSを使用している場合、OracleはCONNECT BY / STARTS WITH構文を使用したツリークエリをサポートしているため、非常に簡単です。SQL Serverの場合、一般的なテーブル式が役立つと思います

于 2008-09-18T10:10:55.933 に答える
2

ツリーは SQL ではうまく機能しません。書き込みアクセスが非常に (非常に) 少ない場合は、ツリーの実装を変更してネストされたセットを使用できます。これにより、このクエリが非常に簡単になります。

例(私が間違っていなければ):

SELECT SUM(points) 
FROM users 
where left > x and right < y 

ただし、ツリーに変更を加えるには、大量の行に触れる必要があります。クライアントで再帰を実行する方がおそらく良いでしょう。

于 2008-09-18T10:11:51.797 に答える
1

つまり、ストアドプロシージャを作成すると、おそらく最高のパフォーマンスが得られます。または、レベルの数が最大の場合、サブクエリを作成できますが、パフォーマンスは非常に低くなります。

(または、MS SQL Server 2008を入手して、新しい階層関数を入手することもできます。。。;))

于 2008-09-18T10:06:56.113 に答える
1

一般に、他の人が言ったように、SQL はそのような関係をうまく処理しません。通常、代理の「関係」テーブルが必要です (id、parent_id、(id、parent_id) の一意のキー)。

  • 「テーブル」にレコードを追加するたびに、次のことを行います。

    INSERT INTO relations (id, parent_id) VALUES ([current_id], [current_id]);

    INSERT INTO relations (id, parent_id) VALUES ([current_id], [current_parent_id]);

    INSERT INTO relations (id, parent_id) SELECT [current_id], parent_id FROM relations WHERE id = [current_parent_id];

  • サイクルを回避するためのロジックを持つ

  • 「リレーション」の更新、削除がストアド プロシージャで処理されることを確認してください

そのテーブルを考えると、次のことが必要です。

SELECT rel.parent_id, SUM(tbl.points)
FROM table tbl INNER JOIN relations rel ON tbl.id=rel.id
WHERE rel.parent_id <> 0
GROUP BY rel.parent_id;
于 2008-09-18T12:05:23.880 に答える
1

リレーショナル データベースに格納されたツリーを使用している場合は、「ネストされたセット」または「変更されたプレオーダー ツリー トラバーサル」を検討することをお勧めします。SQL は次のように単純になります。

SELECT id, 
       SUM(value) AS value 
FROM table 
WHERE left>left\_value\_of\_your\_node 
  AND right<$right\_value\_of\_your\_node;

...そして、関心のあるすべてのノードに対してこれを行います。

たぶんこれが役に立ちます: http://www.dbazine.com/oracle/or-articles/tropashko4または google を使用してください。

于 2008-09-18T10:14:49.090 に答える
1

わかりました、これで探している結果が得られますが、何かを見逃していないという保証はありません。出発点と考えてください。これを行うために SQL 2005 を使用しましたが、SQL 2000 は CTE をサポートしていません

WITH Parent (id, GrandParentId, parentId, Points, Level1Points, Level2Points)
AS
(
    -- Find root
    SELECT id,  
            0 AS GrandParentId,
            ParentId,
            Points,
            0 AS Level1Points,
            0 AS Level2Points
    FROM tblPoints ptr
    WHERE ptr.ParentId = 0

    UNION ALL (
    -- Level2 Points
    SELECT pa.GrandParentId AS Id,
            NULL AS GrandParentId,
            NULL AS ParentId,
            0 AS Points, 
            0 AS Level1Points,
            pa.Points  AS Level2Points
    FROM tblPoints pt
            JOIN Parent pa ON pa.GrandParentId = pt.Id 
    UNION  ALL
    -- Level1 Points
    SELECT pt.ParentId AS Id,
            NULL AS GrandParentId,
            NULL AS ParentId,
            0 AS Points, 
            pt.Points AS Level1Points,
            0 AS Level2Points
    FROM tblPoints pt
            JOIN Parent pa ON pa.Id = pt.ParentId AND pa.ParentId IS NOT NULL 
    UNION  ALL
    -- Points
    SELECT pt.id,
            pa.ParentId AS GrandParentId,
            pt.ParentId,
            pt.Points, 
            0 AS Level1Points,
            0 AS Level2Points
    FROM tblPoints pt
            JOIN Parent pa ON pa.Id = pt.ParentId AND pa.ParentId IS NOT NULL )
)
SELECT id, 
    SUM(Points) AS Points,  
    SUM(Level1Points) AS Level1Points,
    CASE WHEN SUM(Level2Points) > 0 THEN  SUM(Level1Points) + SUM(Level2Points) ELSE 0 END AS Level2Points
FROM Parent
GROUP BY id 
ORDER by id
于 2008-09-18T14:44:54.760 に答える
0

ジョブを実行する単純な再帰関数を作成できます。私の MSSQL は少しさびていますが、次のようになります。

CREATE FUNCTION CALC
(
@node integer,
)
returns 
(
@total integer
)
as
begin
    select @total = (select node_value from yourtable where node_id = @node);

    declare @children table (value integer);
    insert into @children   
    select calc(node_id) from yourtable where parent_id = @node;

    @current = @current + select sum(value) from @children;
    return
end
于 2008-09-18T10:23:21.550 に答える
0

次の表:

Id   ParentId
1   NULL
11    1
12    1
110 11
111 11
112 11
120 12
121 12
122 12
123 12
124 12

そして、次のAmountテーブル:

Id     Val
110 500
111 50
112 5
120 3000
121 30000
122 300000

葉 (最後のレベル) の ID のみに値が定義されています。データを取得するための SQL クエリは次のようになります。

;WITH Data (Id, Val) AS
(
    select t.Id, SUM(v.val) as Val from dbo.TestTable t
    join dbo.Amount v on t.Id = v.Id
    group by t.Id
)

select cd.Id, ISNULL(SUM(cd.Val), 0) as Amount FROM
(
    -- level 3
    select t.Id, d.val from TestTable t
    left join Data d on d.id = t.Id

    UNION

    -- level 2
    select t.parentId as Id, sum(y.Val) from TestTable t
    left join Data y on y.id = t.Id
    where t.parentId is not null
    group by t.parentId

    UNION

    -- level 1
    select t.parentId as Id, sum(y.Val) from TestTable t
    join TestTable c on c.parentId = t.Id
    left join Data y on y.id = c.Id
    where t.parentId is not null
    group by t.parentId
) AS cd
group by id

これにより、次の出力が得られます。

Id     Amount
1     333555
11   555
12   333000
110 500
111 50
112 5
120 3000
121 30000
122 300000
123 0
124 0

これが役立つことを願っています。

于 2010-12-13T13:13:57.303 に答える
0

いくつかのオプションがあります。

  1. カーソルと再帰的なユーザー定義関数呼び出しを使用します (非常に遅いです)。
  2. キャッシュ テーブルを作成し、トリガーを使用して INSERT で更新します (これは最速のソリューションですが、メイン テーブルに多くの更新がある場合は問題になる可能性があります)。
  3. クライアント側で再帰計算を実行します (レコードが多すぎない場合に適しています)。
于 2008-09-18T10:12:51.840 に答える