0

フォルダ パスを解析してテーブルを返す関数を作成する方法は?

区切り文字はバックスラッシュになります\。入力はフォルダー パスになります FolderA\FolderB

出力は、フォルダーが順番に並んだテーブルになります。

FolderName: FolderA  FolderB    
Level:      0      1
4

1 に答える 1

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 の文字列のスプリッターを変更したバージョンに置き換えることができます。

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 は、文字列操作と ROW_NUMBER 関数によって生成されるシーケンスなどに 1 から始まるインデックスを使用するため、一般的な文字列スプリッターがこの規則に従う必要があるのは理にかなっています。

パスの最初のフォルダーをレベル 0 にする必要があるため、クエリは要素の位置から 1 を引いて、0 から始まるインデックスを取得します。

SQL Fiddleでこのソリューションを試すことができます。

于 2012-09-30T12:48:15.323 に答える