【问题标题】:MySQL Select where multiple rows in joined tableMySQL选择连接表中的多行
【发布时间】:2017-03-28 11:31:55
【问题描述】:

假设我有两张桌子,例如:

+-----------------------------+
|a                            |
+------+-----------+----------+
| a_id | firstname | lastname |
+------+-----------+----------+
|    1 | Tom       | Cruise   |
|    3 | Matt      | Damon    |
|    4 | Ben       | Affleck  |
|    8 | Ryan      | Gosling  |
+------+-----------+----------+

+-----------------------+
| b                     |
+------+------+---------+
| b_id | a_id | b_value |
+------+------+---------+
|    2 |    3 | one     |
|    1 |    3 | two     |
|    4 |    8 | three   |
|    8 |    1 | four    |
+------+------+---------+

我希望能够从a 中获取满足b 中的一组条件的行,其中可能有多行。

例如:

  • 获取具有b_value 一个和其他b_value 两个的任何人的名字和姓氏。在示例数据中,这仅指马特达蒙。

  • 获取具有三个b_value 或四个b_value 的任何人的名字和姓氏。在示例数据中,Ryan Gosling 的 b_value 为 3,Tom Cruise 的 b_value 为 4,因此 Ryan Gosling 和 Tom Cruise 都会返回。

  • 获取以下任何人的名字和姓氏:

    • 一个b_value 和另一个b_value 两个。
    • b_value 三个。

在示例数据中,Matt Damon 满足第一个条件,Ryan Gosling 满足第二个条件,因此都返回。

当这些条件很复杂,结合了多个和/或条件,并且长度可变时,困难就来了。

我目前的尝试(在这种情况下具体解决示例 3)是在 HAVING 中使用聚合函数:

SELECT firstname, lastname 
FROM temp_a INNER JOIN temp_b ON temp_a.a_id = temp_b.a_id 
GROUP BY temp_a.a_id 
HAVING (SUM(b_value="one") AND SUM(b_value="two")) OR SUM(b_value="three");

哪个得到了正确的行,但是这种方法对我来说有两个直接的问题:

  • SUM 似乎错误且复杂。我必须找到每个条件并确保每个条件都包含在SUM 中,其中可能有很多。
  • b_value 已编入索引,b 有很多行。 HAVING 子句不使用索引,因此查询速度明显变慢。

我是否应该采用不同的方法来执行此类查询,请记住,我并没有真正直接控制用于过滤的条件。

【问题讨论】:

  • 能否请您添加最终结果应该是什么。目前还不清楚。同样在您的第二个示例中,您说 b_value 是三或四,它应该返回 Ryan Gosling 和 Tom Cruise。如果您在第一部分中指定 OR,那么我想您不能在第二部分中使用 AND,除非我遗漏了什么。
  • @Kevin 我已经稍微更新了这个问题,希望能澄清一些事情
  • 在您的示例中,您指定了 3 个示例,我想还有其他可能的过滤方式吗?我几乎会建议使用动态 sql。我也猜过滤器是由用户制作的?也许你可以在应用程序中准备你的 where 子句,传递一个变量并将它放在你的动态 sql 语句中执行。
  • @Kevin 是的,没错。抱歉,我以为这是暗示的-我的错。那条动态 sql 语句会是什么样子?

标签: mysql sql group-by inner-join having


【解决方案1】:
drop table if exists a,b;
create table a( a_id int, firstname varchar(20), lastname varchar(20));
insert into a values
(    1 , 'Tom'       , 'Cruise' ),  
(    3 , 'Matt'      , 'Damon'  ),  
(    3 , 'Ben'       , 'Affleck'),  
(    8 , 'Ryan'      , 'Gosling');  

create table b( b_id int, a_id int, b_value varchar(20));
insert into b values
(    2 ,    3 , 'one'  ),   
(    1 ,    3 , 'two'  ),   
(    4 ,    8 , 'three'),   
(    8 ,    1 , 'four' );


select firstname, lastname,s.vals
from a
join
(
select b.a_id, group_concat(b_value) vals from b group by b.a_id
) s on a.a_id = s.a_id

where s.vals in ('one,two') or s.vals in ('three')

+-----------+----------+---------+
| firstname | lastname | vals    |
+-----------+----------+---------+
| Matt      | Damon    | one,two |
| Ben       | Affleck  | one,two |
| Ryan      | Gosling  | three   |
+-----------+----------+---------+
3 rows in set (0.02 sec)

【讨论】:

  • 虽然s.vals in ('one,two') 适用于示例查询和给出的数据,但不适用于其他查询。我想数据库将包含 s.val 为 'a,b,c' 而过滤器为 'a,c' 的情况。
【解决方案2】:
SELECT rows from a where it has b_value of both one and two would return Matt Damon.

如果你想要上面的结果,那么表“a”“id”列应该有唯一的记录, 表 a 和 b 都应该有主键。

或者您可以尝试以下查询,但它会返回“matt daemon”的双记录

select firstname,lastname from a left join b on a.a_id=b.a_id ;

【讨论】:

    【解决方案3】:

    在动态 SQL 中,它会是这样的,首先我建议在您的应用程序中创建一个变量,您可以根据过滤器的选择来填充它

     - var exampleOne = "(b_value = 'one') OR (b_value = 'two')";
     - var exampleTwo = "(b_value = 'three') OR (b_value = 'four')";
     - var exampleThree = "(b_value = 'one' or b_value = 'two') OR (b_value = 'three')"
    

    然后你可以创建一个带有一个参数的存储过程,这里你传入你的变量

    CREATE PROCEDURE `spNames`(IN whereFilter VARCHAR(255))
    BEGIN
      SET @SqlText = CONCAT('SELECT DISTINCT a.firstname, a.lastname 
                             FROM @a AS a INNER JOIN @b AS b ON a.a_id=b.a_id
                             WHERE ', whereFilter);
      PREPARE stmt FROM @SqlText;
      EXECUTE stmt;
      DEALLOCATE PREPARE stmt
    END
    

    然后你可以从你的代码中调用这个存储过程并传入变量

    CALL spNames (exampleOne);
    

    【讨论】:

    • 不幸的是,这里的示例三不太正确。如果 b 中有另一行,比如值 (3, 1, "one"),那么 Tom Cruise 将被错误地返回,因为有一个 b_value,但没有两个 b_value - 但在变量中 (b_value = 'one' or b_value = 'two')组件会得到满足。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-23
    • 1970-01-01
    • 2012-02-10
    • 1970-01-01
    • 2015-07-17
    • 2021-06-15
    • 2018-08-01
    相关资源
    最近更新 更多