首页 > 解决方案 > VB/SQL - 如何从具有多个匹配项的联接表中过滤 DataGridView

问题描述

我正在使用 SQL Server 和 Visual Studio 2019。

我正在寻找有关根据它们出现在什么视图中过滤我的文档列表(在 DataGridView 中)的最佳方法的建议。

我有 3 个数据库表,如下所示:

表

我已经为这篇文章简化了我的Document 表,但是DataGridView中显示的每个文档都有很多细节。

我将用于苹果的DataTable填充到DataView中,然后在我的DataGridView DataSource中使用。在填充DataTable的SQL中,我有一个自定义列,它使用JOIN来确定每个文档出现的视图,然后我根据自定义列过滤文档列表。这仅在文档仅出现在 1 个视图中时才真正有效,否则JOIN会检索文档的多个条目以覆盖每个匹配的视图。(希望这是有道理的)。

我也简化了这篇文章的视图表,如果你喜欢,一个视图可以包含子视图,即:

View 1
 > Sub View 1
 > Sub View 2
    > Child of Sub View 2

这意味着Document可以在View中出现多次,因为它可能出现在各种Sub Views中。

我想知道我是否最好继续检索自定义列,或者我是否应该运行一个全新的SQL查询,甚至基于View 表创建一个新的DataTable / DataView

我们的一些工作人员是远程的,而一些项目可能包含数千个文档,所以我想确保我使用了最强大的方法,同时也是一种在检索信息时造成的延迟最少并为用户提供最佳体验的方法。

我希望我已经解释了一切,让你们得到我想要实现的目标的清单。

在此先感谢您,我将不胜感激。

>
>
Edit - Following first answer post:

正如你所说,我可能解释得不够多,所以我整理了一个视觉示例来说明我正在努力实现的目标。

加载项目时,会列出所有文档,但尚未单击​​TreeView ,因此未应用过滤器。列出所有文件:

查看 1

如果用户单击TreeView上的“视图 1 > 子视图 2 ”,它会根据CustomJOINColumn中的数据进行过滤,见下文:

查看 2

如果用户在TreeView上单击“视图 1 > 子视图 1 ”或“另一个视图 > 文档”,它会再次根据CustomJOINColumn中的数据进行过滤,见下文:

查看 34

如您所见,文档 D001已出现在3 个不同的视图/子视图中。

问题显示在第一张图片中。文档 D001列出了 3 次,因为它与 3 个视图/子视图相关联。我只希望Document D001在第一个列表中出现一次,但是当单击TreeView中的相应节点时,它会正确过滤,如图所示。

我希望这是有道理的。

标签: sql-servervb.netvisual-studiodatagridview

解决方案


您的案例中可能有一些细节表明了不同的解决方案,但从这里的情况来看,您应该删除特殊列,删除带有过滤的完全填充,并为您的 DataGridView 实现集中填充。它甚至不涉及太大的变化,只是轻微的修改而不是重大的重写。

为什么?因为特别是如果您在 WAN 上有远程工作人员,如果您有很多大视图,加载完整文档列表的性能损失将是巨大的。集中加载允许您花时间仅加载相关的文档详细信息。您可以“免费”获得用户前 500 毫秒左右的时间,但如果您花费 30 秒来加载和过滤,他们会认为它已经崩溃了。当您的应用程序充满了您永远不会使用的文档详细信息的网格行时,它会占用更多内存。另外,正如您已经发现的那样,提出基于任意数量重叠组的成员身份的过滤方案非常困难,而集中负载非常容易。

那么该怎么做呢?首先,转到您的数据集并使用“添加”->“查询”修改表适配器。您将定义一个使用参数 (@View) 的新查询,并以与原始格式相同的格式返回数据,但仅适用于该视图中的文档。给它一个明显的名称,例如当它提供名称“Fill”时将其命名为“FillByView”

接下来,在 Form_Load 事件中获取填充网格的代码副本(类似于 tadaptDocs.Fill(tblDocList))并将其注释掉,但在用户选择视图的控件的 Change 事件中放置一个副本. 将其从“Fill”更改为“FillByView”并添加@View 参数。

您可能希望 Form_Load 事件将视图设置为选择的最后一个视图(保存在 My.Settings 中)或列表中的第一个视图,这将启动一个 Change 事件并使用一组初始文档填充 DataGridView。

现在,每次用户选择一个新视图时,网格都会加载该视图中的那些文档,而不管它们可能在哪些其他视图中。

当您通过“更新”保存更改时,VB 会自动将更改应用到基础数据,您不必担心它会将您的数据库表截断为该视图中的文档。它对已删除的记录和从未加载的记录进行了内部区分。您也不需要在 TableAdapter 上制作新版本的 Update、Insert 或 Delete 方法,只需 Fill 需要考虑 View 的新版本。

编辑:以下内容是针对添加内容而添加的,以澄清问题

听起来仍然需要集中加载,但仅在用户选择视图时才需要。您的初始视图应该是“全部填充”,但经过修改以去除重复项。如果您在网格中有很多细节,您可能希望使用 CTE 将其分成两部分,示例如下

;WITH cteCust as (
   SELECT D.DocID as DocID, D.docNum , D.docTitle , V.CustJoinName as CustJoin
    , ROW_NUMBER() OVER (PARTITION BY D.docNum ORDER BY V.viewTitle) as ViewRank
   FROM #tblDoc as D  LEFT OUTER JOIN #MapDocView as M on D.DocID = M.DocID
        OUTER APPLY fnGetCustJoinName(M.ViewID) as V 
) SELECT C.DocID , C.docNum , C.docTitle , C.CustJoin , D.docOther 
FROM cteCust as C INNER JOIN #tblDoc as D ON C.DocID = D.DocID 
WHERE ViewRank = 1
ORDER BY D.docNum 

此熊解释的一些元素,如果您需要更多详细信息,请在评论中回复

cteCust提取核心文档信息并添加自定义连接列,我不确定你是如何做到的,但表值函数是一种方法,我使用它是为了简单起见,替换你正在使用的任何内容。你说有很多文档信息,这可以让你只处理核心细节,然后再结合其他东西。我们在 Map 表上使用了LEFT OUTER JOIN,因为您希望没有视图的文档仍然显示在未过滤的列表中。

ViewRank是您摆脱重复项的方法,通过在文档上划分 ROW_NUMBER 函数,您只能获得它所在的第一个视图。在焦点填充中,您希望过滤视图,但您说初始填充应该只是一个视图,所以每个文档只出现一次。

请注意,最终的 SELECT 将其与 Documents 表联系起来,因此您可以选择额外的字段。

注意:在您的示例中,您有一个简单的成员模型 - 您希望文档属于特定视图而不是分层视图,您希望视图和所有子视图中的所有文档(即 D006 已批准但在您在更新问题的示例 3 中选择父文档)。如果这些要求发生变化并且您的客户想要递归会员扫描,您应该将其作为第二个问题发布,这个问答已经很庞大,如果它是一个新问题,您会重新审视它。在这里也发布一个参考,但让它成为一个新问题。


推荐阅读