SQL92和SQL99标准定义了DDL构造。并非所有的数据库都支持此功能,或者并非所有数据库都支持此名称(例如,SQL Server具有用户定义的类型)。CREATE DOMAIN
这些允许人们定义要在其数据库中使用的约束数据类型,以简化和执行与允许值有关的规则。这种数据类型可用于列声明,存储过程和函数的输入和输出等。
我想知道:
- 人们实际上在他们的数据库设计中使用域吗?
- 如果是这样到什么程度?
- 它们有多有用?
- 您遇到了什么陷阱?
我正在尝试评估在将来的数据库开发中使用它们的可行性。
SQL92和SQL99标准定义了DDL构造。并非所有的数据库都支持此功能,或者并非所有数据库都支持此名称(例如,SQL Server具有用户定义的类型)。CREATE DOMAIN
这些允许人们定义要在其数据库中使用的约束数据类型,以简化和执行与允许值有关的规则。这种数据类型可用于列声明,存储过程和函数的输入和输出等。
我想知道:
我正在尝试评估在将来的数据库开发中使用它们的可行性。
Answers:
作为SQL Server和C#的用户,我没有在数据库中使用用户定义类型,因为我在应用程序方面更加强大。在发生诸如LINQ to SQL和Entity Framework之类的ORM之后,我对服务器功能的使用减少了很多。
例如,基于.NET的全球化功能,我使用CLR集成将某些DateTime转换功能加载到SQL Server,以将公历日期时间转换为其他格式。但是现在,情况有所不同,我不再这样做了。我只需要在应用程序层中加载数据并进行转换即可。
因此,经过将近4年的编程和检查我能想到的所有团队之后,我发现没有使用用户定义类型的示例。我也没有在许多.NET博客中看到它的实际效果。
虽然这并不意味着人们不使用数据库域,但肯定意味着至少使用数据库域并不常见。
关于堆栈溢出已经有详细的答案。我大都同意,但不同意给出的例子,我引用:
“例如,我定义了一个“ GenderType”(char(1),不可为空,持有“ M”或“ F”),以确保“性别”字段中仅允许使用适当的数据。”
就个人而言,我觉得更舒适地设置char(1)
类型并在列上定义约束。当违反约束时,我确切地知道在哪里可以找到我做错了什么。它也比用户定义的类型更知名,因此对于初学者来说,仅使用约束的数据库将更容易理解。
当然,这只是个人观点。其他人会说in ('M', 'F')
约束不是自记录的,对于发现数据库的新开发人员来说可能是非常模糊的。
IMO,将用户定义的类型用于更复杂的类型,并将约束用于基本内容。另外,当您无法轻松找到类型的显式名称时,就有可能使约束更合适。
我自己没有使用此功能,但我想您需要确保:
如果您的数据库由单个系统使用,则最好不要使用此功能,并在您的业务层或等效层中添加检查逻辑。这样可以为您提供更大的验证灵活性(例如使用正则表达式),并且可以将所有验证逻辑封装在一个地方。如果您的数据库由多个系统共享,则可以使用非常适合此任务的触发器。
我不确定UDT是否允许您自定义遇到验证错误时返回给客户端的错误消息。
如果同一验证规则适用于多列,则UDT非常有用。我认为,在具有规范化数据库设计的业务应用程序中,这并不常见。
您可能对检查“ [SQL Server]用户定义的类型有什么意义?”感兴趣。博客文章。
当您说“并非所有数据库都支持此功能”时,我认为一种更好的放置方式如下:
每个主要数据库都支持此功能,因为它们广泛支持触发器,功能和其他高级功能。
这使我们得出结论,它是高级SQL的一部分,并且在某些时候是有意义的。
Do people actually use domains in their database designs?
由于所需的覆盖范围广(考虑到运算符,索引等),可能性较小
If so to what extent?
再次,尽可能地限制,如果将现有的类型与一些附加的定义的逻辑(例如,检查等)结合起来可以解决问题,为什么走那么远?
How useful are they?
很多。让我们花一秒钟的时间来考虑一下像MySQL这样不太好的DBMS,我之所以在此示例中选择它是出于以下原因:它对inet(IP地址)类型缺乏良好的支持。
现在,您要编写一个主要关注范围和所有此类IP数据的应用程序,并且受制于默认类型及其有限的功能,您将编写其他功能和运算符(例如postgreSQL本身支持的功能和运算符)例如),或针对所需的每种功能编写更复杂的查询。
在这种情况下,您可以轻松地证明花费在定义自己的函数上的时间(PostgreSQL中的inet >> inet:range运算符中包含的range)。
到那时,您已经证明扩展数据类型支持是合理的,定义新数据类型仅需执行另一步。
现在回到具有真正好的类型支持但没有unsigned int ..的PostgreSQL,因为您确实关心存储/性能(谁知道...),所以您还需要添加它以及运算符-尽管这当然是大多数现有int运算符的派生。
What pitfalls have you encountered?
到目前为止,我还没有解决这个问题,因为我还没有一个项目既需要时间又需要时间证明。
我能看到的最大的问题是重新发明轮子,在“安全”层(db)中引入错误,不完整的类型支持,直到CONCAT(cast * AS varchar)失败后几个月,您才会意识到,因为您没有定义强制类型转换(newtype为varchar),等等。
有关于“不常见”等的答案。肯定是并且应该不常见(否则,这意味着dbms缺少很多重要类型),但是另一方面,应该记住一个(好的)db是ACID兼容的(与应用程序不同),并且与一致性相关的任何内容都最好保留在其中。
在许多情况下,业务逻辑是在软件层中处理的,并且可以在SQL中完成,这更安全。应用程序开发人员倾向于在应用程序层中感到更自在,并经常避免使用SQL实施更好的解决方案,这不应被视为良好实践。
UDT可能是优化的良好解决方案,另一个关于使用char(1)的m / f类型的答案给出了一个很好的例子。如果它是UDT,则它可以是布尔值(除非我们想提供第三和第四种选择)。当然,由于列的开销,我们都知道这并不是真正的优化,但是存在可能性。
从表面上看,UDT是一个很好的概念。它们使您能够真正规范化数据库(例如,当您有两个相互依赖的列时),并且还可以封装与您的新类型相关的任何逻辑。
实际上,您今天支付的价格过高。显然有开发开销,但除此之外,您还会失去各种ORM和报告工具的支持,从而使解决方案的整体复杂性大大提高。没有太多开发人员对UDT非常熟悉,而且我从未见过在示例之外使用过托管UDT。
最好是作为UDT(如果尚不存在)完成的类型的最佳示例之一hierarchyid
(MSDN链接)。为了保持效率,它以二进制格式存储其值(与通常的varchar自定义实现相反),如果没有该类型的函数,将很难维护。与外部函数相反,该类型提供的10种左右方法也最好作为该类型的一部分提供。在这种情况下,我将考虑使用自定义托管的UDT-通过提供有效而简洁的实现作为单独的类型,可以显着提高收益。