【问题标题】:INNER JOIN on broken table (non-nullable field)损坏表上的 INNER JOIN(不可为空的字段)
【发布时间】:2014-06-07 03:23:54
【问题描述】:

我有三张桌子:

Document                     DocumentExt           Tracking
-------------------------    -----------------     --------------------------------------------
ID  |  Name   |   DocType    DocId  |  OtherId     ID  |  DocOtherId  |  DocType  |  AccessTime
-------------------------    -----------------     --------------------------------------------   
1      SomeDoc    Z          1         Doc1        1      Doc2           X          [Date here]
2      SomeDoc2   X          2         Doc2        2                     A          [Date here]
3      SomeDoc3   Y          3         Doc3        3      Doc1           Z          [Date here]
...    ...        ...        ...       ...         ...    ...            ...        ...

注意Tracking.DocOtherId 中的缺失值。这是一个不可为空的字段,默认为空字符串。

问题是我需要对这两个执行连接,其中包括Tracking 中的每条记录的一行,以及来自Document 的相关信息。比如:

SELECT 
    Tracking.ID, Document.Name 
FROM 
    Tracking
INNER JOIN 
    DocumentExt ON DocumentExt.OtherId = Tracking.DocOtherId
INNER JOIN 
    Document ON Document.ID = DocumentExt.DocId

但是,由于Tracking.DocOtherId 不可为空,因此查询将为DocumentExtTracking 中的每个记录返回一行。为了查询的目的,我需要将 Tracking.DocOtherId 视为可为空,以便 JOIN 正常工作。有没有办法做到这一点?

EDIT:我想我需要说明我需要为Tracking 中的EACH 记录返回ONE 记录,包括DocOtherId 中的空字符串。

例如:

TrackingId  |  DocumentName
---------------------------
1              SomeDoc2
2              NULL
3              SomeDoc1
...            ...

EDIT 2:标记为关闭。我做错了,我采取了完全不同的方法。

【问题讨论】:

  • 你不能添加一个 where 子句说 WHERE Tracking.DocOtherId ''?
  • 三个表的 ID 列是否相同?如果您在 SomeDoc 上获得另一个 Tracking 条目,它在 Tracking 中的 ID 是否仍为 1?
  • 1.如果有这么多空字符串,为什么 DocOtherId 是不可为空的字段? 2.您的数据集是否如此庞大以至于仅使用 docotherid 不为空的跟踪数据创建一个临时表会令人望而却步?
  • @Asdfg,不,我不能,很遗憾。因为DocumentExt 中没有空字符串值的记录,所以不会对空字符串进行比较。添加这样的记录可能会给整个数据库带来灾难性的后果。
  • 也许如果您添加了预期的输出,您可能会得到答案。目前尚不清楚您的期望或为什么 LEFT JOIN 不适合您。

标签: sql sql-server tsql inner-join


【解决方案1】:

我看到了这条评论:

我需要包含 Tracking 中的所有记录。

这意味着您需要 OUTER 联接,而不是 INNER 联接。使用 coalesce() 函数显示缺失名称的通用值。

 SELECT Tracking.ID, coalesce(Document.Name,'') As Name
 FROM Tracking t
 LEFT JOIN DocumentExt de ON de.OtherID = t.DocOtherId
 LEFT JOIN Document d on d.ID = de.DocID

这将为跟踪表中的 EACH 记录返回 ONE 记录,包括 DocOtherId 中具有空字符串的记录。

【讨论】:

  • 我不需要返回 NULL 或空字符串。我需要将空字符串视为 JOIN 操作的空字符串。
  • @StephenCollins 我实际上会同意 Joel 的观点。即使您的原始表中有空值,您也需要一个左连接才能让跟踪中的所有内容都显示出来。如果您使用内部联接,它将基本上摆脱跟踪表中的任何空白字符串/空值。你能发布一个你想要得到的结果的例子吗?
  • @Stephen Collins:这与您刚刚发布的预期结果有何不同? Joel 认为您需要外部连接,Conrad 认为是,我需要,但您一直告诉我们不需要。然后您说您不希望结果中有任何 NULLS,但随后您发布了一个包含 NULLS 的所需结果列表。什么鬼?
【解决方案2】:

如果您的数据不是很大,您可以尝试使用临时表来剥离您的数据,然后对经过优化的数据进行联接。例如:

编辑:回复下面的第一条评论

这第一部分将为您提供您需要的临时表,没有空字符串,而是在空字符串所在的位置有空值

Select ID, 
     case when DocOtherId = '' then null else DocOtherId end,
     AccessTime
     into #tracking
From Tracking

然后使用此临时表以与您最初相同的方式连接回您的数据。在此示例中,您只需将#tracking 替换为 Tracking,尽管您可以随意调用临时表。

SELECT 
    Tracking.ID, Document.Name 
FROM 
    #tracking
INNER JOIN 
    DocumentExt ON DocumentExt.OtherId = Tracking.DocOtherId
INNER JOIN 
    Document ON Document.ID = DocumentExt.DocId

【讨论】:

  • 只需添加WHERE Tracking.DocOtherId <> '' 即可完成相同的操作。这种方法的问题在于它从查询中排除了ID 为2 的Tracking 记录(或DocOtherId 为空字符串的任何其他记录。)我需要包含Tracking 中的所有记录.
  • 嗯,另一种选择是不排除所有空白字符串,而是将其转换为临时表中的可为空字段。我将针对这两种情况更新我的回复,以便您了解我的意思(在答案中查看代码比查看评论更容易)。
猜你喜欢
  • 2013-07-30
  • 1970-01-01
  • 2015-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-27
  • 1970-01-01
  • 2020-04-03
相关资源
最近更新 更多