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

Mbe*_*bwo 5 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 |
+--------+-------------+------+
Run Code Online (Sandbox Code Playgroud)

表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 |
+--------+-------------+------+
Run Code Online (Sandbox Code Playgroud)

期望的输出:

表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 |
+--------+-------------+------+
Run Code Online (Sandbox Code Playgroud)

请注意以下条件:

  1. 我只想使用PrefixDestination表的列A.
  2. 我只想使用rate表格列B.
  3. 如果PrefixA= Prefix表B,则复制rate
  4. 如果prefixA在表中没有找到B,然后复制rateprefixB是开头prefixA(只返回最长字符串的值).
  5. 如果prefix表A不在表B中,那么复制表B中的前缀速率,其中表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))
Run Code Online (Sandbox Code Playgroud)

Ada*_*331 2

免责声明:以下答案很长,因此我首先会说最后的查询会产生与您想要的结果匹配的结果来吸引您的注意力。这是SQL Fiddle


嗯,在我看来,解决这个问题的最佳方法是一次只满足一个要求。

我只想使用表 A 的前缀和目标列。

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

SELECT a.prefix, a.destination
FROM tableA a;
Run Code Online (Sandbox Code Playgroud)

我只想使用表 B 的费率列。

我们也可以轻松添加它。请注意,我现在所拥有的只会创建一个笛卡尔积,但随着我们添加更多要求,它会变得理直气壮:

SELECT a.prefix, a.destination, b.rate
FROM tableA a, tableB b;
Run Code Online (Sandbox Code Playgroud)

如果表A的前缀=表B的前缀,则复制率。

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

SELECT a.prefix, a.destination, 
   (SELECT b.rate FROM tableB b WHERE b.prefix = a.prefix) AS rate
FROM tableA a;
Run Code Online (Sandbox Code Playgroud)

如果在B表中没有找到A表的前缀,则复制B表中以A表的前缀开头的前缀的比率(仅返回最长字符串的值)。

为此,我暂时离开并编写了从 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;
Run Code Online (Sandbox Code Playgroud)

现在,您可以采用该选择查询及其上方的查询,并使用该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;
Run Code Online (Sandbox Code Playgroud)

如果表 A 的前缀不在表 B 中,则复制表 B 中前缀的速率,其中表 A 的前缀以表 B 中的前缀开头。(仅返回最长字符串的值)。

好吧,我们可以做与上一个相同的事情,除了操作子查询来检查相反的表。另外,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;
Run Code Online (Sandbox Code Playgroud)

当将我的结果与你的结果进行比较时,我注意到上面没有考虑到 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;
Run Code Online (Sandbox Code Playgroud)

其背后的逻辑类似于以下:

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