Иногда возникает необходимость сортировки иерархического списка в строковых полях ранее уже созданной таблицы. Это очень просто сделать с помощью конвейера CTE в SQL.
DECLARE @t TABLE (txt varchar(50))
INSERT INTO @t (txt) VALUES ('1_____')
INSERT INTO @t (txt) VALUES ('1.2. _____')
INSERT INTO @t (txt) VALUES ('1.2.1._____')
INSERT INTO @t (txt) VALUES ('1.2.10._____')
INSERT INTO @t (txt) VALUES ('1.2.7._____')
INSERT INTO @t (txt) VALUES ('2. _____')
INSERT INTO @t (txt) VALUES ('2.2 _____')
INSERT INTO @t (txt) VALUES ('2.2.10._____')
INSERT INTO @t (txt) VALUES ('2.2.3.1._____')
INSERT INTO @t (txt) VALUES ('22_____')
;WITH points (id, p1, p2, p3, p4, p5, p6, p7, p8, p9, it) AS
( SELECT
id = RTRIM(LEFT(t.txt, PATINDEX('%[^.0-9]%', t.txt) - 1)),
p1 = CONVERT(int, NULL),
p2 = CONVERT(int, NULL),
p3 = CONVERT(int, NULL),
p4 = CONVERT(int, NULL),
p5 = CONVERT(int, NULL),
p6 = CONVERT(int, NULL),
p7 = CONVERT(int, NULL),
p8 = CONVERT(int, NULL),
p9 = CONVERT(int, NULL),
it = 0
FROM @t t
UNION ALL
SELECT
id = p.id,
p1 = NULLIF(CHARINDEX('.', p.id, 1), 0),
p2 = NULLIF(CHARINDEX('.', p.id, 1 + p.p1), 0),
p3 = NULLIF(CHARINDEX('.', p.id, 1 + p.p2), 0),
p4 = NULLIF(CHARINDEX('.', p.id, 1 + p.p3), 0),
p5 = NULLIF(CHARINDEX('.', p.id, 1 + p.p4), 0),
p6 = NULLIF(CHARINDEX('.', p.id, 1 + p.p5), 0),
p7 = NULLIF(CHARINDEX('.', p.id, 1 + p.p6), 0),
p8 = NULLIF(CHARINDEX('.', p.id, 1 + p.p7), 0),
p9 = NULLIF(CHARINDEX('.', p.id, 1 + p.p8), 0),
it = p.it + 1
FROM points p
WHERE p.it < 10
),
levels (id, v1, v2, v3, v4, v5, v6, v7, v8, v9) AS
( SELECT
id = p.id,
v1 = CONVERT(int, SUBSTRING(id, 1, ISNULL(p.p1 - 1, LEN(p.id) ))),
v2 = CONVERT(int, SUBSTRING(id, p.p1 + 1, ISNULL(p.p2 - p.p1 - 1, LEN(p.id) - p.p1))),
v3 = CONVERT(int, SUBSTRING(id, p.p2 + 1, ISNULL(p.p3 - p.p2 - 1, LEN(p.id) - p.p2))),
v4 = CONVERT(int, SUBSTRING(id, p.p3 + 1, ISNULL(p.p4 - p.p3 - 1, LEN(p.id) - p.p3))),
v5 = CONVERT(int, SUBSTRING(id, p.p4 + 1, ISNULL(p.p5 - p.p4 - 1, LEN(p.id) - p.p4))),
v6 = CONVERT(int, SUBSTRING(id, p.p5 + 1, ISNULL(p.p6 - p.p5 - 1, LEN(p.id) - p.p5))),
v7 = CONVERT(int, SUBSTRING(id, p.p6 + 1, ISNULL(p.p7 - p.p6 - 1, LEN(p.id) - p.p6))),
v8 = CONVERT(int, SUBSTRING(id, p.p7 + 1, ISNULL(p.p8 - p.p7 - 1, LEN(p.id) - p.p7))),
v9 = CONVERT(int, SUBSTRING(id, p.p8 + 1, ISNULL(p.p9 - p.p8 - 1, LEN(p.id) - p.p8)))
FROM points p
WHERE p.it = 10
)
SELECT
num = CASE RIGHT(v.id, 1)
WHEN '.' THEN v.id
ELSE v.id + '.'
END,
txt = LTRIM(RIGHT(t.txt, LEN(t.txt) - LEN(v.id)))
FROM @t t
INNER JOIN levels v
ON v.id = RTRIM(LEFT(t.txt, PATINDEX('%[^.0-9]%', t.txt) - 1))
ORDER BY
ISNULL(v.v1, 0),
ISNULL(v.v2, 0),
ISNULL(v.v3, 0),
ISNULL(v.v4, 0),
ISNULL(v.v5, 0),
ISNULL(v.v6, 0),
ISNULL(v.v7, 0),
ISNULL(v.v8, 0),
ISNULL(v.v9, 0)
Комментарии
Отправить комментарий