计算字符串中嵌入的总文件名和唯一文件名

时间:2012-07-04 01:22:11

标签: sql sql-server regex sql-server-2008 sqlclr

我有一个名为ExcelLinks的列的表,其中包含如下记录:

  

= INDEX(\\ SAN1 \ engData [BT_500.0_Structural_Position.xls]混凝土'$ B $ 4:!$ IK 83 $,MATCH($ $ķ9'\\ SAN1 \ engData [BT_500.0_Structural_Position.xls]混凝土'$ A $ 4:!$ A $ 83,0),MATCH(C212,\\ SAN1 \ engData [BT_500.0_Structural_Position.xls]混凝土'$ B $ 3:!$ $ IK 3,0))/ 1000000

     

= INDEX(\\ SAN1 \ engData [GK_600.0_Pumps.xls]泵'$ B $ 4:!$ $ BD 39 MATCH($ $ķ9'\\ SAN1 \ engData [TT_640.0_Generator.xls]发生器'$ A $ 4:!$ A $ 39,0),MATCH(C214,\\ SAN1 \ engData [GK_600.0_Pumps.xls]泵$ B $ 3:!$ $ BD 3,0))/ 1000000

     

= INDEX(\\ SAN1 \ engData [TT_640.0_Generator.xls]发生器'$ B $ 4:!$ HU 83 $,MATCH($ $ķ9'\\ SAN1 \ engData [GK_600.0_Pumps.xls]泵'$ A $ 4:!$ A $ 83,0),MATCH(C218,\\ SAN1 \ engData [TT_640.0_Generator.xls]发生器'$ B $ 3:!$ $ HU 3,0))/ 1000000

理想的输出是:

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

我想查询此数据并查看每条记录使用的文件数和唯一文件数。

我在网上搜索过,找不到任何可以做到这一点的内容。

我想我会制作一个光标,对于每个记录,我会检测以\\开头并以'!$结尾的字符并计算文件数。

硬件位是ExcelLinks,=INDEXMATCH函数使用多个互连链接(可能是不同的文件)。

此表中有超过1200万条记录,因此我担心使用游标的性能。

使用Oracle using RegEx's有一些更好的方法可以做到这一点。我知道SQL Server没有RegEx,如果这是最简单的选择,我愿意编写/使用CLR存储过程。

1 个答案:

答案 0 :(得分:3)

首先,grab this string splitting CLR function from Adam Machanic。将代码编译成DLL(using csc,如果您没有Visual Studio),将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

这依赖于\\除了作为文件路径的开头之外不会自然地出现在数据中,并且所有文件路径都驻留在网络共享上。

这可能不是你能获得的最有效率 - 我确信一些RegEx向导可以使用这种方法改进这个而不是分裂(here is a good article to get you started),但这不是我的强项。大部分成本将是扫描整个表所需的I / O,而不是计数或替换。

如果您不能使用CLR,则可以将该函数替换为任意数量的非CLR版本(here is an example that would be a functionally suitable replacement),但请记住,其他方法可能会受到性能不佳的影响。