解析文件夹路径并返回表的FUNCTION

时间:2012-09-30 11:40:26

标签: tsql

如何创建一个解析文件夹路径并返回表的函数?

分隔符将是反斜杠\。输入将是文件夹路径FolderA\FolderB

输出将是一个按顺序包含文件夹的表:

FolderName: FolderA  FolderB    
Level:      0      1

1 个答案:

答案 0 :(得分:4)

此问题可以推广到字符串拆分问题。由于复杂表达式的语法难以理解,T-SQL中的字符串操作是可能的,但很难理解。

Itzik Ben-Gan在他给sqlservercode博客的采访中提供了解决这个问题所需的所有技巧。

字符串拆分功能

Itzik提供了一个用于分割字符串的内联表值函数,通过一些修改,可以解决您的问题:

CREATE FUNCTION dbo.fn_split(@arr AS VARCHAR(MAX)) RETURNS TABLE
AS
RETURN
  SELECT
    n - LEN(REPLACE(LEFT(@arr, n), ',', '')) + 1 AS pos,
    SUBSTRING(@arr, n,
      CHARINDEX(',', @arr + ',', n) - n) AS element
  FROM dbo.Nums
  WHERE n <= LEN(@arr) AND SUBSTRING(',' + @arr, n, 1) = ',';  

此函数允许您将逗号分隔的元素字符串拆分为位置索引表。像这样的查询:

SELECT * FROM dbo.fn_split('10248,10249,10250'); 

生成如下结果集:

pos element
---- --------
1 10248
2 10249
3 10250

辅助数字表

字符串拆分功能依赖于辅助数字表。您可能已经在数据库中拥有其中一个,因为它们可用于解决各种问题。

如果您没有,那么您可以调整Itzik建议的另一个内联表值函数,以提高其效率,如果不是它的清晰度:

CREATE FUNCTION dbo.fn_nums(@n AS BIGINT) RETURNS TABLE
AS
RETURN
  WITH
  L0   AS(SELECT 1 AS c UNION ALL SELECT 1),
  L1   AS(SELECT 1 AS c FROM L0 AS A, L0 AS B),
  L2   AS(SELECT 1 AS c FROM L1 AS A, L1 AS B),
  L3   AS(SELECT 1 AS c FROM L2 AS A, L2 AS B),
  L4   AS(SELECT 1 AS c FROM L3 AS A, L3 AS B),
  L5   AS(SELECT 1 AS c FROM L4 AS A, L4 AS B),
  Nums AS(SELECT ROW_NUMBER() OVER(ORDER BY c) AS n FROM L5)
  SELECT n FROM Nums 
  WHERE n <= @n;  

此函数允许您生成具有指定行数的表。像这样的查询:

SELECT * FROM dbo.fn_nums(10);  

生成如下结果集:

n
---
1
2
3
4
5
6
7
8
9
10

通用的自包含字符串分割函数

通过组合字符串拆分器和行生成器,您可以使该函数自包含,我的意思是它独立于数据库中的其他对象而工作。

通过添加一个指定分隔符字符的额外参数,可以使字符串分割器具有通用性,因为它会分割由任何字符分隔的字符串,而不仅仅是逗号。

你可以用这样的修改版本替换Itzik的string'splitter:

CREATE FUNCTION dbo.fn_split(@arr AS VARCHAR(MAX), @delim AS CHAR(1)) RETURNS TABLE
AS
RETURN
  WITH
  L0   AS(SELECT 1 AS c UNION ALL SELECT 1),
  L1   AS(SELECT 1 AS c FROM L0 AS A, L0 AS B),
  L2   AS(SELECT 1 AS c FROM L1 AS A, L1 AS B),
  L3   AS(SELECT 1 AS c FROM L2 AS A, L2 AS B),
  L4   AS(SELECT 1 AS c FROM L3 AS A, L3 AS B),
  L5   AS(SELECT 1 AS c FROM L4 AS A, L4 AS B),
  Nums AS(SELECT ROW_NUMBER() OVER(ORDER BY c) AS n FROM L5)
  SELECT
    n - LEN(REPLACE(LEFT(@arr, n), @delim, '')) + 1 AS pos,
    SUBSTRING(@arr, n,
      CHARINDEX(@delim, @arr + @delim, n) - n) AS element
  FROM Nums
  WHERE n <= LEN(@arr) AND SUBSTRING(@delim + @arr, n, 1) = @delim;

您可以通过传递与之前相同的参数并复制逗号作为分隔符来复制原始函数的输出:

SELECT * FROM dbo.fn_split('10248,10249,10250', ',');

它生成如下结果集:

POS ELEMENT
1   10248
2   10249
3   10250

解决问题的方法

既然我们有一个可以为任何分隔的元素字符串生成表格的函数,我们就可以解决您的问题。

使用通用的自包含字符串拆分器,您可以使用此查询:

SELECT
  Element AS FolderName,
  Pos - 1 AS Level
FROM dbo.fn_split('FolderA\FolderB', '\');

它生成如下结果集:

FOLDERNAME  LEVEL
FolderA 0
FolderB 1

SQL Server使用基于1的索引进行字符串操作以及序列(例如由ROW_NUMBER函数生成的序列),因此通用字符串拆分器应遵循此约定。

因为您希望路径中的第一个文件夹为0级,所以查询会从元素位置中减去1以获得基于0的索引。

您可以在SQL Fiddle上使用此解决方案进行实践。