面白かった。
私の目的は、適切にリンクされた階層構造で、各レベルごとにランダムな数の子行を持つ指定された数のレベルを生成することでした。この構造の準備ができたら、ファイル名やフォルダー名などの追加情報を簡単に追加できます。
そこで、ツリーを保存するための古典的なテーブルを生成したかったのです。
ID int NOT NULL
ParentID int NULL
Lvl int NOT NULL
再帰を扱っているため、再帰的なCTEは自然な選択のようです。
数字の表が必要になります。テーブルの番号は1から始まる必要があります。テーブルには少なくとも20個の番号が必要ですMAX(LvlMax)
。
CREATE TABLE [dbo].[Numbers](
[Number] [int] NOT NULL,
CONSTRAINT [PK_Numbers] PRIMARY KEY CLUSTERED
(
[Number] ASC
));
INSERT INTO Numbers(Number)
SELECT TOP(1000)
ROW_NUMBER() OVER(ORDER BY S.object_id) AS Number
FROM
sys.all_objects AS S
ORDER BY Number;
データ生成のパラメーターはテーブルに保存する必要があります。
DECLARE @Intervals TABLE (Lvl int, LvlMin int, LvlMax int);
INSERT INTO @Intervals (Lvl, LvlMin, LvlMax) VALUES
(1, 5, 20),
(2, 1, 10),
(3, 1, 5);
クエリは非常に柔軟で、すべてのパラメーターが1つの場所に分かれていることに注意してください。必要に応じて、パラメーターの行を追加するだけで、さらにレベルを追加できます。
このような動的な生成を可能にするには、次のレベルの行の乱数を覚えておく必要があったため、余分な列がありますChildRowCount
。
ユニークを 生成することIDs
もややトリッキーです。IDs
繰り返さないことを保証するために、1つの親行につき100個の子行の制限をハードコーディングしました。これがそのPOWER(100, CTE.Lvl)
目的です。その結果、には大きなギャップがありIDs
ます。その数はである可能性がMAX(LvlMax)
ありますが、簡単にするためにクエリに定数100を入れています。レベルの数はハードコーディングされていませんが、によって決定され@Intervals
ます。
この式
CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5
範囲内のランダムな浮動小数点数を生成[0..1)
し、必要な間隔にスケーリングされます。
クエリロジックは単純です。再帰的です。最初のステップは、最初のレベルの行のセットを生成します。行数は、の乱数によって決定されTOP
ます。また、各行には、に格納された子行の個別のランダムな数がありChildRowCount
ます。
再帰部分はCROSS APPLY
、各親行ごとに指定された数の子行を生成するために使用します。CTEの再帰部分では許可されていないため、のWHERE Numbers.Number <= CTE.ChildRowCount
代わりに使用する必要がありました。以前、SQL Serverのこの制限について知らなかった。TOP(CTE.ChildRowCount)
TOP
WHERE CTE.ChildRowCount IS NOT NULL
再帰を停止します。
SQLフィドル
WITH
CTE
AS
(
SELECT
TOP(CAST(
(CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5) *
(
1 + (SELECT I.LvlMax FROM @Intervals AS I WHERE I.Lvl = 1)
- (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = 1)
)
+ (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = 1)
AS int))
Numbers.Number AS ID
,NULL AS ParentID
,1 AS Lvl
,CAST(
(CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5) *
(
1 + (SELECT I.LvlMax FROM @Intervals AS I WHERE I.Lvl = 2)
- (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = 2)
)
+ (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = 2)
AS int) AS ChildRowCount
FROM Numbers
ORDER BY Numbers.Number
UNION ALL
SELECT
CA.Number + CTE.ID * POWER(100, CTE.Lvl) AS ID
,CTE.ID AS ParentID
,CTE.Lvl + 1 AS Lvl
,CA.ChildRowCount
FROM
CTE
CROSS APPLY
(
SELECT
Numbers.Number
,CAST(
(CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5) *
(
1 + (SELECT I.LvlMax FROM @Intervals AS I WHERE I.Lvl = CTE.Lvl + 2)
- (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = CTE.Lvl + 2)
)
+ (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = CTE.Lvl + 2)
AS int) AS ChildRowCount
FROM Numbers
WHERE Numbers.Number <= CTE.ChildRowCount
) AS CA
WHERE
CTE.ChildRowCount IS NOT NULL
)
SELECT *
FROM CTE
ORDER BY Lvl, ParentID, ID;
結果(運が良ければ20 + 20 * 10 + 200 * 5 = 1220行まで可能)
+---------+----------+-----+-------------------+
| ID | ParentID | Lvl | ChildRowCount |
+---------+----------+-----+-------------------+
| 1 | NULL | 1 | 3 |
| 2 | NULL | 1 | 1 |
| 3 | NULL | 1 | 6 |
| 4 | NULL | 1 | 5 |
| 5 | NULL | 1 | 3 |
| 6 | NULL | 1 | 7 |
| 7 | NULL | 1 | 1 |
| 8 | NULL | 1 | 6 |
| 101 | 1 | 2 | 3 |
| 102 | 1 | 2 | 5 |
| 103 | 1 | 2 | 1 |
| 201 | 2 | 2 | 5 |
| 301 | 3 | 2 | 4 |
| 302 | 3 | 2 | 5 |
| 303 | 3 | 2 | 1 |
| 304 | 3 | 2 | 2 |
| 305 | 3 | 2 | 4 |
| 306 | 3 | 2 | 3 |
| 401 | 4 | 2 | 3 |
| 402 | 4 | 2 | 1 |
| 403 | 4 | 2 | 2 |
| 404 | 4 | 2 | 2 |
| 405 | 4 | 2 | 4 |
| 501 | 5 | 2 | 1 |
| 502 | 5 | 2 | 3 |
| 503 | 5 | 2 | 5 |
| 601 | 6 | 2 | 2 |
| 602 | 6 | 2 | 5 |
| 603 | 6 | 2 | 3 |
| 604 | 6 | 2 | 3 |
| 605 | 6 | 2 | 4 |
| 606 | 6 | 2 | 5 |
| 607 | 6 | 2 | 4 |
| 701 | 7 | 2 | 2 |
| 801 | 8 | 2 | 2 |
| 802 | 8 | 2 | 3 |
| 803 | 8 | 2 | 3 |
| 804 | 8 | 2 | 3 |
| 805 | 8 | 2 | 5 |
| 806 | 8 | 2 | 2 |
| 1010001 | 101 | 3 | NULL |
| 1010002 | 101 | 3 | NULL |
| 1010003 | 101 | 3 | NULL |
| 1020001 | 102 | 3 | NULL |
| 1020002 | 102 | 3 | NULL |
| 1020003 | 102 | 3 | NULL |
| 1020004 | 102 | 3 | NULL |
| 1020005 | 102 | 3 | NULL |
| 1030001 | 103 | 3 | NULL |
| 2010001 | 201 | 3 | NULL |
| 2010002 | 201 | 3 | NULL |
| 2010003 | 201 | 3 | NULL |
| 2010004 | 201 | 3 | NULL |
| 2010005 | 201 | 3 | NULL |
| 3010001 | 301 | 3 | NULL |
| 3010002 | 301 | 3 | NULL |
| 3010003 | 301 | 3 | NULL |
| 3010004 | 301 | 3 | NULL |
| 3020001 | 302 | 3 | NULL |
| 3020002 | 302 | 3 | NULL |
| 3020003 | 302 | 3 | NULL |
| 3020004 | 302 | 3 | NULL |
| 3020005 | 302 | 3 | NULL |
| 3030001 | 303 | 3 | NULL |
| 3040001 | 304 | 3 | NULL |
| 3040002 | 304 | 3 | NULL |
| 3050001 | 305 | 3 | NULL |
| 3050002 | 305 | 3 | NULL |
| 3050003 | 305 | 3 | NULL |
| 3050004 | 305 | 3 | NULL |
| 3060001 | 306 | 3 | NULL |
| 3060002 | 306 | 3 | NULL |
| 3060003 | 306 | 3 | NULL |
| 4010001 | 401 | 3 | NULL |
| 4010002 | 401 | 3 | NULL |
| 4010003 | 401 | 3 | NULL |
| 4020001 | 402 | 3 | NULL |
| 4030001 | 403 | 3 | NULL |
| 4030002 | 403 | 3 | NULL |
| 4040001 | 404 | 3 | NULL |
| 4040002 | 404 | 3 | NULL |
| 4050001 | 405 | 3 | NULL |
| 4050002 | 405 | 3 | NULL |
| 4050003 | 405 | 3 | NULL |
| 4050004 | 405 | 3 | NULL |
| 5010001 | 501 | 3 | NULL |
| 5020001 | 502 | 3 | NULL |
| 5020002 | 502 | 3 | NULL |
| 5020003 | 502 | 3 | NULL |
| 5030001 | 503 | 3 | NULL |
| 5030002 | 503 | 3 | NULL |
| 5030003 | 503 | 3 | NULL |
| 5030004 | 503 | 3 | NULL |
| 5030005 | 503 | 3 | NULL |
| 6010001 | 601 | 3 | NULL |
| 6010002 | 601 | 3 | NULL |
| 6020001 | 602 | 3 | NULL |
| 6020002 | 602 | 3 | NULL |
| 6020003 | 602 | 3 | NULL |
| 6020004 | 602 | 3 | NULL |
| 6020005 | 602 | 3 | NULL |
| 6030001 | 603 | 3 | NULL |
| 6030002 | 603 | 3 | NULL |
| 6030003 | 603 | 3 | NULL |
| 6040001 | 604 | 3 | NULL |
| 6040002 | 604 | 3 | NULL |
| 6040003 | 604 | 3 | NULL |
| 6050001 | 605 | 3 | NULL |
| 6050002 | 605 | 3 | NULL |
| 6050003 | 605 | 3 | NULL |
| 6050004 | 605 | 3 | NULL |
| 6060001 | 606 | 3 | NULL |
| 6060002 | 606 | 3 | NULL |
| 6060003 | 606 | 3 | NULL |
| 6060004 | 606 | 3 | NULL |
| 6060005 | 606 | 3 | NULL |
| 6070001 | 607 | 3 | NULL |
| 6070002 | 607 | 3 | NULL |
| 6070003 | 607 | 3 | NULL |
| 6070004 | 607 | 3 | NULL |
| 7010001 | 701 | 3 | NULL |
| 7010002 | 701 | 3 | NULL |
| 8010001 | 801 | 3 | NULL |
| 8010002 | 801 | 3 | NULL |
| 8020001 | 802 | 3 | NULL |
| 8020002 | 802 | 3 | NULL |
| 8020003 | 802 | 3 | NULL |
| 8030001 | 803 | 3 | NULL |
| 8030002 | 803 | 3 | NULL |
| 8030003 | 803 | 3 | NULL |
| 8040001 | 804 | 3 | NULL |
| 8040002 | 804 | 3 | NULL |
| 8040003 | 804 | 3 | NULL |
| 8050001 | 805 | 3 | NULL |
| 8050002 | 805 | 3 | NULL |
| 8050003 | 805 | 3 | NULL |
| 8050004 | 805 | 3 | NULL |
| 8050005 | 805 | 3 | NULL |
| 8060001 | 806 | 3 | NULL |
| 8060002 | 806 | 3 | NULL |
+---------+----------+-----+-------------------+
リンクされた階層の代わりにフルパスを生成する
我々は唯一のフルパスに興味がある場合はN
、深いレベル、我々は省略することができますID
し、ParentID
CTEから。補足テーブルに可能な名前のリストがある場合Names
、CTEでこのテーブルからそれらを選択するのは簡単です。Names
表は、各レベルのための十分な行を持つべきである:レベル1 20、レベル2のために10、レベル3 5。20 + 10 + 5 =合計35。レベルごとに異なる行セットを用意する必要はありませんが、適切に設定するのは簡単なので、私はそれを行いました。
DECLARE @Names TABLE (Lvl int, Name nvarchar(4000), SeqNumber int);
-- First level: AAA, BBB, CCC, etc.
INSERT INTO @Names (Lvl, Name, SeqNumber)
SELECT 1, REPLICATE(CHAR(Number+64), 3) AS Name, Number AS SeqNumber
FROM Numbers
WHERE Number <= 20;
-- Second level: 001, 002, 003, etc.
INSERT INTO @Names (Lvl, Name, SeqNumber)
SELECT 2, REPLACE(STR(Number, 3), ' ', '0') AS Name, Number AS SeqNumber
FROM Numbers
WHERE Number <= 10;
-- Third level: I, II, III, IV, V
INSERT INTO @Names (Lvl, Name, SeqNumber) VALUES
(3, 'I', 1),
(3, 'II', 2),
(3, 'III', 3),
(3, 'IV', 4),
(3, 'V', 5);
SQL Fiddle最後のクエリです。私は分割FullPath
にFilePath
してFileName
。
WITH
CTE
AS
(
SELECT
TOP(CAST(
(CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5) *
(
1 + (SELECT I.LvlMax FROM @Intervals AS I WHERE I.Lvl = 1)
- (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = 1)
)
+ (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = 1)
AS int))
1 AS Lvl
,CAST(
(CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5) *
(
1 + (SELECT I.LvlMax FROM @Intervals AS I WHERE I.Lvl = 2)
- (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = 2)
)
+ (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = 2)
AS int) AS ChildRowCount
,N.Name AS FullPath
,N.Name AS [FilePath]
,CAST(N'' AS nvarchar(4000)) AS [FileName]
FROM
Numbers
INNER JOIN @Names AS N ON
N.SeqNumber = Numbers.Number AND N.Lvl = 1
ORDER BY Numbers.Number
UNION ALL
SELECT
CTE.Lvl + 1 AS Lvl
,CA.ChildRowCount
,CTE.FullPath + '\' + CA.Name AS FullPath
,CASE WHEN CA.ChildRowCount IS NOT NULL
THEN CTE.FullPath + '\' + CA.Name
ELSE CTE.FullPath END AS [FilePath]
,CASE WHEN CA.ChildRowCount IS NULL
THEN CA.Name
ELSE N'' END AS [FileName]
FROM
CTE
CROSS APPLY
(
SELECT
Numbers.Number
,CAST(
(CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5) *
(
1 + (SELECT I.LvlMax FROM @Intervals AS I WHERE I.Lvl = CTE.Lvl + 2)
- (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = CTE.Lvl + 2)
)
+ (SELECT I.LvlMin FROM @Intervals AS I WHERE I.Lvl = CTE.Lvl + 2)
AS int) AS ChildRowCount
,N.Name
FROM
Numbers
INNER JOIN @Names AS N ON
N.SeqNumber = Numbers.Number AND N.Lvl = CTE.Lvl + 1
WHERE Numbers.Number <= CTE.ChildRowCount
) AS CA
WHERE
CTE.ChildRowCount IS NOT NULL
)
SELECT
CTE.FullPath
,CTE.[FilePath]
,CTE.[FileName]
FROM CTE
WHERE CTE.ChildRowCount IS NULL
ORDER BY FullPath;
結果
+-------------+----------+----------+
| FullPath | FilePath | FileName |
+-------------+----------+----------+
| AAA\001\I | AAA\001 | I |
| AAA\001\II | AAA\001 | II |
| AAA\002\I | AAA\002 | I |
| AAA\002\II | AAA\002 | II |
| AAA\002\III | AAA\002 | III |
| AAA\002\IV | AAA\002 | IV |
| AAA\002\V | AAA\002 | V |
| AAA\003\I | AAA\003 | I |
| AAA\003\II | AAA\003 | II |
| AAA\003\III | AAA\003 | III |
| AAA\004\I | AAA\004 | I |
| AAA\004\II | AAA\004 | II |
| AAA\004\III | AAA\004 | III |
| AAA\004\IV | AAA\004 | IV |
| BBB\001\I | BBB\001 | I |
| BBB\001\II | BBB\001 | II |
| CCC\001\I | CCC\001 | I |
| CCC\001\II | CCC\001 | II |
| CCC\001\III | CCC\001 | III |
| CCC\001\IV | CCC\001 | IV |
| CCC\001\V | CCC\001 | V |
| CCC\002\I | CCC\002 | I |
| CCC\003\I | CCC\003 | I |
| CCC\003\II | CCC\003 | II |
| CCC\004\I | CCC\004 | I |
| CCC\004\II | CCC\004 | II |
| CCC\005\I | CCC\005 | I |
| CCC\005\II | CCC\005 | II |
| CCC\005\III | CCC\005 | III |
| CCC\006\I | CCC\006 | I |
| CCC\006\II | CCC\006 | II |
| CCC\006\III | CCC\006 | III |
| CCC\006\IV | CCC\006 | IV |
| CCC\007\I | CCC\007 | I |
| CCC\007\II | CCC\007 | II |
| CCC\007\III | CCC\007 | III |
| CCC\007\IV | CCC\007 | IV |
| CCC\008\I | CCC\008 | I |
| CCC\008\II | CCC\008 | II |
| CCC\008\III | CCC\008 | III |
| CCC\009\I | CCC\009 | I |
| CCC\009\II | CCC\009 | II |
| CCC\009\III | CCC\009 | III |
| CCC\009\IV | CCC\009 | IV |
| CCC\010\I | CCC\010 | I |
| CCC\010\II | CCC\010 | II |
| CCC\010\III | CCC\010 | III |
| DDD\001\I | DDD\001 | I |
| DDD\001\II | DDD\001 | II |
| DDD\001\III | DDD\001 | III |
| DDD\001\IV | DDD\001 | IV |
| DDD\002\I | DDD\002 | I |
| DDD\003\I | DDD\003 | I |
| DDD\003\II | DDD\003 | II |
| DDD\003\III | DDD\003 | III |
| DDD\003\IV | DDD\003 | IV |
| DDD\004\I | DDD\004 | I |
| DDD\004\II | DDD\004 | II |
| DDD\004\III | DDD\004 | III |
| DDD\005\I | DDD\005 | I |
| DDD\006\I | DDD\006 | I |
| DDD\006\II | DDD\006 | II |
| DDD\006\III | DDD\006 | III |
| DDD\007\I | DDD\007 | I |
| DDD\007\II | DDD\007 | II |
| DDD\008\I | DDD\008 | I |
| DDD\008\II | DDD\008 | II |
| DDD\008\III | DDD\008 | III |
| DDD\009\I | DDD\009 | I |
| DDD\009\II | DDD\009 | II |
| DDD\010\I | DDD\010 | I |
| DDD\010\II | DDD\010 | II |
| DDD\010\III | DDD\010 | III |
| DDD\010\IV | DDD\010 | IV |
| DDD\010\V | DDD\010 | V |
| EEE\001\I | EEE\001 | I |
| EEE\001\II | EEE\001 | II |
| FFF\001\I | FFF\001 | I |
| FFF\002\I | FFF\002 | I |
| FFF\002\II | FFF\002 | II |
| FFF\003\I | FFF\003 | I |
| FFF\003\II | FFF\003 | II |
| FFF\003\III | FFF\003 | III |
| FFF\003\IV | FFF\003 | IV |
| FFF\003\V | FFF\003 | V |
| FFF\004\I | FFF\004 | I |
| FFF\004\II | FFF\004 | II |
| FFF\004\III | FFF\004 | III |
| FFF\004\IV | FFF\004 | IV |
| FFF\005\I | FFF\005 | I |
| FFF\006\I | FFF\006 | I |
| FFF\007\I | FFF\007 | I |
| FFF\007\II | FFF\007 | II |
| FFF\007\III | FFF\007 | III |
| GGG\001\I | GGG\001 | I |
| GGG\001\II | GGG\001 | II |
| GGG\001\III | GGG\001 | III |
| GGG\002\I | GGG\002 | I |
| GGG\003\I | GGG\003 | I |
| GGG\003\II | GGG\003 | II |
| GGG\003\III | GGG\003 | III |
| GGG\004\I | GGG\004 | I |
| GGG\004\II | GGG\004 | II |
| HHH\001\I | HHH\001 | I |
| HHH\001\II | HHH\001 | II |
| HHH\001\III | HHH\001 | III |
| HHH\002\I | HHH\002 | I |
| HHH\002\II | HHH\002 | II |
| HHH\002\III | HHH\002 | III |
| HHH\002\IV | HHH\002 | IV |
| HHH\002\V | HHH\002 | V |
| HHH\003\I | HHH\003 | I |
| HHH\003\II | HHH\003 | II |
| HHH\003\III | HHH\003 | III |
| HHH\003\IV | HHH\003 | IV |
| HHH\003\V | HHH\003 | V |
| HHH\004\I | HHH\004 | I |
| HHH\004\II | HHH\004 | II |
| HHH\004\III | HHH\004 | III |
| HHH\004\IV | HHH\004 | IV |
| HHH\004\V | HHH\004 | V |
| HHH\005\I | HHH\005 | I |
| HHH\005\II | HHH\005 | II |
| HHH\005\III | HHH\005 | III |
| HHH\005\IV | HHH\005 | IV |
| HHH\005\V | HHH\005 | V |
| HHH\006\I | HHH\006 | I |
| HHH\007\I | HHH\007 | I |
| HHH\007\II | HHH\007 | II |
| HHH\007\III | HHH\007 | III |
| HHH\008\I | HHH\008 | I |
| HHH\008\II | HHH\008 | II |
| HHH\008\III | HHH\008 | III |
| HHH\008\IV | HHH\008 | IV |
| HHH\008\V | HHH\008 | V |
+-------------+----------+----------+
TOP(n)
2CROSS APPLY
秒で正しく動作させることができませんでした。何か他の機能が得られたらそのコードを削除したので、私が何を違う/間違ってしたのか分かりません。このアップデートを提供したので、すぐに投稿します。そして、上記のコメントのほとんどを整理しました。