基于字段值的“有条件”加入?

Posted

技术标签:

【中文标题】基于字段值的“有条件”加入?【英文标题】:"Conditional" Join Based on Field Value? 【发布时间】:2012-12-08 06:38:34 【问题描述】:

我正在开发的网站有 3 种不同类型的用户:管理员、申请者、审阅者。这些组中的每一个都将有一些需要存储的基本信息(姓名、ID、电子邮件等)以及每个组独有的一些数据。我为每个特定组创建了一个users 表以及一个表来存储它们的唯一数据。

用户:id、f_name、l_name、email、user_type users_admin:id、user_id、office、emp_id users_applicant:id、user_id、dob、地址 users_reviewer:id、user_id、active_status、地址、电话

如果用户的user_type 为“1”(申请人)登录,我需要JOINusers_applicants 表以检索他们的完整记录。我尝试使用UNION,但我的表有很大不同的列。

有没有办法根据用户的类型编写一个条件查询,将JOIN 写入正确的表?我是不是完全走错了路?

提前感谢您的帮助!

【问题讨论】:

为什么这必须发生在 SQL 而不是 php 中?你已经尝试过什么? 既然您一开始就必须处理 3 个不同的列组,为什么不使用 3 个不同的查询呢? @DanMan 我想这也是我想知道的一件事。查询获取用户的类型,然后使用 PHP 确定要使用的 JOIN 是否是不好的做法? @DanMan 下面是正确的,如果您在此处使用 TPT,请转储表特定键,而基表中的 Id 键是您唯一需要的键。 【参考方案1】:

好吧,最后你的桌子已经有缺陷了。为什么每种类型都有一张桌子?为什么不将所有这些字段放入users 表或user_details 表中(如果您真的想要一个额外的表用于非通用数据字段)?目前,从关系的角度来看,您实际上是在创建 4 个独立的用户表。

那么为什么类型表有代理键?为什么user_id 不是(唯一的)主键?

如果您更改了它,您只需要用户 ID 来检索您想要的数据,而您已经得到了它(或者您甚至无法检索用户类型)。

【讨论】:

使用一个用户表不会有问题。鉴于每种用户类型需要不同的值,这会导致许多空字段,我认为这是应该避免的。不是这样吗? 确实如此,但不如跨多个表运行复杂查询那么糟糕。只要那些空列不必是 NULL-able - 没什么大不了的。 @Vecta Table per type 是一个有效的 DB 结构,不确定是否有足够的信息来确定它是否在此处正确使用,但它在许多情况下非常有用。使用 ORM 会容易得多,但不必担心这部分。 @Matthew:也许吧,但是通过外键以外的任何方式关联条目绝对是糟糕的设计。因此,如果他想保持桌子原样,他至少需要一个 users_types 桌子,正如 raheel shan 建议的那样。 @DanMan 同意 - 在我的回答中提出了类似的建议。【参考方案2】:

您可以通过编程方式执行此操作,也可以使用一系列CASEs 和LEFT JOINs 执行此操作。

为简单起见,让我们使用表 users 来执行此操作,您可以在其中拥有类型 1(普通用户)、2(高级用户)或 3(管理员)的用户。普通用户有一个电子邮件但没有电话,高级用户有一个地址和一个称为“超级大国”的字段,管理员有一个电话号码,没有别的。

既然你想为所有人使用相同的SELECT,当然你需要将所有这些都放在你的SELECT中:

SELECT user.id, user.type, email, address, superpower, telephone

然后您需要LEFT JOIN 来恢复这些

FROM user
LEFT JOIN users_data ON (user.id = users_data.user_id)
LEFT JOIN power_data ON (user.id = power_data.user_id)
LEFT JOIN admin_info ON (user.id = admin_info.user_id)

现在“未使用”字段将为 NULL,但您可以提供默认值:

SELECT
    CASE WHEN user.type = 0 THEN email ELSE 'nobody@nowhere.com' END AS email,
    CASE WHEN user.type = 1 OR user.type = 2 THEN ... ELSE ... END as whatever,
    ...

您可以将特定的WHERE 条件放入JOIN 本身,例如如果你只想要 J 部门的管理员,你可以使用

LEFT JOIN admin_info ON (user.id = admin_info.user_id AND admin_info.sector = 'J')

总查询时间应该不会太长,因为大多数JOINs 将返回很少(而且,如果您指定用户 ID,它们实际上会很快返回 nothing) .

您也可以使用UNION 执行相同操作,这样会更快:

SELECT user.id, 'default' AS email, 'othermissingfield' AS missingfieldinthistable,
  ... FROM user JOIN user_data ON (user.id = user_data.user_id)
  WHERE ...
UNION
SELECT user.id, email, 'othermissingfield' AS missingfieldinthistable,
  ... FROM user JOIN power_data ON (user.id = power_data.user_id)
  WHERE ...
UNION
  ...

现在,如果您指定用户 ID,则除一个之外的所有查询都将很快失败。每个查询都有相同的WHERE 重复加上任何特定于表的条件。 UNION 版本的可维护性较差(除非您以编程方式生成它),但应该稍微快一些。

在所有情况下,建议您在适当的字段上保持更新的索引。

【讨论】:

【参考方案3】:

相反,我建议您像这样重建表格结构。

创建一个表

users_types : 
id 
type

然后用外键创建另一个表users

users :     
id 
f_name 
l_name 
email 
office 
emp_id 
dob 
address 
active_status 
phone 
users_types_id

现在,当您需要插入数据时,在特定用户不需要的字段中插入 null。您可以简单地根据 id 获取记录。同样使用左连接会给你用户类型的名称。

【讨论】:

users_types 表只有在用户可以有多个类型时才需要。即使这样,您也可能会使用 SET() 列。

以上是关于基于字段值的“有条件”加入?的主要内容,如果未能解决你的问题,请参考以下文章

有条件地加入和映射 2 个 RDD

基于在 Excel 中的每一行中为不确定的行数确定的值的条件格式

加入不起作用的条件

SQL:基于另一个字段值的条件字段输出

基于 Symfony2 中其他字段值的字段条件验证

php WPForms:基于字段值的条件表单重定向。