解析这 50 万行的最有效方法是什么?

What's the most efficient way to parse these 500k rows?

我目前有一个充满 ACL 条目的数据库,如下所示:

我需要检查并解析根节点(如 \\chrlcltsvr02\AYY_LMO\ClientServices)和它的 child 节点(例如 \\chrlcltsvr02\AYY_LMO\ClientServices\Client1)之间的区别。

我曾尝试使用 ORM 和原始 T-SQL 在 C# 代码中执行此操作(事实上我知道每行打开一个会话是一个可怕的想法):

foreach (string path in distinctPaths)
{
    using (session = sessionFactory.OpenSession())
    {
        string query;

        query = String.Format("SELECT DISTINCT UserGroup, AllowDeny, Permissions FROM FilerACLs WHERE FullPath LIKE '{0}'", path.Replace("'", "''"));

        var parentACLs = session.CreateSQLQuery(query).SetResultTransformer(Transformers.AliasToBean<ShareACLEntry>()).List<ShareACLEntry>();

        query = String.Format("SELECT DISTINCT UserGroup, AllowDeny, Permissions FROM FilerACLs WHERE FullPath LIKE '{0}\%'", path.Replace("'", "''"));

        var childACLs = session.CreateSQLQuery(query).SetResultTransformer(Transformers.AliasToBean<ShareACLEntry>()).List<ShareACLEntry>();

        if (childACLs.Except(parentACLs, comparer).ToList().Count > 0)
            Console.WriteLine("{0} has diffs!", path);
    }
}

最后比较结果数据,看看 child 节点是否与根节点不同。

不同的是,我的意思是如果我有一个组 "CLT-AD\Full Access Shared-CHRL" 的 ACL,允许完全控制 parent 节点而不是 child 节点,我想请注意,ACL 存在于 child 而不是 parent。

不幸的是,这个过程太慢了,无法在任何合适的时间内解析 500k 行。

我想知道是否有人有想法来有效地确定数据中是否存在差异——直接使用 T-SQL、SQL CLR 函数或更好的方法一般算法。

如果需要澄清,请告诉我。

谢谢!

编辑

因为我对这个问题有相当多的仇恨,让我 re-clarify 正是我正在寻找的减去我上面概述的失败方法。

我最近对 ​​Windows 服务器上的约 1,000 个共享文件夹进行了扫描。此扫描从顶级目录一直递归到文件夹层次结构,并且为每个文件夹为每个 ACL 记录一行。

因此数据库看起来像上面的屏幕截图。

我需要做的是从该数据库中提取一份报告,详细说明从顶级目录记录的 ACL 与为该顶级目录下的任何目录记录的 ACL 之间的差异(甚至是否存在差异) .

希望这更有意义。

这是一些 TSQL,

DECLARE @parentFullPath NVARCHAR(260) = N'\chrlcltsvr02\AYY_LMO\ClientServices';
DECLARE @childFullPath NVARCHAR(260) = N'\chrlcltsvr02\AYY_LMO\ClientServices\Client1';

SELECT
            [UserGroup],
            [AllowDeny],
            [Permissions]
    FROM
            [ACLs]
    WHERE
            [FullPath] = @childFullPath
EXCEPT
SELECT
            [UserGroup],
            [AllowDeny],
            [Permissions]
    FROM
            [ACLs]
    WHERE
            [FullPath] = @parentFullPath;

它可能会或可能不会满足您的要求,这很难说。


要找到所有的父子对,

WITH [paths] AS (
SELECT
             [FullPath]
    FROM
             [ACLs]
    GROUP BY
             [FullPath])
SELECT
            [P].[FullPath] [ParentFullPath],
            [C].[FullPath] [ChildFullPath]
    FROM
            [paths] [P]
        JOIN
            [paths] [C]
                ON
                        [C].[FullPath] <> [P].[FullPath]
                    AND
                        CHARINDEX([P].[FullPath], [C].[FullPath]) = 1;

所以您实际上可以一次完成所有操作,就像这样。

WITH [paths] AS (
SELECT
             [FullPath]
    FROM
             [ACLs]
    GROUP BY
             [FullPath])
SELECT
            [PC].[ParentFullPath],
            [PC].[ChildFullPath],
            [D].[UserGroup],
            [D].[AllowDeny],
            [D].[Permissions]
    FROM (
            SELECT
                        [P].[FullPath] [ParentFullPath],
                        [C].[FullPath] [ChildFullPath]
                FROM
                        [paths] [P]
                    JOIN
                        [paths] [C]
                            ON
                                    [C].[FullPath] <> [P].[FullPath]
                                AND
                                    CHARINDEX([P].[FullPath], [C].[FullPath]) = 1;
        ) [PC]
CROSS APPLY
(
SELECT
            [UserGroup],
            [AllowDeny],
            [Permissions]
    FROM
            [ACLs]
    WHERE
            [FullPath] = [PC].[ChildFullPath]
EXCEPT
SELECT
            [UserGroup],
            [AllowDeny],
            [Permissions]
    FROM
            [ACLs]
    WHERE
            [FullPath] = [PC].[ParentFullPath]
) [D];

最终,如果您希望这段代码 运行 有效,您需要对您的模式进行一些规范化。只要父子关系仅通过字符串比较推断存在,这将是一个相对较慢的操作。

如果你想一次完成整个列表,你可以写一个 SQL 表达式(例如使用 substring())到 get_parent_path_from_child_path 和 运行 下面SQL 结构。从您的问题中不清楚在一般情况下如何将 parent 与 child 分开。所以我只是给你一个线框代码。

(
SELECT -- parents
            [UserGroup],
            [AllowDeny],
            [Permissions],
            [FullPath] as parent_path
    FROM
            [ACLs]
  WHERE add a filter for parents here

minus 
SELECT -- children
            [UserGroup],
            [AllowDeny],
            [Permissions],
            get_parent_path_from_child_path([FullPath]) as parent_path
    FROM
            [ACLs]
    WHERE add a filter for children here

)
union 
(
SELECT -- children
            [UserGroup],
            [AllowDeny],
            [Permissions],
            get_parent_path_from_child_path([FullPath]) as parent_path
    FROM
            [ACLs]
    WHERE add a filter for children here
minus
SELECT -- parents
            [UserGroup],
            [AllowDeny],
            [Permissions],
            [FullPath] as parent_path
    FROM
            [ACLs]
  WHERE add a filter for parents here

)