【问题标题】:Mysql query with multiple left joins from tables that are subsets of the "from" table not working as expected来自作为“来自”表子集的表的多个左连接的 Mysql 查询未按预期工作
【发布时间】:2015-02-21 02:57:54
【问题描述】:

为入站呼叫创建一个数据库,其中包含每种呼叫类型的数据。我已经定义了一个带有“records”表、“sales”表和“accounts_receivable”表的数据库。 “sales”和“accounts_receivable”表本质上是“records”表的子集。意思是,对于“sales”和“accounts_receivable”表的每一行(唯一),“records”表中都有相应的行。

我的“记录”表有值:

id (BIGINT)
timestamp (TIMESTAMP)
rep_id (INT)
notes (VARCHAR)

我的“销售”表有值:

local_record_id (BIGINT)
record_id (BIGINT)
amount (VARCHAR)
bottles_sold (VARCHAR)
record_type (VARCHAR) default 'sales'

我的“accounts_receivable”表有值:

local_record_id (BIGINT)
record_id (BIGINT)
amount (VARCHAR)
bottles_sold (VARCHAR)
record_type (VARCHAR) default 'ar'

我正在尝试提取整个数据库中的所有记录以及每条记录的适用数据。为此,我认为以“记录”表开头的 LEFT JOIN 可以正常工作,但由于某种原因它不是。这是我的查询:

SELECT *
FROM $table_name_records
LEFT JOIN $table_name_sales ON ($table_name_records.id = $table_name_sales.record_id)
LEFT JOIN $table_name_ar ON ($table_name_records.id = $table_name_ar.record_id)

这将返回一组结果,其中列名相似的子表中的数据被清空。

输出示例(对应于我的数据库的“销售”表中的一行/条目):

Array (
    [id] => 1
    [timestamp] => 2014-12-17 13:11:07
    [rep_id] => 37
    [notes] => Some notes for you.
    [local_record_id] =>
    [record_id] =>
    [amount] =>
    [bottles_sold] =>
    [record_type] =>
)

我已验证我的数据库的每个子表的每个列中都有数据,所以我不明白为什么这些值返回为空白。如果我只使用一个左连接查询:

SELECT *
FROM $table_name_records
LEFT JOIN $table_name_sales ON ($table_name_records.id = $table_name_sales.record_id)

我得到了我期望看到的:

Array (
    [id] => 1
    [timestamp] => 2014-12-17 13:11:07
    [rep_id] => 37
    [notes] => Some notes for you.
    [local_record_id] => 14
    [record_id] => 1
    [amount] => 45.45
    [bottles_sold] => Multiple
    [record_type] => sales
)

如果我只加入“应收帐款”表,工作方式与我预期的相同:

SELECT *
FROM $table_name_records
LEFT JOIN $table_name_ar ON ($table_name_records.id = $table_name_ar.record_id)

Array (
    [id] => 1
    [timestamp] => 2014-12-17 13:12:16
    [rep_id] => 37
    [notes] => Some notes.
    [local_record_id] => 6
    [record_id] => 2
    [amount] => 50.89
    [bottles_sold] => Single
    [record_type] => ar
)

我查看了很棒的图形 SQL 连接文档 MySQL Joins,那里的 LEFT JOIN 看起来正是我想要的:表 A(记录)中的每一件事与表 B(销售)中的数据,然后也是第二个表 B (accounts_receivable) 附加到相应的行(根据 ON 语句)。

我做错了什么,这没有像我期望的那样响应?

我只需要“销售”记录的查询通常是在另一个方向构建的。因此:

SELECT *
FROM $table_name_sales
LEFT JOIN $table_name_records ON ($table_name_records.id = $table_name_sales.record_id)

所以,我尝试从那个方向构建我的查询,然后通过以下方式加入它们:

SELECT *
FROM $table_name_sales
LEFT JOIN $table_name_records ON ($table_name_records.id = $table_name_sales.record_id)
UNION
SELECT *
FROM $table_name_ar
LEFT JOIN $table_name_records ON ($table_name_records.id = $table_name_ar.record_id)

这个查询完全符合我的预期:

Array (
    [id] => 1
    [timestamp] => 2014-12-17 13:11:07
    [rep_id] => 37
    [notes] => Some notes for you.
    [local_record_id] => 14
    [record_id] => 1
    [amount] => 45.45
    [bottles_sold] => Multiple
    [record_type] => sales
)

但在我看来,当我有大量记录和更多表时(对应于不同类型的入站呼叫——例如:信息请求、订单问题等),以这种方式构建查询可能会变得昂贵)。不过,我对 MySQL 没有太多经验。

【问题讨论】:

  • JOIN 查询(实际上是任何生产代码)中不建议使用SELECT *。正如您所发现的,类似命名的列将无法正确返回。您必须指定每个(实际上,只指定您需要的列)并为类似名称的列提供别名:SELECT $table_name_records.id AS records_id, $table_name_records.notes AS records_notes, $table_accounts_receivable.bottles_sold AS ar_bottles_sold, $table_sales.bottles_sold AS sales_bottles_sold...
  • 执行UNION 会产生与JOIN 截然不同的查询和结果集。 JOIN 似乎是您在这里真正需要的。
  • @MichaelB:所以,我尝试按照您的建议进行操作,并且得到了相同的结果。只有这一次我注意到最终查询结果中的所有“ar”记录看起来都很好。我加了:$table_name_records.id AS id, $table_name_records.timestamp AS timestamp, $table_name_records.rep_id AS rep_id, $table_name_records.notes AS notes, $table_name_sales.amount AS amount, $table_name_sales.bottles_sold AS bottle_sold, $table_name_sales.record_type AS record_type, $table_name_ar.amount AS amount, $table_name_ar.bottles_sold AS bottle_sold, $table_name_ar.record_type AS record_type 到查询。
  • 它们都必须是不同的别名。您同时拥有$table_name_sales.bottles_sold AS bottles_sold$table_name_ar.bottles_sold AS bottles_sold。使用原始表的前缀区分 $table_name_sales.bottles_sold AS sales_bottles_sold$table_name_ar.bottles_sold AS ar_bottles_sold 等别名,因此它们都不是 bottles_sold。对 all 类似命名的列执行此操作。
  • 如果您需要不同地使用sales和accounts_receivable,您应该分别查询它们。与它们进行 JOIN 会使事情变得混乱,除非这些行真正相互关联,而不是仅仅与 records 独立相关

标签: mysql left-join


【解决方案1】:

看起来您的 salesaccounts_receivable 表具有并行结构,并且每个表中的行都与您的 records 表相关,但彼此不相关。

@MichaelBerkowski 指出了尝试从 SELECT * 解释结果集的危险。

在我看来,您需要以下类型的查询。

SELECT r.id AS record_id, r.timestamp, r.rep_id, r.notes AS record_notes,
       d.record_type, d.amount, d.bottles_sold
  FROM records AS r
  LEFT JOIN (
         SELECT record_id, record_type, amount, bottles_sold FROM sales
          UNION ALL
         SELECT record_id, record_type, amount, bottles_sold FROM accounts_receivable
       ) AS d ON d.record_id = r.id
   ORDER BY r.rep_id, r.id, r.timestamp, d.record_type DESC

这将按顺序显示每个代表的活动,交错“销售”和“ar”记录。

如果您分别加入 sales 和 accounts_receivable 表,您将获得不需要的组合爆炸。执行 UNION ALL 可以防止这种情况发生。

请注意,您可以将单个物理表用于销售和 ar 数据。

【讨论】:

  • 所以实际的表格比我展示的要复杂得多。我为这个问题做了一个降低复杂性的案例。我实际上有 5 个不同的子集表,其中一个有 6 个子集表。我以这种方式设置以节省数据库空间,因为很少有子集表有太多重叠(sales 和 ar 是最多的两个)。我试图合并这些表的原因是因为我正在构建一个记录管理页面,并且想一次性打印出给定查询的所有结果,但我不明白为什么查询结果很奇怪。
  • 看起来这种设置可以工作。暂时将此标记为答案,一旦我有机会尝试并实施它,将返回更新内容。现在,我只是将所有查询保留在给定的子集表中。感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-17
  • 2014-11-16
  • 1970-01-01
  • 1970-01-01
  • 2017-03-28
  • 1970-01-01
相关资源
最近更新 更多