Mysql 使用另一个字段的值作为左联接的默认值

Mysql 使用另一个字段的值作为左联接的默认值,mysql,sql,left-join,default-value,Mysql,Sql,Left Join,Default Value,我有以下三个表格: users | id | name | address | |----+-------+----------+ | 1 | user1 | address1 | | 2 | user2 | address2 | locations | id | user_id | name | |----+---------+-----------+ | 1 | 1 | location1 | | 2 | 1 | location2 | | 3

我有以下三个表格:

users
| id | name  | address  |
|----+-------+----------+
| 1  | user1 | address1 |
| 2  | user2 | address2 |

locations
| id | user_id | name      |
|----+---------+-----------+
| 1  | 1       | location1 |
| 2  | 1       | location2 |
| 3  | 2       | location3 |

orders
| id | user_id | from_id | to_id |
|----+---------+---------+-------+
| 1  | 1       | 1       | 2     |
| 2  | 1       | 1       | 0     |
from_id或to_id可以有0,这意味着在本例中使用了用户的地址

在这些表上执行“朴素”联接:

SELECT u.name uname, fl.location flocation, tl.location tlocation
FROM   users u, orders o, locations fl, locations tl
WHERE  u.id = o.user_id
  AND  o.from_id = fl.id
  AND  o.to_id = tl.id
不显示包含0的记录:

user1 | location1 | location2
我想看到的是以下数据:

user1 | location1 | location2
user1 | location1 | address1

使用mysql,有没有办法扩展连接以显示这样的结果?

以下SQL应该可以做到这一点:

SELECT u.name uname, IF(fl.id, fl.location, u.address) AS flocation, IF(tl.id, tl.location, u.address) AS tlocation
FROM users u
    INNER JOIN orders o ON u.id = o.user_id
    LEFT JOIN locations fl ON o.from_id = fl.id
    LEFT JOIN locations tl ON o.to_id = tl.id;
如果我可以提出一个建议,我建议您不要使用WHERE子句作为连接表的方式

SELECT u.name uname, fl.location flocation, tl.location tlocation
FROM users u 
left join orders o
          on u.id = o.user_id
left join locations fl
          on o.from_id = fl.id
left join locations tl
          on o.to_id = tl.id
这将为您提供所有用户的数据


正如蒂姆·伯奇(Tim Burch)所建议的,最好使用ANSI SQL语法。

要使用用户地址作为发件人和收件人名称的默认值,您可以进行左连接,并使用
合并
将空值替换为地址

SELECT 
  u.name uname, 
  COALESCE(fl.name, u.address) flocation, 
  COALESCE(tl.name, u.address) tlocation
FROM users u
JOIN orders o ON u.id = o.user_id
LEFT JOIN locations fl ON o.from_id = fl.id
LEFT JOIN locations tl ON o.to_id = tl.id

.

我建议放弃join操作的“逗号”语法;使用JOIN关键字,将连接谓词移动到
ON
子句,而不是
WHERE
子句

要从返回的
订单中获取所有行,无论其他表中是否有“匹配”行,都可以使用外部联接操作

在选择列表中,您可以测试
位置
中的
位置
列是否为空,并使用
合并
功能从
用户
表返回
地址

SELECT u.name uname
     , COALESCE(fl.location,u.address) flocation
     , COALESCE(tl.location,u.address) tlocation
  FROM orders o
  LEFT
  JOIN users u
    ON u.id = o.user_id
  LEFT
  JOIN locations fl
    ON fl.id = o.from_id
  LEFT
  JOIN locations tl
    ON tl.id = o.to_id
COALESCE(x,y)
函数本质上是“当x为NULL时,则y为x结束”的简写形式

这里有一个小例子,如果在
locations
表中找到了匹配的行,但
location
列实际上包含空值,该怎么办

为了更精确地匹配您的规范,您可以在
订单
表的
To\u id
列中测试值“
0

SELECT u.name uname
     , IF(o.from_id = 0, u.address, fl.location) flocation
     , IF(o.to_id   = 0, u.address, tl.location) tlocation
  FROM orders o
  LEFT
  JOIN users u
    ON u.id = o.user_id
  LEFT
  JOIN locations fl
    ON fl.id = o.from_id
  LEFT
  JOIN locations tl
    ON tl.id = o.to_id
但这里有一个角落的案例;如果
位置中有一行id值为“
0
”该怎么办

标准模式是通过检查一列来测试行是否从
位置返回,该列在找到匹配项时保证不为空,在没有找到匹配项时保证为空:

SELECT u.name uname
     , IF(fl.id IS NULL, u.address, fl.location) flocation
     , IF(tl.id IS NULL, u.address, tl.location) tlocation
  FROM orders o
  LEFT
  JOIN users u
    ON u.id = o.user_id
  LEFT
  JOIN locations fl
    ON fl.id = o.from_id
  LEFT
  JOIN locations tl
    ON tl.id = o.to_id
对于“正常”情况,所有这三个查询将返回相同的结果;他们只是在处理“角落案件”的方式上有所不同

总之:

  • 使用
    LEFT[OUTER]JOIN
    操作和
    orders
    作为驱动表
  • 使用“选择”列表中的条件表达式确定要返回的列值