2

次のようなレコードを含むExcelLinksという列を持つテーブルがあります。

= INDEX('\\ san1 \ engData [BT_500.0_Structural_Position.xls] Concrete'!$ B $ 4:$ IK $ 83、MATCH($ K $ 9、'\\ san1 \ engData [BT_500.0_Structural_Position.xls] Concrete'!$ A $ 4:$ A $ 83,0)、MATCH(C212、'\\ san1 \ engData [BT_500.0_Structural_Position.xls] Concrete'!$ B $ 3:$ IK $ 3,0))/ 1000000

= INDEX('\\ san1 \ engData [GK_600.0_Pumps.xls] Pumps'!$ B $ 4:$ BD $ 39、MATCH($ K $ 9、'\\ san1 \ engData [TT_640.0_Generator.xls] Generator'!$ A $ 4:$ A $ 39,0)、MATCH(C214、'\\ san1 \ engData [GK_600.0_Pumps.xls] Pumps'!$ B $ 3:$ BD $ 3,0))/ 1000000

= INDEX('\\ san1 \ engData [TT_640.0_Generator.xls] Generator'!$ B $ 4:$ HU $ 83、MATCH($ K $ 9、'\\ san1 \ engData [GK_600.0_Pumps.xls] Pumps'!$ A $ 4:$ A $ 83,0)、MATCH(C218、'\\ san1 \ engData [TT_640.0_Generator.xls] Generator'!$ B $ 3:$ HU $ 3,0))/ 1000000

理想的な出力は次のとおりです。

_______________________________________
| Row  |  LinkCount |  UniqueLinkCount |
| 1    |     3      |        1         |
| 2    |     3      |        2         |
| 3    |     3      |        2         |

このデータをクエリして、レコードごとに使用されているファイルと一意のファイルの数を確認したいと思います。

オンラインで検索しましたが、これを行うものは見つかりませんでした。

カーソルを作成し、レコードごとに、ファイルの数で始まる文字\\と終わる文字を検出し'!$て、ファイルの数を数えることを考えています。

難しいのは、複数のインターリンク(異なるファイルである可能性があります)を使用する関数=INDEXと関数を備えたExcelLinksです。MATCH

このテーブルには1200万を超えるレコードがあるため、カーソルを使用したパフォーマンスが心配です。

正規表現を使用してOracleでこれを行うためのより良い方法がいくつかあります。SQL ServerにはRegExがないことを知っており、それが最も簡単なオプションである場合は、CLRストアドプロシージャを作成/使用する用意があります。

4

1 に答える 1

3

まず、AdamMachanicからこの文字列分割CLR関数を取得します。コードをDLLにコンパイルし(Visual Studioがない場合はcscを使用)、DLLをサーバーにコピーしてから、次のようにDLLを登録します(ファイルパスなど、ここでいくつかの可変部分を置き換える必要があります) 、アセンブリと呼びたいものなど):

CREATE ASSEMBLY CLRStuff 
  FROM 'C:\DLLs\CLRStuff.dll'  
  WITH PERMISSION_SET = SAFE;
GO

CREATE FUNCTION dbo.SplitStrings
(
   @List      NVARCHAR(MAX),
   @Delimiter NVARCHAR(255)
)
RETURNS TABLE ( Item NVARCHAR(4000) )
  EXTERNAL NAME CLRStuff.UserDefinedFunctions.SplitString_Multi;
GO

これがあれば、クエリ自体は非常に簡単です。いくつかの行を保持する単純なテーブル変数を作成しましょう(簡潔にするためにパスを短縮しました)。

DECLARE @x TABLE(i INT, ExcelLink VARCHAR(MAX));

INSERT @x

    -- 3 files, 1 unique: 
    SELECT 1,'=INDEX(''\\san1\a.xls''!$B$4:$IK$83,MATCH($K$9,''\\san1\a.xls'
    + '''!$A$4:$A$83,0),MATCH(C212,''\\san1\a.xls''!$B$3:$IK$3,0))/1000000'

UNION ALL 

    -- 3 files, 3 unique:
    SELECT 2,'=INDEX(''\\san1\a.xls''!$B$4:$BD$39,MATCH($K$9,''\\san1\b.xls'
    + '''!$A$4:$A$39,0),MATCH(C214,''\\san1\c.xls''!$B$3:$BD$3,0))/1000000'

UNION ALL 

    -- 3 files, 2 unique:
    SELECT 3,'=INDEX(''\\san1\b.xls''!$B$4:$HU$83,MATCH($K$9,''\\san1\c.xls'
    + '''!$A$4:$A$83,0),MATCH(C218,''\\san1\c.xls''!$B$3:$HU$3,0))/1000000'

UNION ALL 

    -- 1 file, 1 unique:
    SELECT 4,'=INDEX(''\\san1\foo.xls''!$B$4:$HU$83,0)';

-- the above was just inserts; the remainder is all of the query:

;WITH x(i,part) AS 
(
  SELECT x.i, SUBSTRING(t.Item, CHARINDEX('''\\', t.Item), 2048) 
    FROM @x AS x CROSS APPLY dbo.SplitStrings(x.ExcelLink, '!$') AS t
)
SELECT i, [file_count] = COUNT(part), [unique_files] = COUNT(DISTINCT part)
  FROM x WHERE part LIKE '''\\%'
  GROUP BY i ORDER BY i;

結果:

i   file_count  unique_files
--  ----------  ------------
1   3           1
2   3           3
3   3           2
4   1           1

\\これは、ファイルパスの先頭として以外はデータに自然に表示されないこと、およびすべてのファイルパスがネットワーク共有上に存在することに依存しています。

これはおそらくあなたが得ることができる最も効率的ではありません-いくつかの正規表現ウィザードは分割の代わりにそのアプローチを使用してこれを改善できると確信しています(ここにあなたが始めるための良い記事があります)が、それは私の得意ではありません。コストの大部分は、カウントや交換ではなく、テーブル全体をスキャンするために必要なI/Oになります。

CLRを使用できない場合は、その関数を任意の数の非CLRバージョンに置き換えることができます(これは機能的に適切な置き換えとなる例です)が、他のアプローチではパフォーマンスが最適化されない可能性があることに注意してください。

于 2012-07-04T01:37:30.650 に答える