【问题标题】:Database structure, Users + User Types where Users can be of more than one Type数据库结构,用户 + 用户类型,其中用户可以是多个类型
【发布时间】:2013-02-20 13:16:27
【问题描述】:

我目前有一个用户表 tblUser 和一个用户类型表 tblUserTypes

两者通过tblUser... fkUserTypeID中的外键链接进行链接。

因此,目前用户只能属于一种类型。

但是,在某些情况下,用户可以是多种类型...例如,CustomerSupplier

对我来说显而易见的解决方案是在tblUsertblUserTypestblUser_UserTypes 之间创建一个新表,这是一个桥接表:

[tblUser] ---< [tblUser_UserTypes] >--- [tblUserTypes]

但是,我可以看到由此产生的复杂性......例如,当导出加入到他们的用户类型的用户列表时,通过直接加入,我最终会得到多行这些用户。也许可以使用 PIVOT 查询将每个用户记录带回单行? (更多关于这个)

将用户导入系统似乎也有问题......我目前正在使用文件中的BCP(批量复制过程)将用户直接导入用户表......导入文件包含单个字段“用户type”在现有模型中有效,因为每个用户当前只能是一种类型。但是,对于多种用户类型,我看不到直接进入用户表的直接 BCP 是如何工作的。

增加复杂性的是用户类型当前不是固定的......表 tblUserTypes 是动态的......系统的一部分是允许创建任意数量的用户类型。但是,我需要了解某些类型的用户才能在更高级别定义业务逻辑......例如“只允许 type=x 的用户在这个区域”......所以有人建议在用户类型表中有一系列标志来定义用户类型的类型(例如IsCustomer,@987654332 @)

这感觉像是一个过于复杂的混乱,我正在为如何前进而失眠。

我很想将用户类型带回表tblUser 并完全取消其他两个表...用户表中的一系列复选框(例如IsCustomerIsSupplier).. . 因为这样可以直接进行导入和导出。但是,用户类型不会是动态的。有趣的是,虽然用户类型不是完全动态的......因为如上所述,在业务登录方面我需要了解一些用户类型。

嗯,它应该是两者的混合体吗?我是否试图将两个功能合并为一个?也许我可以在用户表中包含与业务逻辑相关的类型的复选框/布尔类型(例如IsCustomerIsSupplier)并将"User Types" 的上下文重命名为"User Groups" 或类似的东西。

在考虑直接连接将导致用户被复制的结构时,我主要关心的是对导入、导出和搜索结果的影响......他们所属的每种用户类型对应一行。我必须进行 PIVOT 查询才能将其恢复到每个用户的一条记录,每个用户类型都有一个列,不是吗?一个现实的例子是一个包含 300 万条记录的用户表,一次导入 10,000 条记录……或一次导出 10,000 条记录……或搜索这 300 万条记录以检索 3,000 条匹配项并将其呈现在网页上以分页方式,他们可以浏览搜索结果页面(我在搜索查询中使用 ROWNUM 来处理分页,我不会每次都返回全部内容)。

这是我关于 Stack Overflow 的第一个问题,如果它有点令人费解或者已经列出了答案,我很抱歉......我试图搜索但无法提出处理与用户合作的复杂性的示例可以是多种类型。

哦,以防万一……这是一个使用 SQL Server 的 C# ASP.NET 应用程序。


在仔细考虑并阅读回复后,我将一路走来使用桥接表......要求说用户可以是多种类型,所以它就是这样。对现有代码的影响是巨大的,但现在比现在要好。

我玩弄了表结构,在平面结构中获取数据所需的查询有点繁琐,最终需要动态 SQL(因为用户类型的列表是动态的)我不喜欢但我看不到其他方法。

在下面的示例中,获取的公司由“事件 ID”过滤,即 fkEventID

如果有更好的“扁平化”方法,我将非常感谢任何帮助 :-)


直接加入(如果它们属于一种以上类型,则每个公司多行)

select * from tblCompany 
left join tblCompany_CompanyType on fkCompanyID = pkCompanyID
left join tblCompanyType on fkCompanyTypeID = pkCompanyTypeID
where tblCompany.fkEventID = 1


硬编码数据透视查询(如果它们属于多种类型,则每个公司单行,但公司类型不是动态的)

select * from (
select tblCompany.*,tblCompanyType.CompanyType from tblCompany left join
tblCompany_CompanyType on fkCompanyID = pkCompanyID
left join tblCompanyType on fkCompanyTypeID = pkCompanyTypeID
where tblCompany.fkEventID = 1
) AS sourcequery
Pivot (count(CompanyType) for CompanyType IN ([Customer],[Supplier],[Something Else])) as CompanyTypeName


动态透视查询(每个公司多行并处理动态公司类型)

DECLARE @cols AS NVARCHAR(MAX)
DECLARE @sql  AS NVARCHAR(MAX)

SET @cols = STUFF(
(SELECT N',' + QUOTENAME(CompanyType) AS [text()]
FROM (
select CompanyType from tblCompanyType
where fkEventID = 1
) AS Y
FOR XML PATH('')),
1, 1, N'');

SET @sql = N'SELECT * FROM (
select tblCompany.*,tblCompanyType.CompanyType from tblCompany left join tblCompany_CompanyType on fkCompanyID = pkCompanyID
left join tblCompanyType on fkCompanyTypeID = pkCompanyTypeID
where tblCompany.fkEventID = 1
) AS sourcequery
Pivot (count(CompanyType) for CompanyType IN (' + @cols + ')) as CompanyTypeName
order by pkCompanyID'

EXEC sp_executesql @sql;

【问题讨论】:

  • 好吧,请停止在表格上添加愚蠢的tbl 前缀。它还能是什么,一个观点?所以呢?你仍然要把它当作一张桌子来对待。就叫它dbo.Users
  • 另外,当您批量导入新用户时,如何将用户唯一标识为new?如果您导入一个名为 John Smith 的新客户,该客户是否与名为 John Smith 的现有供应商是同一个 John Smith?
  • 最后,同一个用户多次返回有什么问题?您始终可以按用户类型进行过滤,或者只是让应用程序/表示层折叠同一用户的多行。
  • 您是否还在用户定义的存储过程前面加上sp_
  • 我想旧习惯很难改掉...我仍然在所有内容前加上前缀!多年前被教过,在阅读任何类型的代码、存储过程、c# 代码等时都有帮助... txtUser 是一个文本框,strUser 是一个字符串,tblUser 是一个表。也许来自记事本代码编写的时代,一切都是黑色文本。

标签: c# asp.net sql-server


【解决方案1】:

您确实在用户和用户类型之间存在多对多关系,我建议您继续以这种方式实现它。

如果您需要在某些情况下看到它变平,您可以使用视图或存储过程来适应它。

如果您想继续使用 BCP 导入,您可以随时将 BCP 导入一个临时表,然后使用存储过程来填写您的 3 个表。无论如何,这样做可能更安全。

保持完全实现多对多关系将为您的应用提供最大的灵活性,并防止您在获得对新安全角色的新要求时不断修改用户表。

【讨论】:

  • 你是对的。我很懒惰,试图避免一路走下去……我已经开发了很多代码,而这个核心实体结构的变化将主要是震撼!……但我要必须将此归咎于计划不周,并将其放在下巴上。我会按照你的建议做:将文件 BCPing 到临时表中并从那里使用它。将使用我计划用来在需要的地方展平数据的 PIVOT 查询结构发布答案......可能会帮助其他人。
猜你喜欢
  • 1970-01-01
  • 2013-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-17
  • 2016-09-01
  • 2019-01-26
  • 2012-12-04
相关资源
最近更新 更多