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

时间:2014-12-17 16:06:12

标签: 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. 我只想使用表Prefix的{​​{1}}和Destination列。
  2. 我只想使用表A的{​​{1}}列。
  3. 如果表格rate = B的{​​{1}},请复制Prefix
  4. 如果在表A中找不到表Prefix的{​​{1}},请复制表rateprefix的{​​{1}}表A的{​​{1}}(仅返回最长字符串的值)。
  5. 如果表A中的B不在表B中,则复制表B中的前缀速率,其中表A的前缀是以表B中的前缀开头。(仅返回最长字符串的值) )
  6. 如果满足条件4和5,则仅返回最长前缀的速率。

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

    rate

4 个答案:

答案 0 :(得分:2)

免责声明:以下答案很长,所以我首先会引起你的注意,说最后的查询会产生符合你想要的结果。这是SQL Fiddle


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

  

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

这只是一个简单的选择声明:

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

  

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

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

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

  

如果表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;

  

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

为此,我离开了一秒钟并写了一个查询,从B获取前缀从表A开始的B的速率,忘记了先前的条件。我像你一样使用了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;

  

如果表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;

当我将结果与你的结果进行比较时,我注意到上面没有考虑到4和5都满足,在这种情况下我们想要采用最长的前缀。虽然可能有更简洁的方法来编写它,但我只写了以下案例陈述:

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 AND 5。
    • 如果是,请检查条件4中的前缀是否更长。
      • 如果是,请使用它。如果不是,请使用条件5中的前缀。
    • 如果不是,请从条件4和5中选择第一个非空条件。

答案 1 :(得分:1)

我讨厌子查询,但这应该可以解决问题:

INSERT INTO TableC (prefix, destination, rate)
SELECT
    TableA.prefix,
    TableA.destination,
    (SELECT TableB.rate
        FROM TableB
        WHERE TableB.prefix = SUBSTRING(TableA.prefix, 1, LENGTH(TableB.prefix))
        OR TableA.prefix = SUBSTRING(TableB.prefix, 1, LENGTH(TableA.prefix))
        ORDER BY LENGTH(TableB.prefix) DESC LIMIT 1)
FROM TableA;

答案 2 :(得分:0)

您可以加入TABLE_B两次并在IFNULL子句中使用花哨的SELECT

INSERT INTO TABLE_C (prefix,destination, rate) 
    SELECT TABLE_A.prefix, TABLE_A.destination, IFNULL(B1.rate, B2.rate) 
    FROM TABLE_A
    LEFT JOIN TABLE_B AS B1 ON B1.prefix = TABLE_A.prefix
    LEFT JOIN (
        SELECT * FROM TABLE_B
        ORDER BY LENGTH(prefix) DESC
    ) AS B2 ON B2.prefix = SUBSTRING(TABLE_A.prefix, 1, LENGTH(B2.prefix))
    GROUP BY TABLE_A.prefix;

IFNULL返回第一个参数(如果存在),否则返回第二个参数。根据我的经验,在IF中设置类似SELECT的条件比在JOIN中放置一个更好。

修改

根据您的表C,看起来您还需要第6个条件或5b,其中您将截断表B的前缀而不是表A的前缀。这将永远不会起作用,因为你有可能采用任何一种方式的值(表A,其中prefix = 238可以匹配B的23或B的2381)。

但是,如果你消除了这种“5b”状态,这个解决方案对你有用。

答案 3 :(得分:0)

以下是使用left joincase-when

执行此操作的方法
insert into tableC (prefix,Destination,rate) 
select
t1.prefix,
t1.Destination,
case 
  when t2.prefix is null 
    then (
      select rate from tableB
      where tableB.prefix = SUBSTRING(t1.prefix, 1, LENGTH(tableB.prefix))
      order by SUBSTRING(t1.prefix, 1, LENGTH(tableB.prefix)) desc limit 1
    )
  else t2.rate
end as rate
from tableA t1
left join tableB t2 on t1.prefix = t2.prefix
where t1.prefix is not null;

<强> DEMO

现在使用你提到的逻辑 e.g prefixes 5602 and 5604 are not in Table B. But Table B has 56 and 560 that are substrings of 5602 and 5604. But since 560 is longer than 56, return the rate of 560 instead

238不在第二个表中,23是第二个表中它的最大子字符串,该行来自该行。