php - 从具有多个要求的两个表中选择值,但只使用其中一个

标签 php mysql database join

我想根据条件连接两个表。

表 A:

+--------+-------------+------+
| prefix | Destination | rate |
+----------------------+------+
| 56     |  Monn       |  25  |
| 5602   |  Monn M1    |  23  |
| 5604   |  Monn M3    |  44  |
| 5607   |  Monn M1    |  23  |
| 5625   |  Monn M2    |  22  |
| 23     |  Xpia       |  0.3 |
| 238    |  Xpia F3    |  0.9 |
+--------+-------------+------+

表 B:

+--------+-------------+------+
| prefix | Destination | rate |
+----------------------+------+
| 56     |  Monn       |  75  |
| 560    |  Monn M1x   |  49  |
| 5607   |  Monn M1    |  03  |
| 56254  |  Monn M2    |  9.5 |
| 23     |  Xpia       |  1.3 |
| 2301   |  Xpia T1    |  2.4 |
| 2302   |  Xpia T2    |  3.5 |
| 2381   |  Xpia F     |  8.9 |
+--------+-------------+------+

期望的输出:

表 C:

+--------+-------------+------+
| prefix | Destination | rate |
+----------------------+------+
| 56     |  Monn       |  75  |
| 5602   |  Monn M1    |  49  |
| 5604   |  Monn M3    |  49  |
| 5607   |  Monn M1    |  03  |
| 5625   |  Monn M2    |  9.5 |
| 23     |  Xpia       |  1.3 |
| 238    |  Xpia F3    |  8.9 |
+--------+-------------+------+

注意以下条件:

  1. 我只想使用表 APrefixDestination 列。
  2. 我只想使用表 Brate 列。
  3. 如果表APrefix = 表B的Prefix,则复制rate
  4. 如果在表B中找不到表Aprefix,则复制rateB 中以 Aprefix 开头的前缀(仅返回最长字符串的值)。
  5. 如果表 A 的 prefix 不在表 B 中,则表 B 中前缀的复制率(其中表 A 的前缀为 a)从表 B 中的前缀开始。(仅返回最长字符串的值)

如果满足条件 4 和 5,则只返回最长前缀的速率。

我有这个查询,但它不工作。我意识到我需要一个更复杂的查询。

INSERT INTO Table C(prefix,destination, rate) 
       SELECT Table A.prefix, Table A.destination, Table B.rate 
       FROM Table A, Table B 
       WHERE Table B.prefix= SUBSTRING(Table A.prefix, 1, length(Table B.prefix))

最佳答案

免责声明:下面的回答很长,所以我首先要引起你的注意,最后的查询会产生与你想要的结果相匹配的结果。这是 SQL Fiddle .


好吧,在我看来,解决这个问题的最佳方式是一次满足一项要求。

I want to use only Prefix and Destination columns of table A.

这只是一个简单的选择语句:

SELECT a.prefix, a.destination
FROM tableA a;

I want to use only rate column of table B.

我们也可以很容易地添加它。请注意,我现在所拥有的只是创建一个笛卡尔积,但随着我们添加更多要求,它会理顺:

SELECT a.prefix, a.destination, b.rate
FROM tableA a, tableB b;

If Prefix of table A = Prefix of table B, then copy rate.

我将上面的内容更改为在 select 子句中使用相关子查询,如果它具有与 tableA 的前缀匹配的前缀,则提取速率。这将为任何不匹配的值设置 null(暂时):

SELECT a.prefix, a.destination, 
   (SELECT b.rate FROM tableB b WHERE b.prefix = a.prefix) AS rate
FROM tableA a;

If prefix of table A is not found in table B, then copy rate of prefix in table B that starts with prefix of table A (return only the longest string's value).

为此,我暂时离开并编写了从 B 获取费率的查询,其中前缀以表 A 的前缀开头,忘记了之前的条件。我像你一样使用了 substring 函数,但按长度降序排列以获得最大的一个:

SELECT a.prefix, a.destination, 
   (SELECT b.rate FROM tableB b WHERE b.prefix = 
   SUBSTRING(a.prefix, 1, LENGTH(b.prefix)) ORDER BY LENGTH(b.prefix) DESC LIMIT 1) AS rate
FROM tableA a;

现在,您可以采用该选择查询和上面的查询,并使用 COALESCE 函数获取第一个非空值。因此,第 3 步中返回空值的那些将被第 4 步中的替换(如果仍然存在非空值):

SELECT a.prefix, a.destination, COALESCE(
  (SELECT b.rate FROM tableB b WHERE b.prefix = a.prefix),
  (SELECT b.rate FROM tableB b WHERE b.prefix = SUBSTRING(a.prefix, 1, LENGTH(b.prefix)) ORDER BY LENGTH(b.prefix) DESC LIMIT 1)) AS rate  
FROM tableA a;

If prefix of table A is not in table B then copy rate of prefix in table B where prefix of table A starts with a prefix in table B. (return only longest string's value).

好吧,我们可以做与上一个相同的事情,除了操纵子查询来检查相反的表。另外,COALESCE函数没有参数限制,所以我们可以把它作为第三个参数添加进去。如果第一个返回 null,它将尝试第二个。如果返回 null,它将尝试第三个。这是最终查询:

SELECT a.prefix, a.destination, COALESCE(
  (SELECT b.rate FROM tableB b WHERE b.prefix = a.prefix),
  (SELECT b.rate FROM tableB b WHERE b.prefix = SUBSTRING(a.prefix, 1, LENGTH(b.prefix)) ORDER BY LENGTH(b.prefix) DESC LIMIT 1),
  (SELECT b.rate FROM tableB b WHERE a.prefix = SUBSTRING(b.prefix, 1, LENGTH(a.prefix)) ORDER BY LENGTH(a.prefix) DESC LIMIT 1)) AS rate
FROM tableA a;

将我的结果与您的结果进行比较时,我注意到上面没有考虑同时满足 4 和 5,在这种情况下我们希望采用最长的前缀。虽然可能有更简洁的方式来编写它,但我只是编写了以下 case 语句:

SELECT a.prefix, a.destination,
  CASE WHEN 
    (SELECT b.rate FROM tableB b WHERE b.prefix = a.prefix) IS NOT NULL
  THEN
    (SELECT b.rate FROM tableB b WHERE b.prefix = a.prefix)
  ELSE
    CASE WHEN
      ((SELECT b.rate FROM tableB b WHERE b.prefix = SUBSTRING(a.prefix, 1, LENGTH(b.prefix)) ORDER BY LENGTH(b.prefix) DESC LIMIT 1) IS NOT NULL)
      AND
      ((SELECT b.rate FROM tableB b WHERE a.prefix = SUBSTRING(b.prefix, 1, LENGTH(a.prefix)) ORDER BY LENGTH(a.prefix) DESC LIMIT 1) IS NOT NULL)
    THEN
      CASE WHEN 
        (SELECT LENGTH(b.prefix) FROM tableB b WHERE b.prefix = SUBSTRING(a.prefix, 1, LENGTH(b.prefix)) ORDER BY LENGTH(b.prefix) DESC LIMIT 1)
        >
        (SELECT LENGTH(a.prefix) FROM tableB b WHERE a.prefix = SUBSTRING(b.prefix, 1, LENGTH(a.prefix)) ORDER BY LENGTH(a.prefix) DESC LIMIT 1)
      THEN
        (SELECT b.rate FROM tableB b WHERE b.prefix = SUBSTRING(a.prefix, 1, LENGTH(b.prefix)) ORDER BY LENGTH(b.prefix) DESC LIMIT 1)
      ELSE
        (SELECT b.rate FROM tableB b WHERE a.prefix = SUBSTRING(b.prefix, 1, LENGTH(a.prefix)) ORDER BY LENGTH(a.prefix) DESC LIMIT 1)
      END
    ELSE
      COALESCE(
        (SELECT b.rate FROM tableB b WHERE b.prefix = SUBSTRING(a.prefix, 1, LENGTH(b.prefix)) ORDER BY LENGTH(b.prefix) DESC LIMIT 1),
        (SELECT b.rate FROM tableB b WHERE a.prefix = SUBSTRING(b.prefix, 1, LENGTH(a.prefix)) ORDER BY LENGTH(a.prefix) DESC LIMIT 1))
    END
  END AS rate
FROM tableA a;

其背后的逻辑有点像下面这样:

  • 检查是否满足条件 3。
    • 如果是,则使用该值。
  • 检查是否满足条件 4 5。
    • 如果是,检查条件 4 的前缀是否更长。
      • 如果是,就使用它。如果不是,则使用条件 5 中的前缀。
    • 如果不是,则从条件 4 和 5 中选择第一个非空条件。

关于php - 从具有多个要求的两个表中选择值,但只使用其中一个,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27529792/

相关文章:

php - MYSQL Select Query 我想要两行基于where条件

php - 警告 : mysql_fetch_array(): supplied argument is not a valid MySQL result

mysql查询匹配字符串数组与数据库

android - 每个表都需要一个 CursorAdapter 吗?

database - 扇形陷阱和裂隙陷阱 - 数据库

php - 更新 SQL,如果不为空则只更新图像

php - PHP 中的 2 个简单 SQL 查询抛出错误

php - MySQL 时区查询

php - laravel artisan 命令 cron 作业在 ubuntu 服务器上不起作用

mysql - 做 MySQL 对象层的更好方法