简体   繁体   English

MySQL选择连接表中多行的位置

[英]MySQL Select where multiple rows in joined table

Let's say I have two tables, something like: 假设我有两张桌子,例如:

+-----------------------------+
|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    |
+------+------+---------+

I would like to be able to SELECT rows from a which meet a set of criteria from b , where there might be multiple rows. 我希望能够SELECT从行a满足了一组从标准b ,其中可能有多个行。

For example: 例如:

  • Get the first and last name(s) of anyone who has a b_value of one, and other b_value of two. 获取b_value为1,其他b_value为2的任何人的b_valueb_value In the example data, this refers only to Matt Damon. 在示例数据中,这仅指Matt Damon。

  • Get the first and last name(s) of anyone who has a b_value of three, or a b_value of four. 获取b_value为3或b_value为4的任何人的名字和姓氏。 In the example data, Ryan Gosling has a b_value of three, and Tom Cruise has a b_value of four, so both Ryan Gosling and Tom Cruise would be returned. 在示例数据中,Ryan Gosling的b_value为3,而Tom Cruise的b_value值为4,因此Ryan Gosling和Tom Cruise都将被退回。

  • Get the first and last name(s) of anyone that has either: 获取具有以下任何一个的任何人的名字和姓氏:

    • A b_value of one and another b_value of two. 一个b_value和另一个b_value为2。
    • A b_value of three. b_value为三。

In the example data, Matt Damon meets the first condition, and Ryan Gosling meets the second condition, so both are returned. 在示例数据中,Matt Damon遇到了第一个条件,而Ryan Gosling遇到了第二个条件,因此两者都返回了。

The difficulty comes from when these conditions are complex, combining multiple and/or conditions, and a variable in length. 困难来自于这些条件复杂,组合多个和/或条件以及长度变量。

My current attempt (solving specifically example 3 in this case), is using aggregate functions in the HAVING : 我当前的尝试(在这种情况下具体解决了示例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");

Which gets the correct rows, but there's two immediate problems with this approach for me: 哪个获得了正确的行,但这种方法对我来说有两个直接的问题:

  • SUM seems wrong and complicated. SUM看似错误和复杂。 I'd have to find each of the conditionals and make sure each one is wrapped in a SUM , of which there might be lots. 我必须找到每个条件并确保每个条件包含在SUM ,其中可能有很多。
  • The b_value is indexed, and b has lots of rows. b_value被索引, b有很多行。 The HAVING clause doesn't use the index and so the query is noticeably slow. HAVING子句不使用索引,因此查询速度明显变慢。

Is there a different approach I should be taking to do this sort of query, bearing in mind I'm not really in direct control of the conditionals used for filtering. 我应该采取不同的方法来进行这种查询,记住我并不是真正直接控制用于过滤的条件。

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)
SELECT rows from a where it has b_value of both one and two would return Matt Damon.

if you want above result then table "a" "id" column should have unique records, table a and b both should have primary key. 如果你想要上面的结果,那么表“a”“id”列应该有唯一的记录,表a和b都应该有主键。

or you can try following query, but it will return double records for "matt daemon" 或者您可以尝试以下查询,但它将返回“matt daemon”的双记录

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

In dynamic SQL it would somthing like this, first I would suggest creating a variable in your application which you fill up based on the selection of filters 在动态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')"

Then you can create a stored procedure with one parameter, here you pass in your variable 然后,您可以使用一个参数创建存储过程,在此处传入变量

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

Then you can call this stored procedure from your code and pass in the variable 然后,您可以从代码中调用此存储过程并传入变量

CALL spNames (exampleOne);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM