我有一个带有SQL Server后端的asp.net-mvc网站。我正在简化我的情况以突出显示并隔离问题。我在DB中有3个表
在我的网站上,当您创建文章时,您可以从多选列表框中选择您希望该文章发送的位置。
大约有25个地点,所以我在辩论添加一个名为“全球”的新位置作为快捷方式,而不是让人从列表框中选择25个不同的项目。我仍然可以将其作为前端的快捷方式,但现在我正在辩论是否有利于此流程到后端。
因此,如果我有一篇全球文章,而不是在ArticleLocation表中有25条记录,我只会有一条,然后我会在前端做一些技巧来选择所有项目。我想弄清楚这是不是一个非常糟糕的主意。
我能想到的事情让我感到紧张:
如果我创建一篇文章并选择全局但后来最后添加3个新位置会怎么样?如果没有这种全局设置,这3个位置将无法获得文章,但是以新的方式,他们会。我不确定什么是更好的,因为第二件事可能实际上是你想要的,但它有点不那么明确。
我对报告有要求,我想过滤所有全球文章。想象一下,我需要一篇文章.IsGlobal()方法。现在我想我可以说,如果一个项目与位置表中的所有记录具有相同的位置数量,我可以将其转换为被认为是全球性的,但是再次因为人们可以添加新的位置,我觉得这种方法有点不稳定
有没有人对这个困境提出任何建议,围绕在真正反映“所有记录”的参考数据表中创建记录。感谢任何建议
答案 0 :(得分:9)
根据要求,这是我的评论提升为答案。这也是扩展它的机会。
我会将答案限制在一个只有一个位置列表的系统中。我做过公司层级的事情:公司,部门,地区,州,县,办事处和员工或其他一些人。它变得丑陋。
在OP的问题的情况下,似乎向AllLocations
表添加Articles
位使得意图明确。标志设置为1的任何文章都会出现在所有位置,无论它们何时创建,并且不需要在ArticleLocation
表中包含任何条目。如果作者不希望文章自动显示在将来的位置,则仍可以将文章明确添加到所有现有位置。
实施涉及更多工作。我会将INSERT
和UPDATE
个触发器添加到Article
和ArticleLocation
表,以强制执行AllLocations
位设置且没有相应行的规则在ArticleLocation
中,或者该位清晰,可以显式设置位置。 (这是个人偏好让数据库在可行的情况下防御“坏数据”。)
根据您的需要,表值函数是隐藏一些脏工作的好方法,例如: dbo.GetArticleIdsForLocation( LocationId )
可以在内部处理AllLocations
标志。您可以在JOIN
Article
的存储过程和即席查询中使用它。在其他情况下,视图可能是合适的。
欢迎您借用的另一个功能(“从朋友那里窃取!”)是让管理员的登录页面成为“例外”页面。在这里,我展示的东西各不相同,从大规模的火焰灾难到简单的peccadillos。在这种情况下,与零位置相关联的文章将有资格成为非关键的内容,但值得检查。
在每个位置显示明确的文章可能会对添加新位置的人感兴趣,因此我可能会有一个网页。可能应更新某些文章以明确说明新位置,或重新考虑更改为所有位置。
答案 1 :(得分:6)
这是一个好主意......代表“所有其他记录”吗?
在表格中表示树是不是一个好主意?树的 Root 代表“所有其他记录”。
树和层次结构并不简单,但有很多例子,文章和书籍可以解决这个问题 - 比如Celko的树和SQL中的层次结构; Karwin的 SQL Antipatterns 。
所以你在这里实际拥有的是一个层次结构(可能只是一棵树) - 它可能有助于从一开始就解决问题。您示例中的Global
只是另一个Location
(树的根),因此当添加新位置时,您可以决定它是否是Global
的子项
<强>事实强>
<强>约束强>
Each
位置包含在at most one
父级位置中。 It is possible that for some
家长位置,more than one
位置包含在that
家长位置中。
It is possible that some
文章位于more than one
位置
and that for some
位置,more than one
文章位于that
位置。
<强>逻辑强>
这样您就可以为文章指定任何位置 - 但必须在需要时将其解析为叶级。
层次结构(树)在这里用“天真的方式”表示;使用闭包表,嵌套集或枚举路径 - 或者,如果你喜欢递归...
答案 2 :(得分:2)
在我理解的情况下,我认为在Location表中创建一个“全局”位置是个好主意。我肯定觉得最好在Article表中创建一个“全局”标志。
“这是一个好主意......?”这不是我们想要回答的问题。这主要是一个辩论问题,而不是Q&amp; A问题,此外,我们在社区中有足够的创造力来提出一些示例,其中“it”将是一个好主意,无论如何。
对于更具体的问题,如何在数据库中表示“所有位置”?这是根据您的业务需求进行的判断调用。
如果没有,那么可能您应该只将“所有位置”实现为帮助选择数据库中所有当前位置的帮助程序。
真实世界的位置具有重要的层次结构:
如果你认为你想要选择一个国家而不是全球,那么实施像Damir这样的等级表示是最好的选择。但是,如果您不确定除了Global之外是否还有其他任何位置分组,那么现在分层数据结构太多了。您需要做的就是确保您当前的实现具有可能的未来分层表示的迁移路径。
如果您 希望将来的地点包含在Global中而不需要一个分层的位置结构,那么我基于多年经验的本能就是创建“全球”作为伪位置。也就是说,Global将是Location表中的一个位置,但它具有特殊含义。这绝对是一种权衡,但是不更改数据结构以支持Global的好处,这意味着“Global”创建的所有特殊情况都通过排除或包含查询中的某些位置来处理而不是在某处检查一些标志。 (或者如果你喜欢标志,你可以在Location表中添加一个'伪位置'标志。)
使用Global作为位置,将自动处理Location表的添加或删除。所有全球文章的查询都很简单:与任何其他位置的所有文章的查询相同。按位置报告文章也很简单,全球文章与任何其他位置一样出现在报告中。您还可以表示“全局”文章(所有当前和未来位置)与“所有位置”文章(所有当前位置,但否未来位置)之间的差异。
选择在特定位置应该可见的所有文章稍微困难一些,现在检查“全局”以及该位置,但至少它检查同一个表中的2个值而不是检查两个不同的表。
SELECT article_id FROM ArticleLocation WHERE location_id in (1, 5);
VS
SELECT article_id FROM ArticleLocation WHERE location_id = 5
UNION
SELECT id FROM Article WHERE is_global;
答案 3 :(得分:1)
从逻辑上讲,正如您所描述的那样,GLOBAL实际上应该是全局的并且保持全局,即使您添加新位置(问题1已解决)。但这也意味着GLOBAL与“所有位置”不同(因为可能还存在一些我们尚未定义的其他位置)。我认为这个逻辑特别需要你的要求2 - 否则它会在添加新位置时完全失败。
分析完成了!从上面我们看到GLOBAL是所有这些位置之上的东西。尝试将其定义为位置是没有意义的。寻求最简单的解决方案!
即。 boolean flag - 文章是否为全局。在用户界面中,只需将其作为复选框 - 如果选中,则会禁用多选框。简单,容易,满足要求。完成!
答案 4 :(得分:0)
添加新位置时是否需要自动将某些文章添加到新位置?如果是,那么在这种情况下,我会考虑在后端添加新的“全局”属性。
否则可能不值得努力。即使您为每篇文章选择了10000篇文章和20个不同的位置,这些文章大约是200k记录,但在设置索引时并没有那么糟糕。
检查现有数据,了解人们如何选择地点。如果大多数用户只选择多个位置而不是全部,那么它实际上是一个边缘情况,除非确实产生问题,否则你不应该对它进行处理。
答案 5 :(得分:0)
我同意@ HABO的评论(他应该发布它 - 如果他这样做的话,那就投票给他)。添加atrribute到表文章以识别那些与所有位置相关联的项目,提供和 future,大概是在文章的生命周期中,从长远来看应该节省您的时间和精力。当然,触发器和计数器都可以解决这个问题,但它们很尴尬,如果/后续系统发生变化,这将是一种痛苦。 UI将更易于使用,因为用户只需单击一个复选框(或其他),而不是在不可预见长度的下拉列表中多次单击所有内容。
(@ Damir的等级理念也会起作用,但是 - 从一点点经验来讲 - 他们很麻烦,我不会在这里介绍一个,除非有更多的系统和/或者商业用来摆脱它。)