Tsql 函数,用于解析文件夹路径并返回表

Tsql 函数,用于解析文件夹路径并返回表,tsql,Tsql,如何创建解析文件夹路径并返回表的函数 分隔符将是反斜杠\。输入将是文件夹路径FolderA\FolderB 输出将是一个按顺序包含文件夹的表: FolderName: FolderA FolderB Level: 0 1 这个问题可以推广到字符串拆分问题。T-SQL中的字符串操作是可能的,但很难理解,因为复杂表达式的语法很难理解 伊兹克·本·甘在接受博客采访时提供了解决这个问题所需的所有技巧 字符串拆分函数 Itzik提供了一个用于拆分字符串的内联表值函数,经过几

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

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

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

FolderName: FolderA  FolderB    
Level:      0      1

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

伊兹克·本·甘在接受博客采访时提供了解决这个问题所需的所有技巧

字符串拆分函数 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
SELECT * FROM dbo.fn_nums(10);  
n
---
1
2
3
4
5
6
7
8
9
10
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;
POS ELEMENT
1   10248
2   10249
3   10250
FOLDERNAME  LEVEL
FolderA 0
FolderB 1
辅助数表 字符串拆分函数依赖于辅助数字表。您的数据库中可能已经有其中一个,因为它们对于解决各种问题都很有用

如果没有,您可以调整另一个inline表值函数,该函数是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_split('10248,10249,10250'); 
pos element
---- --------
1 10248
2 10249
3 10250
SELECT * FROM dbo.fn_nums(10);  
n
---
1
2
3
4
5
6
7
8
9
10
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;
POS ELEMENT
1   10248
2   10249
3   10250
FOLDERNAME  LEVEL
FolderA 0
FolderB 1
一个通用的自包含字符串拆分函数 通过组合字符串拆分器和行生成器,可以使函数自包含,这意味着它独立于数据库中的其他对象工作

通过添加一个指定分隔符字符的额外参数,可以使字符串拆分器成为通用的,因为它将拆分由任何字符(而不仅仅是逗号)分隔的字符串

您可以使用如下修改版本替换伊兹克的字符串拆分器:

SELECT * FROM dbo.fn_split('10248,10249,10250'); 
pos element
---- --------
1 10248
2 10249
3 10250
SELECT * FROM dbo.fn_nums(10);  
n
---
1
2
3
4
5
6
7
8
9
10
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;
POS ELEMENT
1   10248
2   10249
3   10250
FOLDERNAME  LEVEL
FolderA 0
FolderB 1
它生成如下结果集:

SELECT * FROM dbo.fn_split('10248,10249,10250'); 
pos element
---- --------
1 10248
2 10249
3 10250
SELECT * FROM dbo.fn_nums(10);  
n
---
1
2
3
4
5
6
7
8
9
10
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;
POS ELEMENT
1   10248
2   10249
3   10250
FOLDERNAME  LEVEL
FolderA 0
FolderB 1
你问题的解决方案 现在我们有了一个可以为任何分隔的元素字符串生成表的函数,我们有了一个解决问题的方法

使用通用自包含字符串拆分器后,可以使用以下查询:

SELECT
  Element AS FolderName,
  Pos - 1 AS Level
FROM dbo.fn_split('FolderA\FolderB', '\');
它生成如下结果集:

SELECT * FROM dbo.fn_split('10248,10249,10250'); 
pos element
---- --------
1 10248
2 10249
3 10250
SELECT * FROM dbo.fn_nums(10);  
n
---
1
2
3
4
5
6
7
8
9
10
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;
POS ELEMENT
1   10248
2   10249
3   10250
FOLDERNAME  LEVEL
FolderA 0
FolderB 1
SQL Server使用基于1的索引进行字符串操作和序列(如由ROW_NUMBER函数生成的序列),因此通用字符串拆分器应遵循此约定是有意义的

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


您可以在上使用此解决方案。

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

伊兹克·本·甘在接受博客采访时提供了解决这个问题所需的所有技巧

字符串拆分函数 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
SELECT * FROM dbo.fn_nums(10);  
n
---
1
2
3
4
5
6
7
8
9
10
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;
POS ELEMENT
1   10248
2   10249
3   10250
FOLDERNAME  LEVEL
FolderA 0
FolderB 1
辅助数表 字符串拆分函数依赖于辅助数字表。您的数据库中可能已经有其中一个,因为它们对于解决各种问题都很有用

如果没有,您可以调整另一个inline表值函数,该函数是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_split('10248,10249,10250'); 
pos element
---- --------
1 10248
2 10249
3 10250
SELECT * FROM dbo.fn_nums(10);  
n
---
1
2
3
4
5
6
7
8
9
10
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;
POS ELEMENT
1   10248
2   10249
3   10250
FOLDERNAME  LEVEL
FolderA 0
FolderB 1
一个通用的自包含字符串拆分函数 通过组合字符串拆分器和行生成器,可以使函数自包含,这意味着它独立于数据库中的其他对象工作

通过添加一个指定分隔符字符的额外参数,可以使字符串拆分器成为通用的,因为它将拆分由任何字符(而不仅仅是逗号)分隔的字符串

您可以使用如下修改版本替换伊兹克的字符串拆分器:

SELECT * FROM dbo.fn_split('10248,10249,10250'); 
pos element
---- --------
1 10248
2 10249
3 10250
SELECT * FROM dbo.fn_nums(10);  
n
---
1
2
3
4
5
6
7
8
9
10
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;
POS ELEMENT
1   10248
2   10249
3   10250
FOLDERNAME  LEVEL
FolderA 0
FolderB 1
它生成如下结果集:

SELECT * FROM dbo.fn_split('10248,10249,10250'); 
pos element
---- --------
1 10248
2 10249
3 10250
SELECT * FROM dbo.fn_nums(10);  
n
---
1
2
3
4
5
6
7
8
9
10
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;
POS ELEMENT
1   10248
2   10249
3   10250
FOLDERNAME  LEVEL
FolderA 0
FolderB 1
你问题的解决方案 现在我们有了一个可以为任何分隔的元素字符串生成表的函数,我们有了一个解决问题的方法

使用通用自包含字符串拆分器后,可以使用以下查询:

SELECT
  Element AS FolderName,
  Pos - 1 AS Level
FROM dbo.fn_split('FolderA\FolderB', '\');
它生成如下结果集:

SELECT * FROM dbo.fn_split('10248,10249,10250'); 
pos element
---- --------
1 10248
2 10249
3 10250
SELECT * FROM dbo.fn_nums(10);  
n
---
1
2
3
4
5
6
7
8
9
10
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;
POS ELEMENT
1   10248
2   10249
3   10250
FOLDERNAME  LEVEL
FolderA 0
FolderB 1
SQL Server使用基于1的索引进行字符串操作和序列(如由ROW_NUMBER函数生成的序列),因此通用字符串拆分器应遵循此约定是有意义的

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


您可以在上安装此解决方案。

欢迎使用堆栈溢出!我们鼓励你这样做。如果您有,请将其添加到问题中-如果没有,请先研究并尝试您的问题,然后再回来。欢迎使用堆栈溢出!我们鼓励你这样做。如果您有,请将其添加到问题中-如果没有,请先研究并尝试您的问题,然后再回来。