【问题标题】:SQL - Getting data from joins if other joins are nullSQL - 如果其他联接为空,则从联接获取数据
【发布时间】:2026-01-20 17:10:01
【问题描述】:

我已经尝试了很多解决方案,但还没有找到一个可以正常工作的解决方案。任何帮助表示赞赏。我正在使用 SQL Server。

我有一些这样的表:

汽车

+-------+-----------------+-----------------+
| CarId |    CarImage     |    CarVideo     |
+-------+-----------------+-----------------+
|     1 | http://imageurl | http://videourl |
|     2 | http://imageurl | http://videourl |
|     3 | http://imageurl | http://videourl |
|     4 | http://imageurl | http://videourl |
+-------+-----------------+-----------------+

汽车名称

+----+-------+---------+------------+
| Id | CarId | CarName | LanguageId |
+----+-------+---------+------------+
|  1 |     1 | Car 1   |          1 |
|  2 |     1 | Bil 1   |          2 |
|  3 |     2 | Car 2   |          1 |
|  4 |     2 | Bil 2   |          2 |
|  5 |     3 | Car 3   |          1 |
|  6 |     3 | Bil 3   |          2 |
|  7 |     4 | Car 4   |          1 |
|  8 |     4 | Bil 4   |          2 |
+----+-------+---------+------------+

CARS_NICKS

+--------+--------+-------+---------------------+---------------------+
| NickId | UserId | CarId |      CarImage       |      CarVideo       |
+--------+--------+-------+---------------------+---------------------+
|      1 |      1 |     1 |                     |                     |
|      2 |      1 |     2 | http://NEW-imageurl |                     |
|      3 |      1 |     3 |                     | http://NEW-videourl |
|      4 |      2 |     1 |                     | http://NEW-videourl |
+--------+--------+-------+---------------------+---------------------+

CARS_NICKS_NAMES

+----+--------+--------------+------------+
| Id | NickId |   CarName    | LanguageId |
+----+--------+--------------+------------+
|  1 |      1 | Nickname 1   |          1 |
|  2 |      1 | Nicknavn 1   |          2 |
|  3 |      4 | Nickname 1-2 |          1 |
|  4 |      4 | Nicknavn 1-2 |          2 |
|  5 |      2 | Nickname 2   |          1 |
|  6 |      2 | Nicknavn 2   |          2 |
+----+--------+--------------+------------+

我想做的是显示汽车列表,如下所示:

汽车(用于 UserId 1)

+-------+------------+---------------------+---------------------+
| CarId |  CarName   |      CarImage       |      CarVideo       |
+-------+------------+---------------------+---------------------+
|     1 | Nickname 1 | http://imageurl     | http://videourl     |
|     2 | Nickname 2 | http://NEW-imageurl | http://videourl     |
|     3 | Car 3      | http://imageurl     | http://NEW-videourl |
|     4 | Car 4      | http://imageurl     | http://videourl     |
+-------+------------+---------------------+---------------------+

汽车(用于 UserId 2)

+-------+--------------+---------------------+---------------------+
| CarId |   CarName    |      CarImage       |      CarVideo       |
+-------+--------------+---------------------+---------------------+
|     1 | Nickname 1-2 | http://imageurl     | http://NEW-videourl |
|     2 | Nickname 2   | http://NEW-imageurl | http://videourl     |
|     3 | Car 3        | http://imageurl     | http://NEW-videourl |
|     4 | Car 4        | http://imageurl     | http://videourl     |
+-------+--------------+---------------------+---------------------+

这样做基本上是从 Cars 和 CarNames 表中获取每辆车的所有默认信息。但是,如果 Cars_Nicks 和 Cars_Nicks_Names 表存在,我希望这些数据被覆盖。

在我们的应用程序中,每个用户基本上都有一个“老师”。每个老师的设置也将是每个学生的默认设置,除非学生自己也设置了昵称。所以在这种情况下,UserId 1 是 UserId 2 的“老师”。这就是为什么在 UserId 2 的表中,他们有一些关于 UserId 1 的数据,还有他们自己的昵称,除了默认的 Cars 信息(如果某个数据点)在刻痕中不存在。

我尝试了不同的解决方案来实现这一点,JOINUNIONGROUP BYDISTINCT 以及我能想到的所有组合。但我不断得到多个结果,如下表:

汽车(用于 UserId 2)(我的尝试)

+-------+--------------+---------------------+---------------------+
| CarId |   CarName    |      CarImage       |      CarVideo       |
+-------+--------------+---------------------+---------------------+
|     1 | Nickname 1   | http://imageurl     | http://videourl     |
|     1 | Nickname 1-2 | http://imageurl     | http://NEW-videourl |
|     2 | Nickname 2   | http://NEW-imageurl | http://videourl     |
|     2 | Car 2        | http://imageurl     | http://videourl     |
|     3 | Car 3        | http://imageurl     | http://NEW-videourl |
|     3 | Car 3        | http://imageurl     | http://videourl     |
|     4 | Car 4        | http://imageurl     | http://videourl     |
+-------+--------------+---------------------+---------------------+

有人知道我怎么能得到这个吗?

这是我的众多尝试之一:

SELECT
    CarId,
    ISNULL(CarNicks.CarName, CarNames.CarNames) CarNames,
    ISNULL(CarNicks.CarImage, Cars.CarImage) CarImage,
    ISNULL(CarNicks.CarVideo, Cars.CarVideo) CarVideo
FROM 
    Cars
LEFT JOIN 
    (SELECT CarId, CarName, CarImage, CarVideo
     FROM Cars_Nicks Nicks
     JOIN Cars_Nicks_Names NickNames ON NickNames.NickId = Nicks.NickId 
                                     AND NickNames.LanguageId = 1 AND UserId = 1
     UNION
     SELECT CarId, CarName, CarImage, CarVideo
     FROM Cars_Nicks Nicks
     JOIN Cars_Nicks_Names NickNames ON NickNames.NickId = Nicks.NickId  
                                     AND NickNames.LanguageId = 1 AND UserId = 2) CarNicks ON CarNicks.CarId = Cars.CarId
JOIN 
    CarNames ON CarNames.CarId = Cars.CarId;

【问题讨论】:

  • 感谢格式化链接,我已经更新了。您对我可以使用的查询有什么想法吗?
  • 请在代码问题中给出minimal reproducible example--剪切&粘贴&可运行代码;具有期望和实际输出(包括逐字错误消息)的示例输入(作为初始化代码);标签和版本;明确的规范和解释。对于包含最少代码的错误,您可以给出的代码是您显示的代码可以通过您显示的代码扩展为不正常。 (调试基础。)对于包含 DBMS 和 DDL 的 SQL,其中包括约束、索引和表格初始化。
  • 当你得到一个你不期望/不理解的结果时,停止试图找到你的总体目标并找出你的误解是什么。--隔离第一个意外/误解的子表达式及其输入 &输出并了解是什么误解、错字、错误推理等导致了它。 (调试基础。)询问一下。 PS“基本上”或“基本上”或“换句话说”,它没有介绍或总结您也给出的清晰、准确和完整的描述,只是意味着“不清楚”。
  • 感谢 philipxy,我补充说我正在使用 MSSQL。否则,我相信我已经给出了我的问题的最小可重复示例,以及我拥有的数据
  • 我们不能剪切、粘贴和运行——所以不能minimal reproducible example。也没有一个明确的规范,只有一些“基本上”带有模糊的短语——没有minimal reproducible example。另请参阅我的最后一条评论,该评论与您的​​评论交叉。 PS 在给出业务关系(船舶)/关联或表(基础或查询结果)时,请说出其中的一行根据其列值说明了业务情况。

标签: sql join group-by union distinct


【解决方案1】:

使用LEFT JOIN 将各种表放在一起,而无需复制连接键。您应该(外部)连接所有三个表 CarsCars_NicksCars_Nicks_Names 一次,使用 CarIdNickId 作为各自的连接键。这自然会为每个CarId 生成多个记录,但是(如果我正确理解您的数据模型)每个CarIdUserLanguage 最多只能生成一个昵称。您需要将老师的昵称加入为Cars_NicksCars_Nicks_Names 表的另一个副本,并适当地重命名,以便您可以识别昵称是来自学生还是来自老师。

使用COALESCE 使一件事优先于另一件事。在SELECT 子句中,您将对您希望其中一个表中的值覆盖另一个表中的字段值的字段执行类似于您最初发布的尝试的操作,并且每个列表中的最后一项应该是最终后备值。

在这种情况下不要使用UNION

以下只是名称的未经测试的示例。扩展要连接的内容以在输出中包含您想要的其他字段是相当容易的。

select Cars.CarId, Cars.LanguageId, coalesce( StudentNN.CarName, TeacherNN.CarName, Cars.CarName ) as CarName
  from
      Car_Names left join (
           select NickId from Cars_Nicks where UserId = :student
      ) StudentN using (CarId)
      left join (
          select NickId from Cars_Nicks where UserId = :teacher
      ) TeacherN using (CarId)
      left join (
          select CarName from Cars_Nicks_Names
      ) StudentNN on StudentN.NickId = StudentNN.NickId and Car_Names.LanguageId = StudentNN.LanguageId
      left join (
          select CarName from Cars_Nicks_Names
      ) TeacherNN on TeacherN.NickId = TeacherNN.NickId and Car_Names.LanguageId = TeacherNN.LanguageId

【讨论】:

  • 似乎 union 是相关的——学生自己的昵称取代了老师的昵称,然后我们将 Cars 行与基于这些昵称的行合并。但规范不明确。
  • 我已经尝试通过简单地将 Car_Nick_Names 加入 Cars_Nicks 表两次(一次用于学生 ID,一次用于教师 ID)到 Cars_Nicks 表......但即使使用了CarName 的 COALESCE
  • 这是因为有些数据确实有多行,因为它既有教师数据又有学生数据
  • 是的,现在应该可以了。不幸的是,不得不为此做一组额外的 2 个左连接,但我认为这可能是目前最好的解决方案。谢谢
  • 只要可以为没有老师的用户使用哨兵ID就可以了。
【解决方案2】:

我认为你想要inner joincarscars_nickscar_names,然后用left join 引入car_nick_names。然后就可以用coalesce()实现替换逻辑了:

select
    ni.userid,
    ni.carid,
    coalesce(nn.carname, na.carname) carname
    coalesce(ni.carimage, ca.carimage) carimage,
    coalesce(ni.carvideo, ca.carvideo) carvideo
from cars ca
inner join cars_nicks ni on ni.carid = ca.carid
inner join car_names na on na.carid = ca.carid and na.languageid = 1
left join cars_nicks_names nn on nn.nickid = ni.nickid and nn.language_id = 1

您没有解释如何管理languageid 列,该列存在于*_names 表中,因此我将其强制为固定值。在现实世界中,您将拥有一个存储用户语言的users 表,并使用该值进行过滤。

【讨论】:

  • 这是真的,对于 LanguageId。我有一个用户表来获得它,但是像你所做的那样将它设置为 1 对于基本想法来说是好的。对于那个很抱歉。至于查询,这表明我可能需要从主汽车表中进行 4 次左连接。我需要在该表的 NickId 上为每个 cars_nicks 左联接,在该表的 NickId 上为 cars_nicks_names,然后还重复同样的事情,但对于在 UserId = 2 上的 cars_nicks 等
  • 查看我在回答中发布的示例