สร้างลำดับชั้นของหลายระดับโดยที่แต่ละโหนดมีจำนวนชายด์แบบสุ่ม


16

ฉันต้องการสร้างข้อมูลทดสอบที่เกี่ยวข้องกับลำดับชั้น ฉันสามารถทำให้ง่ายและทำสองสามCROSS JOINวินาที แต่นั่นจะให้โครงสร้างที่เหมือนกันโดยสมบูรณ์ / ไม่มีการเปลี่ยนแปลงใด ๆ นั่นไม่เพียง แต่ดูน่าเบื่อ แต่การขาดความผันแปรในข้อมูลการทดสอบบางครั้งก็เป็นตัวกำหนดปัญหาที่อาจพบได้ ดังนั้นฉันต้องการสร้างลำดับชั้นที่ไม่เหมือนกันซึ่งปฏิบัติตามกฎเหล่านี้:

  • ลึก 3 ระดับ
    • ระดับ 1 คือการสุ่ม 5 - 20 โหนด
    • ระดับ 2 คือ 1 - 10 โหนดสุ่มต่อโหนดระดับ 1 แต่ละโหนด
    • ระดับ 3 คือ 1 - 5 โหนดสุ่มต่อโหนดระดับ 2 แต่ละโหนด
  • ทุกสาขาจะลึก 3 ระดับ ความสม่ำเสมอในเชิงลึกก็โอเค ณ จุดนี้
  • อาจมีการทับซ้อนกันในชื่อของโหนดลูกในระดับที่กำหนดใด ๆ (เช่นชื่อของโหนดลูกไม่จำเป็นต้องไม่ซ้ำกันในทุกโหนดในระดับเดียวกัน)
  • คำว่า "สุ่ม" ถูกกำหนดไว้ที่นี่ว่าเป็นการหลอกแบบสุ่มไม่ใช่แบบสุ่มที่ไม่ซ้ำกัน สิ่งนี้จำเป็นต้องกล่าวถึงเนื่องจากคำว่า "สุ่ม" มักใช้เพื่อหมายถึง "การเรียงลำดับแบบสุ่มของชุดที่กำหนดซึ่งไม่ได้สร้างรายการซ้ำ" ฉันยอมรับว่า random = random และถ้าจำนวนเด็กต่อโหนดระดับ 1 แต่ละอันมีเพียง 4, 7 และ 8 แม้จะข้าม 20 โหนดในระดับ 1 ที่มีโอกาสแพร่กระจาย 1 - 10 ลูกต่อโหนดเหล่านั้นแต่ละโหนด ถ้าอย่างนั้นก็ดีเพราะนั่นคือสิ่งที่สุ่ม
  • แม้ว่าสิ่งนี้สามารถทำได้ค่อนข้างง่ายด้วยWHILEลูปซ้อนกันการตั้งค่าคือการหาวิธีการตั้งค่า โดยทั่วไปแล้วการสร้างข้อมูลทดสอบไม่มีข้อกำหนดด้านประสิทธิภาพที่รหัสการผลิตจะมี แต่การถ่ายภาพโดยใช้วิธีการตั้งค่าจะมีความรู้มากขึ้นและช่วยในอนาคตด้วยการค้นหาแนวทางที่เป็นปัญหา ดังนั้นWHILEลูปจะไม่ถูกตัดออก แต่สามารถใช้ได้เฉพาะเมื่อไม่มีวิธีการตั้งค่าที่เป็นไปได้
  • Set-based = เป็นการสอบถามที่ดีเลิศโดยไม่คำนึงถึง CTEs, APPLYs และอื่น ๆ ดังนั้นการใช้ตารางตัวเลขที่มีอยู่หรือ inline เป็นเรื่องปกติ การใช้วิธีการในขณะที่ / เคอร์เซอร์ / ขั้นตอนจะไม่ทำงาน ฉันคิดว่าการจัดเตรียมบางส่วนของข้อมูลลงในตาราง temp หรือตัวแปรตารางได้ดีตราบใดที่การดำเนินการทั้งหมดตั้งค่าไว้โดยไม่มีการวนซ้ำ อย่างไรก็ตามที่ถูกกล่าวว่าวิธีการสืบค้นเดียวอาจจะได้รับการสนับสนุนมากกว่าแบบสอบถามหลายรายการเว้นแต่จะสามารถแสดงให้เห็นว่าวิธีการสืบค้นหลายแบบสอบถามเป็นจริงที่ดีกว่า โปรดระลึกไว้เสมอว่าสิ่งที่ถือว่า "ดีกว่า" มักเป็นอัตนัย ;-) โปรดทราบว่าการใช้ "โดยทั่วไป" ในประโยคก่อนหน้านั้นเป็นเรื่องส่วนตัวเช่นกัน
  • SQL Server รุ่นใดและรุ่นใด (2005 และใหม่กว่านั้นฉันว่า) จะทำ
  • T-SQL ล้วนๆ: ไม่มี SQLCLR ที่ไร้สาระอะไร !! อย่างน้อยก็ในแง่ของการสร้างข้อมูล การสร้างไดเรกทอรีและไฟล์จะทำได้โดยใช้ SQLCLR แต่ที่นี่ฉันแค่มุ่งเน้นไปที่การสร้างคุณค่าของสิ่งที่จะสร้าง
  • T-SQL Multi-statement TVF ได้รับการพิจารณาว่าเป็นขั้นตอนไม่ใช่ตามการตั้งค่าแม้ว่าจะอยู่ภายนอก มีบางครั้งที่เหมาะสมอย่างยิ่ง นี่ไม่ใช่ครั้งเดียว ตามบรรทัดเดียวกันเหล่านั้นฟังก์ชัน T-SQL Scalar ไม่ได้รับอนุญาตไม่เพียงเพราะเป็นขั้นตอน แต่บางครั้ง Query Optimizer บางครั้งแคชค่าของพวกเขา
  • T-SQL Inline TVFs (aka iTVFs) เป็น okey-dokey เนื่องจากมีการตั้งค่าและมีประสิทธิภาพเหมือนกับการใช้[ CROSS | OUTER ] APPLYซึ่งระบุไว้ข้างต้นว่าเป็น ok
  • การดำเนินการซ้ำของแบบสอบถาม (IES) ควรให้ผลลัพธ์ที่แตกต่างจากการรันครั้งก่อน
  • การปรับปรุงที่ชัดเจน 1:ชุดผลลัพธ์สุดท้ายควรแสดงเป็นหนึ่งแถวสำหรับแต่ละโหนดที่แตกต่างกันของระดับ 3 โดยมีเส้นทางแบบเต็มเริ่มต้นที่ระดับ 1 ซึ่งหมายความว่าค่า Level1 และ Level2 จำเป็นต้องทำซ้ำในแถวอย่างน้อยหนึ่งแถวยกเว้นในกรณีที่มีเพียงโหนด Level2 เดียวที่มีเพียงโหนด Level3 เดียวเท่านั้น
  • Clarification Update 2:มีการกำหนดลักษณะที่แข็งแกร่งมากสำหรับแต่ละโหนดที่มีชื่อหรือเลเบลไม่ใช่เฉพาะตัวเลข ซึ่งจะช่วยให้ข้อมูลการทดสอบที่ได้มีความหมายและสมจริงยิ่งขึ้น

ฉันไม่แน่ใจว่าข้อมูลเพิ่มเติมนี้มีความสำคัญหรือไม่ แต่ในกรณีที่ช่วยให้มีบริบทข้อมูลการทดสอบเกี่ยวข้องกับคำตอบของฉันสำหรับคำถามนี้:

นำเข้าไฟล์ XML ไปยัง SQL Server 2012

ในขณะที่ไม่เกี่ยวข้องในจุดนี้เป้าหมายสุดท้ายของการสร้างลำดับชั้นนี้คือการสร้างโครงสร้างไดเรกทอรีเพื่อทดสอบวิธีการระบบไฟล์แบบเรียกซ้ำ ระดับ 1 และ 2 จะเป็นไดเรกทอรีและระดับ 3 จะเป็นชื่อไฟล์ ฉันค้นหารอบ ๆ (ทั้งที่นี่และผ่านทาง Google) และพบการอ้างอิงเดียวเพื่อสร้างลำดับชั้นแบบสุ่ม:

Linux: สร้างลำดับชั้นของไดเร็กทอรี / ไฟล์แบบสุ่ม

คำถามนั้น (ใน StackOverflow) นั้นค่อนข้างใกล้เคียงกับผลลัพธ์ที่ต้องการเนื่องจากยังพยายามสร้างโครงสร้างไดเรกทอรีสำหรับการทดสอบ แต่คำถามนั้น (และคำตอบ) นั้นมุ่งเน้นไปที่การเขียนสคริปต์เชลล์ / Linux และไม่มากในโลกที่ตั้งตามที่เราอาศัยอยู่

ตอนนี้ฉันรู้วิธีสร้างข้อมูลแบบสุ่มและฉันกำลังทำอยู่แล้วเพื่อสร้างเนื้อหาของไฟล์เพื่อให้พวกเขาสามารถแสดงรูปแบบต่างๆ ส่วนที่ซับซ้อนนี่คือจำนวนขององค์ประกอบในแต่ละชุดเป็นแบบสุ่มไม่ใช่เขตข้อมูลเฉพาะ และจำนวนองค์ประกอบภายในแต่ละโหนดจะต้องมีการสุ่มจากโหนดอื่น ๆ ในระดับเดียวกัน

ตัวอย่างลำดับชั้น

     Level 1
              Level 3
|---- A
|     |-- 1
|     |   |--- I
|     |
|     |-- 2
|         |--- III
|         |--- VI
|         |--- VII
|         |--- IX
|
|---- B
|     |-- 87
|         |--- AAA
|         |--- DDD
|
|---- C
      |-- ASDF
      |   |--- 11
      |   |--- 22
      |   |--- 33
      |
      |-- QWERTY
      |   |--- beft
      |
      |-- ROYGBP
          |--- Poi
          |--- Moi
          |--- Soy
          |--- Joy
          |--- Roy

ตัวอย่างชุดผลลัพธ์ที่อธิบายลำดับชั้นข้างต้น

Level 1    Level 2    Level 3
A          1          I
A          2          III
A          2          VI
A          2          VII
A          2          IX
B          87         AAA
B          87         DDD
C          ASDF       11
C          ASDF       22
C          ASDF       33
C          QWERTY     beft
C          ROYGBP     Poi
C          ROYGBP     Moi
C          ROYGBP     Soy
C          ROYGBP     Joy
C          ROYGBP     Roy

คำตอบ:


9

( หมายเหตุของ OP:วิธีแก้ปัญหาที่แนะนำคือบล็อกรหัส 4 / สุดท้าย)

XML ดูเหมือนว่าฉันจะเป็นตัวเลือกที่ชัดเจนของโครงสร้างข้อมูลที่จะใช้ที่นี่

with N as
(
  select T.N
  from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),
              (12),(13),(14),(15),(16),(17),(18),(19),(20)) as T(N)
)

select top(5 + abs(checksum(newid())) % 15)
  N1.N as '@Value',
  (
  select top(1 + abs(checksum(newid())) % 10)
    N2.N as '@Value',
    (
    select top(1 + abs(checksum(newid())) % 5)
      N3.N as '@Value'
    from N as N3
    where N2.N > 0
    for xml path('Level3'), type
    )
  from N as N2
  where N1.N > 0
  for xml path('Level2'), type
  )
from N as N1
for xml path('Level1'), root('Root');

เคล็ดลับในการทำให้ SQL Server ใช้ค่าที่แตกต่างกันtop()สำหรับแต่ละโหนดคือการทำให้แบบสอบถามย่อยสัมพันธ์กัน และN1.N > 0 N2.N > 0

แฟบ XML:

declare @X xml;

with N as
(
  select T.N
  from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),
              (12),(13),(14),(15),(16),(17),(18),(19),(20)) as T(N)
)
select @X  = (
             select top(5 + abs(checksum(newid())) % 15)
               N1.N as '@Value',
               (
               select top(1 + abs(checksum(newid())) % 10)
                 N2.N as '@Value',
                 (
                 select top(1 + abs(checksum(newid())) % 5)
                   N3.N as '@Value'
                 from N as N3
                 where N2.N > 0
                 for xml path('Level3'), type
                 )
               from N as N2
               where N1.N > 0
               for xml path('Level2'), type
               )
             from N as N1
             for xml path('Level1')
             );


select L1.X.value('@Value', 'varchar(10)')+'\'+
       L2.X.value('@Value', 'varchar(10)')+'\'+
       L3.X.value('@Value', 'varchar(10)')
from @X.nodes('/Level1') as L1(X)
  cross apply L1.X.nodes('Level2') as L2(X)
  cross apply L2.X.nodes('Level3') as L3(X);

และเวอร์ชันที่เป็นโมฆะของ XML ทั้งหมด

with N as
(
  select T.N
  from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),
              (12),(13),(14),(15),(16),(17),(18),(19),(20)) as T(N)
)
select cast(N1.N as varchar(10))+'\'+
       cast(N2.N as varchar(10))+'\'+
       cast(N3.N as varchar(10))
from (
     select top(5 + abs(checksum(newid())) % 15)
       N.N
     from N
     ) as N1
  cross apply
     (
     select top(1 + abs(checksum(newid())) % 10)
       N.N
     from N
     where N1.N > 0
     ) as N2
  cross apply
     (
     select top(1 + abs(checksum(newid())) % 5)
       N.N
     from N
     where N2.N > 0
     ) as N3;

ความสัมพันธ์N1.N > 0และ N2.N > 0ยังคงเป็นสิ่งสำคัญ

เวอร์ชันที่ใช้ตารางที่มี 20 ชื่อเพื่อใช้แทนจำนวนเต็ม

declare @Elements table
(
  Name nvarchar(50) not null
);

insert into @Elements(Name)
select top(20) C.name 
from sys.columns as C
group by C.name;

select N1.Name + N'\' + N2.Name + N'\' + N3.Name
from (
     select top(5 + abs(checksum(newid())) % 15)
       E.Name
     from @Elements as E
     ) as N1
  cross apply
     (
     select top(1 + abs(checksum(newid())) % 10)
       E.Name
     from @Elements as E
     where N1.Name > ''
     ) as N2
  cross apply
     (
     select top(1 + abs(checksum(newid())) % 5)
       E.Name
     from @Elements as E
     where N2.Name > ''
     ) as N3;

1
ฉันชอบเวอร์ชั่นใหม่ที่ดีกว่า มันก็เกือบจะเหมือนกันกับที่ฉันพยายามครั้งแรก แต่ด้วยเหตุผลบางอย่างฉันไม่TOP(n)สามารถทำงานได้อย่างถูกต้องใน 2 CROSS APPLYวินาที ไม่แน่ใจว่าสิ่งที่ฉันทำแตกต่าง / ไม่ถูกต้องตั้งแต่ฉันลบรหัสนั้นเมื่อฉันได้ทำงานอย่างอื่น ฉันจะโพสต์ในเร็ว ๆ นี้เมื่อคุณให้อัปเดตนี้แล้ว และฉันได้ล้างความคิดเห็นส่วนใหญ่ของฉันด้านบน
โซโลมอน Rutzky

ฉันเพิ่งโพสต์รุ่นของฉัน ความแตกต่างที่สำคัญคือ: 1)เนื่องจากฉันไม่สามารถรับ TOP (n) เพื่อทำงานฉันจึงได้รับnองค์ประกอบผ่านเงื่อนไข WHERE และ2)ฉันมีnameส่วนประกอบที่ควบคุมได้ดีกว่าไดเรกทอรีสุ่มและ / หรือชื่อไฟล์ .
โซโลมอน Rutzky

ขอโทษที่ต้องจากไปนานมาก แต่ฉันยุ่งมาก แต่ถึงกระนั้นฉันได้คิดเกี่ยวกับเรื่องนี้และไม่สามารถตัดสินใจระหว่างคำตอบและรุ่นที่ไม่ใช่ XML ของคุณ ฉันชอบความเรียบง่ายและความยืดหยุ่นของคุณ แต่ต้องการความสามารถในการคืนชื่อเพื่อใช้ในการสร้างโครงสร้างโฟลเดอร์ที่ฉันมี จากนั้นฉันก็รู้ว่าฉันได้อัพเดต Vlad ของเขาให้มีตารางการค้นหาและเข้าร่วมกับมันเพื่อให้ได้ผลลัพธ์ที่ดีที่สุด ดังนั้นหากไม่เหมาะสมที่จะถามคุณช่วยอัพเดทของคุณให้มีการค้นหาเดียวกันได้ไหม จากนั้นทั้ง 3 คำตอบจะให้ผลลัพธ์ที่เทียบเท่า (เหมาะสำหรับการเปรียบเทียบทั้ง 3) และฉันจะยอมรับของคุณ ไม่เป็นไร?
โซโลมอน Rutzky

1
@srutzky ฉันได้อัพเดตคำตอบแล้ว เมื่อไม่นานมานี้ดังนั้นหวังว่าฉันจะทำให้ถูกต้องและสิ่งที่คุณกำลังมองหา แน่นอนคุณสามารถเพิ่มคอลัมน์ระดับ@Elemetsเพื่อรับชุดชื่อที่แตกต่างกันสำหรับแต่ละระดับให้เลือก
Mikael Eriksson

1
@ srutzky ไม่ต้องกังวล ฉันดีใจที่คำตอบนี้มีประโยชน์สำหรับคุณ
Mikael Eriksson

6

นั่นเป็นเรื่องที่น่าสนใจ

เป้าหมายของฉันคือการสร้างจำนวนระดับที่กำหนดด้วยจำนวนแถวเด็กแบบสุ่มต่อแต่ละระดับในโครงสร้างลำดับชั้นที่เชื่อมโยงอย่างเหมาะสม เมื่อโครงสร้างนี้พร้อมจะเพิ่มข้อมูลพิเศษเช่นชื่อไฟล์และโฟลเดอร์

ดังนั้นฉันต้องการสร้างตารางคลาสสิกสำหรับจัดเก็บต้นไม้:

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);

โปรดทราบว่าแบบสอบถามมีความยืดหยุ่นค่อนข้างและพารามิเตอร์ทั้งหมดจะถูกแยกออกเป็นที่เดียว คุณสามารถเพิ่มระดับได้มากขึ้นหากต้องการเพียงเพิ่มพารามิเตอร์เพิ่มเติมอีกหนึ่งแถว

ChildRowCountที่จะทำให้การสร้างแบบไดนามิกเช่นผมไปได้ที่จะต้องจำตัวเลขสุ่มแถวสำหรับระดับถัดไปดังนั้นฉันมีคอลัมน์พิเศษ

การสร้างความเป็นเอกลักษณ์ IDsนั้นค่อนข้างยุ่งยาก ฉันกำหนดขีด จำกัด ขีด จำกัด ของแถวเด็ก 100 แถวต่อแถวพาเรนต์ 1 เพื่อรับประกันว่าIDsจะไม่ทำซ้ำ นี่คือสิ่งที่POWER(100, CTE.Lvl)เกี่ยวกับ เป็นผลให้มีช่องว่างขนาดใหญ่มาIDsมา ตัวเลขนั้นอาจเป็น a MAX(LvlMax)แต่ฉันใส่ค่าคงที่ 100 ลงในแบบสอบถามเพื่อความเรียบง่าย จำนวนของระดับที่ไม่ได้กำหนดค่าตายตัว @Intervalsแต่จะถูกกำหนดโดย

สูตรนี้

CAST(CRYPT_GEN_RANDOM(4) as int) / 4294967295.0 + 0.5

สร้างหมายเลขจุดลอยตัวแบบสุ่มในช่วง [0..1)ซึ่งจะถูกปรับสัดส่วนตามช่วงเวลาที่ต้องการ

ตรรกะการสืบค้นนั้นง่าย มันเกิดซ้ำ ขั้นตอนแรกสร้างชุดของแถวของระดับแรก จำนวนแถวถูกกำหนดโดยการสุ่มตัวเลขในTOPจำนวนแถวถูกกำหนดโดยจำนวนสุ่มChildRowCountนอกจากนี้สำหรับแต่ละแถวมีจำนวนสุ่มแยกต่างหากจากแถวเด็กเก็บไว้ใน

ส่วนที่เรียกซ้ำใช้CROSS APPLYในการสร้างจำนวนแถวที่กำหนดต่อแต่ละแถวพาเรนต์ ฉันต้องใช้WHERE Numbers.Number <= CTE.ChildRowCountแทนTOP(CTE.ChildRowCount)เพราะTOPไม่ได้รับอนุญาตในส่วนที่เรียกซ้ำของ CTE ไม่ทราบเกี่ยวกับข้อ จำกัด ของ SQL Server นี้มาก่อน

WHERE CTE.ChildRowCount IS NOT NULL หยุดการสอบถามซ้ำ

ซอ Fiddle

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ตารางควรจะมีแถวเพียงพอสำหรับแต่ละระดับ: 20 สำหรับระดับ 1, 10 สำหรับระดับ 2, 5 ระดับ 3; 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เข้าและFilePathFileName

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        |
+-------------+----------+----------+

วิธีการที่น่าสนใจ :) ฉันชอบมัน. เพื่อความสมบูรณ์คุณสามารถเพิ่มแบบสอบถามเพื่อเติมตารางตัวเลข (จาก SQL Fiddle) หรือเพียงรวมอินไลน์นั้นเป็นส่วนหนึ่งของ CTE ถ้างั้นใครก็สามารถคัดลอกและวางได้ง่ายกว่า สำหรับคำตอบนี้ผลลัพธ์สุดท้ายสามารถแสดงได้เนื่องจากแต่ละแถวเป็นเส้นทางที่สมบูรณ์จากระดับ 1 ถึงระดับ 3 สำหรับค่าระดับ 3 ทั้งหมดหรือไม่ ผมคิดว่าจะใช้เวลาเพียง 2 วินาทีในรอบสุดท้ายINNER JOIN SELECTสุดท้ายสามารถกำหนดชื่อ / เลเบลให้แต่ละโหนดเพื่อให้พวกเขาไม่ได้เป็นเพียงตัวเลข? ฉันจะอัปเดตคำถามเพื่อให้ความกระจ่างทั้งสองประเด็นนี้
โซโลมอน Rutzky

ชื่อ / ป้ายกำกับเหล่านี้มาจากไหน ฉันควรจะมีตาราง 'ชื่อ' ซึ่งมี 20 แถวและเลือกชื่อจากมันได้หรือไม่ ดังนั้นชื่อชุดเดียวกันจะปรากฏในแต่ละระดับ หรือแต่ละระดับควรมีชื่อชุดแยกต่างหากหรือไม่
Vladimir Baranov

ฉันคิดว่าชื่ออาจมาจากตาราง (temp, real หรือ variable) หรือ inline ซึ่งเป็นส่วนหนึ่งของ CTE ฉันเริ่มวางไว้ใน CTE แต่แล้วย้ายไปยังตาราง temp ท้องถิ่นเพื่อให้ส่วนหลักของแบบสอบถามสามารถอ่านได้ที่นี่ ฉันคิดว่าด้วยโครงสร้างที่คุณมีมันจะง่ายพอที่จะแยกจากกันในแต่ละระดับ แต่ถ้าเป็นเพียงชุดเดียว 20 ชุดที่จะพอเพียงก็ให้ความแปรปรวนน้อยลงในข้อมูลการทดสอบ ความต้องการที่แท้จริงเพียงอย่างเดียวคือไม่มีชื่อซ้ำภายในโหนดซึ่งจะเกิดข้อผิดพลาดเมื่อพยายามสร้างไดเรกทอรีหรือไฟล์ :)
โซโลมอน Rutzky

1
@srutzky ฉันเพิ่มตัวแปรที่สอง
Vladimir Baranov

1
@srutzky ผมแยกFullPathเข้าและFilePath FileName
Vladimir Baranov

4

ดังนั้นนี่คือสิ่งที่ฉันมาด้วย ด้วยเป้าหมายของการสร้างโครงสร้างไดเรกทอรีฉันจึงค้นหา "ชื่อ" ที่ใช้งานได้สำหรับไดเรกทอรีและไฟล์ เนื่องจากฉันไม่สามารถTOP(n)ทำงานได้ในCROSS APPLYs (ฉันคิดว่าฉันพยายามเชื่อมโยงข้อความค้นหาโดยใช้ค่าจากผู้ปกครองเป็นตัวnเลือกTOP(n)แต่ไม่ใช่การสุ่ม) ฉันจึงตัดสินใจสร้าง "ตัวเลข" ประเภทหนึ่ง ตารางที่จะอนุญาตให้มีINNER JOINหรือWHEREเงื่อนไขในการผลิตชุดขององค์ประกอบง่ายๆโดยการสุ่มตัวเลขและระบุว่ามันเป็นn WHERE table.Level = random_numberเคล็ดลับคือมีเพียง 1 แถวสำหรับระดับ 1, 2 แถวสำหรับระดับ 2, 3 แถวสำหรับระดับ 3 และอื่น ๆ ดังนั้นการใช้WHERE LevelID = 3จะได้รับฉัน 3 แถวและแต่ละแถวมีค่าที่ฉันสามารถใช้เป็นชื่อไดเรกทอรี

ติดตั้ง

เดิมส่วนนี้ถูกระบุไว้แบบอินไลน์เป็นส่วนหนึ่งของ CTE แต่เพื่อประโยชน์ในการอ่าน (เพื่อให้คุณไม่จำเป็นต้องเลื่อนดูINSERTคำสั่งมากมายเพื่อไปยังคิวรีของแบบสอบถามจริงสองสามบรรทัด) ฉันแยกมันออกเป็นตารางชั่วคราวในพื้นที่

IF (OBJECT_ID(N'tempdb..#Elements') IS NULL)
BEGIN
  PRINT 'Creating #Elements table...';
  CREATE TABLE #Elements (
     ElementLevel TINYINT NOT NULL,
     LevelName NVARCHAR(50) NOT NULL
                         );

  PRINT 'Populating #Elements table...';
  INSERT INTO #Elements (ElementLevel, LevelName)
    SELECT tmp.[Level], tmp.[Name]
    FROM (
                  SELECT 1,  N'Ella'
       UNION ALL  SELECT 2,  N'Itchy'
       UNION ALL  SELECT 2,  N'Scratchy'
       UNION ALL  SELECT 3,  N'Moe'
       UNION ALL  SELECT 3,  N'Larry'
       UNION ALL  SELECT 3,  N'Curly'
       UNION ALL  SELECT 4,  N'Ian'
       UNION ALL  SELECT 4,  N'Stephen'
       UNION ALL  SELECT 4,  N'Peter'
       UNION ALL  SELECT 4,  N'Bernard'
       UNION ALL  SELECT 5,  N'Michigan'
       UNION ALL  SELECT 5,  N'Erie'
       UNION ALL  SELECT 5,  N'Huron'
       UNION ALL  SELECT 5,  N'Ontario'
       UNION ALL  SELECT 5,  N'Superior'
       UNION ALL  SELECT 6,  N'White'
       UNION ALL  SELECT 6,  N'Orange'
       UNION ALL  SELECT 6,  N'Blonde'
       UNION ALL  SELECT 6,  N'Pink'
       UNION ALL  SELECT 6,  N'Blue'
       UNION ALL  SELECT 6,  N'Brown'
       UNION ALL  SELECT 7,  N'Asia'
       UNION ALL  SELECT 7,  N'Africa'
       UNION ALL  SELECT 7,  N'North America'
       UNION ALL  SELECT 7,  N'South America'
       UNION ALL  SELECT 7,  N'Antarctica'
       UNION ALL  SELECT 7,  N'Europe'
       UNION ALL  SELECT 7,  N'Australia'
       UNION ALL  SELECT 8,  N'AA'
       UNION ALL  SELECT 8,  N'BB'
       UNION ALL  SELECT 8,  N'CC'
       UNION ALL  SELECT 8,  N'DD'
       UNION ALL  SELECT 8,  N'EE'
       UNION ALL  SELECT 8,  N'FF'
       UNION ALL  SELECT 8,  N'GG'
       UNION ALL  SELECT 8,  N'HH'
       UNION ALL  SELECT 9,  N'I'
       UNION ALL  SELECT 9,  N'II'
       UNION ALL  SELECT 9,  N'III'
       UNION ALL  SELECT 9,  N'IV'
       UNION ALL  SELECT 9,  N'V'
       UNION ALL  SELECT 9,  N'VI'
       UNION ALL  SELECT 9,  N'VII'
       UNION ALL  SELECT 9,  N'VIII'
       UNION ALL  SELECT 9,  N'IX'
       UNION ALL  SELECT 10, N'Million'
       UNION ALL  SELECT 10, N'Billion'
       UNION ALL  SELECT 10, N'Trillion'
       UNION ALL  SELECT 10, N'Quadrillion'
       UNION ALL  SELECT 10, N'Quintillion'
       UNION ALL  SELECT 10, N'Sestillion'
       UNION ALL  SELECT 10, N'Sextillion'
       UNION ALL  SELECT 10, N'Octillion'
       UNION ALL  SELECT 10, N'Nonillion'
       UNION ALL  SELECT 10, N'Decillion'
     ) tmp([Level], [Name]);
END;

ข้อความค้นหาหลัก

สำหรับระดับ 1 ฉันเพิ่งคว้า[name]ค่าออกมาsys.objectsเนื่องจากมีแถวจำนวนมากอยู่เสมอ แต่ถ้าฉันต้องการควบคุมชื่อฉันสามารถขยาย#Elementsตารางเพื่อให้มีระดับเพิ่มเติม

;WITH topdir(Level1, Randy) AS
(
    SELECT TOP ( (CONVERT(INT, CRYPT_GEN_RANDOM(1)) % 20) + 5 ) so.[name],
                ( (CONVERT(INT, CRYPT_GEN_RANDOM(1)) % 10) + 1 )
    FROM sys.objects so
    ORDER BY CRYPT_GEN_RANDOM(8) ASC
)
SELECT  td.Level1, tmp1.Level2, tmp2.Level3
FROM    topdir td
CROSS APPLY (SELECT help.LevelName, (CONVERT(INT, CRYPT_GEN_RANDOM(1)) % 5) + 1
            FROM #Elements help
            WHERE help.ElementLevel = td.Randy
            ) tmp1 (Level2, Bandy)
CROSS APPLY (SELECT help.LevelName
            FROM #Elements help
            WHERE help.ElementLevel = tmp1.Bandy
            ) tmp2 (Level3);

ข้อความค้นหาได้รับการปรับเพื่อสร้างเส้นทางชื่อและเนื้อหาของแต่ละไฟล์

เพื่อสร้างพา ธ แบบเต็มสำหรับไฟล์และเนื้อหาไฟล์ฉันได้เลือก SELECT หลักของ CTE เพียงแค่ CTE อื่นและเพิ่ม SELECT หลักใหม่ที่ให้ผลลัพธ์ที่เหมาะสมที่เพียงแค่ต้องเข้าไปในไฟล์

DECLARE @Template NVARCHAR(4000);
SET @Template = N'<?xml version="1.0" encoding="ISO-8859-1"?>
<ns0:P4131 xmlns:ns0="http://switching/xi">
<R000000>
    <R00000010>R000000</R00000010>
    <R00000020>I</R00000020>
    <R00000030>{{Tag30}}</R00000030>
    <R00000040>{{Tag40}}</R00000040>
    <R00000050>{{Tag50}}</R00000050>
    <R00000060>2</R00000060>
</R000000>
</ns0:P4131>
';


;WITH topdir(Level1, Thing1) AS
(
    SELECT TOP ( (CONVERT(INT, CRYPT_GEN_RANDOM(1)) % 20) + 5 ) so.[name],
                ( (CONVERT(INT, CRYPT_GEN_RANDOM(1)) % 10) + 1 )
    FROM sys.objects so
    ORDER BY CRYPT_GEN_RANDOM(8) ASC
), main AS
(
   SELECT  td.Level1, tmp1.Level2, tmp2.Level3,
           td.Level1 + N'\' + tmp1.Level2 AS [FullPath],
           RIGHT('000' + CONVERT(VARCHAR(10),
                          (CONVERT(INT, CRYPT_GEN_RANDOM(2)) % 9999) + 1), 4) AS [R30],
           RIGHT('000' + CONVERT(VARCHAR(10),
                          (CONVERT(INT, CRYPT_GEN_RANDOM(2)) % 500) + 100), 4) AS [R50],
           ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS [RowNum]
   FROM    topdir td
   CROSS APPLY (SELECT help.LevelName, (CONVERT(INT, CRYPT_GEN_RANDOM(1)) % 5) + 1
                FROM #Elements help
                WHERE help.ElementLevel = td.Thing1
               ) tmp1 (Level2, Thing2)
   CROSS APPLY (SELECT help.LevelName
                FROM #Elements help
                WHERE help.ElementLevel = tmp1.Thing2
               ) tmp2 (Level3)
)
SELECT  mn.FullPath,
        mn.Level3 + N'.xml' AS [FileName],
        REPLACE(
            REPLACE(
                REPLACE(
                    @Template,
                    N'{{Tag30}}',
                    mn.R30),
                N'{{Tag40}}',
                mn.RowNum),
            N'{{Tag50}}',
            mn.R50) AS [Contents]
FROM    main mn;

สินเชื่อพิเศษ

ในขณะที่ไม่ได้เป็นส่วนหนึ่งของข้อกำหนดที่ระบุไว้ในคำถามเป้าหมาย (ซึ่งถูกกล่าวถึง) คือการสร้างไฟล์เพื่อทดสอบฟังก์ชั่นระบบไฟล์แบบเรียกซ้ำ ดังนั้นเราจะใช้ชุดชื่อพา ธ ชื่อไฟล์และเนื้อหาไฟล์และทำอะไรกับมันได้อย่างไร เราแค่ต้องการฟังก์ชั่น SQLCLR สองฟังก์ชั่นหนึ่งรายการเพื่อสร้างโฟลเดอร์และอีกหนึ่งฟังก์ชันเพื่อสร้างไฟล์

เพื่อให้ข้อมูลนี้ทำงานได้ฉันได้แก้ไข main SELECTของ CTE ที่แสดงด้านบนโดยตรงดังนี้:

SELECT  SQL#.File_CreateDirectory(
            N'C:\Stuff\TestXmlFiles\' + mn.FullPath) AS [CreateTheDirectory],
        SQL#.File_WriteFile(
            N'C:\Stuff\TestXmlFiles\' + mn.FullPath + N'\' + mn.Level3 + N'.xml',
            REPLACE(
                REPLACE(
                    REPLACE(
                        @Template,
                        N'{{Tag30}}',
                        mn.R30),
                    N'{{Tag40}}',
                    mn.RowNum),
                N'{{Tag50}}',
                mn.R50), -- @FileData
            0, -- @AppendData
            '' -- @FileEncoding
                            ) AS [WriteTheFile]
FROM    main mn;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.