【问题标题】:Storing User Settings - anything wrong with using "Flags" or "Bits" instead of a bunch of bools?存储用户设置 - 使用“标志”或“位”而不是一堆布尔值有什么问题吗?
【发布时间】:2013-12-17 07:41:44
【问题描述】:

我正在为我的 MVC 应用程序设计用户设置,现在我有大约 20 个布尔设置供用户切换。由于每个用户将始终拥有所有设置,因此我正在考虑将每个设置作为布尔值存储在 User 表中。尽管随着应用程序需求的增长,这将变得笨拙。

第一个问题 - 在这种情况下,你的桌子上有很多列有什么问题吗?

然后我考虑使用标志,并将设置作为一个位存储在一个数组中:

[Flags]
public enum Settings
{
    WantsEmail = 1,
    WantsNotifications = 2,
    SharesProfile = 4,
    EatsLasagna = 8
}

然后每个用户将在其用户行中有一个“设置”列,如果有 20 个设置,则存储值 2^20。

我会用它来指导我的工作:What does the [Flags] Enum Attribute mean in C#?

这比以前的方法更好吗?欢迎任何建议。

【问题讨论】:

    标签: c# asp.net asp.net-mvc database-design database-schema


    【解决方案1】:

    这取决于从数据管理的角度来看应该将什么视为原子

    • 如果您总是从/向数据库中搜索、读取和写入所有设置,那么整组设置可以被认为是原子的,可以一起存储在数据库中。李>
    • 但是,如果您需要在设置的 子集 上执行任何这些操作(例如,只设置一个标志而不修改其他标志),那么它们就不是原子的(从数据管理的角度来看),因此将它们一起存储在同一个数据库字段中会违反atomicity 的原则,因此也违反了 1NF。

    请注意,某些 DBMS(例如 MS SQL Server)在存储布尔值方面非常有效(理想情况下每个布尔字段只有一位)。即使是那些不够完美的,通常每个布尔值也不会花费超过一个字节(Oracle,我在看你),只有当你有数百万或数十亿用户时,这可能会成为一个问题。

    【讨论】:

    • Oracle, I'm looking at you,现在这既可笑又可笑!一个比特一个完整的字节?
    • @MichaelPerrenoud Oracle 不支持“布尔字段”的概念,it looks like 永远不会,恕我直言,这是一个错误。相反,您通常会使用类似:FLAG CHAR(1) CHECK (FLAG IN ('Y', 'N')).
    • 呃!无论如何,我的朋友对此答案+1。伙计,我很高兴我不必与 Oracle 合作!
    • @MichaelPerrenoud 不要对他们太苛刻 - 他们做的很多事情都是正确的,我实际上更喜欢与 Oracle 合作而不是其他 DBMS。但是每个软件都有它的烦恼,这就是生活中的事实......
    • 确实如此。我只需要在内部使用其他使用 Oracle 的应用程序,它速度慢、臃肿且有限。每次我要求他们做某事时;呃,我们不能那样做。但也许这不是甲骨文的限制,而是那些从事这项工作的人或他们的软件。
    【解决方案2】:

    如果您只有 20 个值,并且没有机会有一天会是 100 个值 - 可以使用其中任何一个(最好是枚举)。但是如果有机会获得 100 个值 - 你真的需要构建一个键值对表(有一个表 Users、表 Settings 和表 UsersSettings 相互映射)。

    【讨论】:

    • 这可能是在特定情况下遵循的最佳方法。要添加到这一点,您可以考虑将您的属性名称存储在单列主表中(而不是多列)。因此,要添加新属性,您只需在表中插入新属性名称即可。您可以使用 Dictionary 类型来存储每个用户的值。
    【解决方案3】:

    是的,这是一种更好的保存和检索数据的方法,因为您不必在每次出现新设置时都更改数据库。这意味着对系统进行更改的成本将大大降低。

    只需确保为您的枚举分配适当的整数值(2 的幂),C# 不会自动执行此操作,如果您输入错误,它将无法按预期工作。

    【讨论】:

      【解决方案4】:

      我认为有两种情况可能会造成阻碍:

      1 - 未来用户设置是否存在非布尔值的范围?除非这已经单独处理,否则您将不得不想出一种存储此非布尔设置的新方法,最终可能会为您的设置提供两个不同的存储容器 - 一个用于布尔值,另一个用于其他任何内容。不过,您最初的问题确实只是指定了布尔值,所以我假设您已经想到了这一点;)

      2 - 删除设置可能是个问题?如果两年后,用户无法再收到通知或吃千层面,您将不得不仔细管理对此枚举的修改,以确保您的位标志在您从中删除项目时向后兼容。

      从历史上看,我为此使用了一个键值对“用户设置”表。架构基本上包括:

      [数据库身份](长/身份)
      [UserId](对用户表的 FK 引用)
      [SettingId](给定设置的任何标识符 - 数据类型取决于标识符)
      [SettingValue](NVarChar(?) - 设置值的字符串表示 - 字段大小取决于请求)

      它的性能不如位标志,并且需要对字符串值进行一些应用程序端解析以重新水合用户设置,但它确实为您提供了一个可扩展的解决方案,可以:

      1 - 几乎可以处理任何给定设置的任何核心数据类型。
      2 - 轻松处理每个用户的不同数量的设置。
      3 - 可以仅使用与您的应用程序默认设置不同的设置进行稀疏填充。

      迄今为止,我已在多个生产应用程序中成功使用了此解决方案,但是如果您在数十万用户的区域中使用此解决方案,则此解决方案可能不合适。

      【讨论】:

        【解决方案5】:

        以您提出问题的方式回答您的问题:

        “在这种情况下,你的桌子上有很多列有什么问题吗?”

        以这种方式存储数据绝对没有问题。你可以比你在这里提出的要多得多,没有任何麻烦。

        “这比以前的方法更好吗?”

        这就是我做这些事情的方式,但这是因为它适合我管理此类数据的方式,而不是更好或更正确的情况。 Branko 提出了一个论点,即这不是 1NF,他是完全正确的,但即使是规范化的支持者也承认,有时规范化并不总是最好的方法,有时你需要去规范化以获得更好的性能。

        拥有单独的 BIT 的优点: 您可以在 SQL 查询中引用每个属性(列)并确保您选择了正确的位,否则您需要在应用程序代码中继续引用您的枚举器以了解每个位在 SQL 表中的含义。

        您可以更轻松地自动填充实体对象。

        报表等可以使用这些设置,而无需进行任何计算来获取单独设置的值。

        正确的标准化。

        将所有内容放在一列(标志)中的优点(如果您需要更多存储空间,则可以多于一列):

        您可以更轻松快捷地以组的形式读取和写入设置。

        操作或读取设置的 SQL 查询将更快地编写。

        您可以用更少的代码迭代设置集合。

        编写和维护自己的实体对象更容易。

        更少的内存使用(但公平地说,无论您使用哪种方法,这都会很低)。

        如果您想将其放入会话对象中,则负载要小得多。

        我要说的唯一考虑是,一旦您的标志总数超过您可以从一个变量 (2^64) 获得的总内存空间,您就会失去一些使用标志作为数据的优点,然后必须传播无论您使用哪种方法,都可以跨多个列。

        【讨论】:

          【解决方案6】:

          去吧。
          请记住 63 个标志,然后您选择扩展,那又如何?
          我在 Oracle 10g 3.8m 行项目中做到了这一点,在 SQL 端表现出色
          MS SQL 15.2M 行在 SQL 端表现出色
          我没有在 Azure SQL 中测试它,但通常在 SQL 中你有 WHERE userID=XXX 然后在选择你做面具找到类似 INROLE...

          很难获得支持,但是如果您付出额外的努力并编写一些代码将任何数字转换为很好的解释......也可以使用描述

          请记住,在 SQL 方面没有索引可以帮助您进行全面扫描...但是对于用户设置权限...没问题,您可以离线进行报告...

          在 C# 方面,我进行描述并将其用于...动态构建下拉列表...

          using System;
          using System.Collections.Generic;
          using System.Linq;
          using System.Web;
          using System.ComponentModel;
          
          
          namespace common
          {
          
          
          public static class EnumHelper
          {
          
              public enum Speed  //example
              {
                  [Description("5 metters per second")]
                  Five = 5,
                  [Description("10 metters per second")]
                  Ten = 10,
                  [Description("15 metters per second")]
                  Fifteen = 15,
                  [Description("20 metters per second")]
                  Twenty = 20,
                  //[Description("25 metters per second")]
                  TwentyFive = 25,
                 [Description("30 metters per second")]
                  Thirty = 30
              }
          
              /// <summary>
              /// get the string value of Enum Attribute
              /// </summary>
              /// <param name="EnumConstant"></param>
              /// <returns>
              /// string enumDesctiption = EnumHelper.EnumDescription(EnumHelper.Speed.Thirty);
              ///  enumDesctiption = EnumHelper.EnumDescription(DayOfWeek.Monday); when there is no desc returns as string the ENUM property
              /// </returns>
              public static string EnumDescription(Enum EnumConstant)
              {
                  System.Reflection.FieldInfo fi = EnumConstant.GetType().GetField(EnumConstant.ToString());
                  DescriptionAttribute[] aattr = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
                  if (aattr.Length > 0)
                  {
                    return aattr[0].Description;
                  }
                  else
                  {
                      return EnumConstant.ToString();
                  }
              }
          
             }
          
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2015-02-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-12-18
            • 2011-08-08
            • 2014-12-30
            相关资源
            最近更新 更多