2

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

=INDEX('\\san1\engData[BT_500.0_Structural_Position.xls]混凝土'!$B$4:$IK$83,MATCH($K$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]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         |

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

我在网上进行了搜索,找不到任何这样做的东西。

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

困难的一点是 ExcelLinks=INDEXMATCH使用多个链接(可能是不同的文件)的函数。

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

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

4

1 回答 1

3

首先,从 Adam Machanic 中获取这个字符串拆分 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

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

这可能不是你能得到的最有效的方法——我相信一些 RegEx 向导可以使用这种方法而不是拆分来改进这一点(这是一篇很好的文章,可以帮助你入门),但这不是我的强项。大部分成本将是扫描整个表所需的 I/O,而不是计数或替换。

如果您不能使用 CLR,则可以将该函数替换为任意数量的非 CLR 版本(这里是一个在功能上适合替换的示例),但请记住,其他方法可能会受到不太理想的性能的影响。

于 2012-07-04T01:37:30.650 回答