一对多联表查询

一:总结说明

1.需求

  根据classId查询出对应的班级信息,包括学生和老师。

 

2.结构目录

 

3.新建学生表

 1 CREATE TABLE student(
 2     s_id INT PRIMARY KEY AUTO_INCREMENT,
 3     s_name VARCHAR(20),
 4     class_id INT
 5 );
 6 INSERT INTO student(s_name,class_id) VALUES('xs1',1);
 7 INSERT INTO student(s_name,class_id) VALUES('xs2',1);
 8 INSERT INTO student(s_name,class_id) VALUES('xs3',1);
 9 INSERT INTO student(s_name,class_id) VALUES('xs4',2);
10 INSERT INTO student(s_name,class_id) VALUES('xs5',2);
11 INSERT INTO student(s_name,class_id) VALUES('xs6',2);

 

4.实体类Student.java

 1 package com.jun.bean;
 2 
 3 public class Student {
 4     //
 5     private int id;
 6     private String name;
 7     //
 8     public Student() {}
 9     public Student(int id,String name) {
10         this.id=id;
11         this.name=name;
12     }
13     public int getId() {
14         return id;
15     }
16     public void setId(int id) {
17         this.id = id;
18     }
19     public String getName() {
20         return name;
21     }
22     public void setName(String name) {
23         this.name = name;
24     }
25     @Override
26     public String toString() {
27         return "Student [id=" + id + ", name=" + name + "]";
28     }
29     
30 }

 

5.修改Classes.java类

  因为classes除了老师,现在需要添加一个属性学生,这个学生的属性是List。

  需要修改get set方法,修改构造函数,修改tostring方法。

 1 package com.jun.bean;
 2 
 3 import java.util.List;
 4 
 5 public class Classes {
 6     //
 7     private int id;
 8     private String name;
 9     private Teacher teacher;
10     private List<Student> list;     //新添加的属性
11     //
12     public Classes() {}
13     public Classes(int id,String name,Teacher teacher,List<Student> list) {
14         this.id=id;
15         this.name=name;
16         this.teacher=teacher;
17         this.list=list;
18     }
19     public int getId() {
20         return id;
21     }
22     public void setId(int id) {
23         this.id = id;
24     }
25     public String getName() {
26         return name;
27     }
28     public void setName(String name) {
29         this.name = name;
30     }
31     public Teacher getTeacher() {
32         return teacher;
33     }
34     public void setTeacher(Teacher teacher) {
35         this.teacher = teacher;
36     }
37     public List<Student> getList() {
38         return list;
39     }
40     public void setList(List<Student> list) {
41         this.list = list;
42     }
43     @Override
44     public String toString() {
45         return "Classes [id=" + id + ", name=" + name + ", teacher=" + teacher + ", list=" + list + "]";
46     }
47         
48 }

 

6.Configuration.xml

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
 3 
 4 <configuration>
 5   <environments default="development">
 6     <environment id="development">
 7       <transactionManager type="JDBC">
 8         <property name="" value=""/>
 9       </transactionManager>
10       <dataSource type="UNPOOLED">
11         <property name="driver" value="com.mysql.jdbc.Driver"/>
12         <property name="url" value="jdbc:mysql://127.0.0.1:3308/mybatis"/>
13         <property name="username" value="root"/>
14         <property name="password" value="123456"/>
15       </dataSource>
16     </environment>
17   </environments>
18 
19    <mappers>
20       <mapper resource="com/jun/sql/config/class.xml"/>
21    </mappers>
22 
23 </configuration>

 

二:第一种方式

1.映射文件class.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE mapper
 3     PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 4     "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 5 <mapper namespace="one2many">
 6     <!-- 第一种方式: 
 7         嵌套结果
 8         Select * from class c,teacher t,student s where c.teacher_id=t.t_id and c.c_id=s.class_id and c.c_id=1; 
 9     -->
10     <select id="selectAll" parameterType="int" resultMap="getClasses">
11         Select * from class c,teacher t,student s where c.teacher_id=t.t_id and c.c_id=s.class_id and c.c_id=#{id}
12     </select>
13     <resultMap type="com.jun.bean.Classes" id="getClasses">
14         <id property="id" column="c_id"/>
15         <result property="name" column="c_name"/>
16         <association property="teacher" javaType="com.jun.bean.Teacher">
17             <id property="id" column="t_id"/>
18             <result property="name" column="t_name"/>
19         </association>
20         <collection property="list" ofType="com.jun.bean.Student">
21             <id property="id" column="s_id"/>
22             <result property="name" column="s_name"/>
23         </collection>
24     </resultMap>
25 </mapper>

 

2.测试类

 1 package com.jun.main;
 2 
 3 import java.io.IOException;
 4 import java.io.Reader;
 5 
 6 import org.apache.ibatis.io.Resources;
 7 import org.apache.ibatis.session.SqlSession;
 8 import org.apache.ibatis.session.SqlSessionFactory;
 9 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
10 import org.junit.Test;
11 import com.jun.bean.Classes;
12 public class MainTest {
13     /**
14      * 方式一
15      * @throws Exception
16      */
17     @Test
18     public void test1() throws Exception {
19         Reader reader=Resources.getResourceAsReader("com/jun/config/Configuration.xml");
20         SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);
21         SqlSession sqlSession=sqlSessionFactory.openSession(true);                //true后是自动提交
22         String statement="one2many.selectAll";
23         Classes classes=sqlSession.selectOne(statement, 1);
24         System.out.println(classes);
25         sqlSession.close();
26     }
27     
28 }

 

3.测试结果

  

 

三:方式二

1.映射文件

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE mapper
 3     PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 4     "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 5 <mapper namespace="one2many">
 6     <!-- 第一种方式: 
 7         嵌套结果
 8         Select * from class c,teacher t,student s where c.teacher_id=t.t_id and c.c_id=s.class_id and c.c_id=1; 
 9     -->
10     <select id="selectAll" parameterType="int" resultMap="getClasses">
11         Select * from class c,teacher t,student s where c.teacher_id=t.t_id and c.c_id=s.class_id and c.c_id=#{id}
12     </select>
13     <resultMap type="com.jun.bean.Classes" id="getClasses">
14         <id property="id" column="c_id"/>
15         <result property="name" column="c_name"/>
16         <association property="teacher" javaType="com.jun.bean.Teacher">
17             <id property="id" column="t_id"/>
18             <result property="name" column="t_name"/>
19         </association>
20         <collection property="list" ofType="com.jun.bean.Student">
21             <id property="id" column="s_id"/>
22             <result property="name" column="s_name"/>
23         </collection>
24     </resultMap>
25     
26     <!-- 
27         方式二:
28         三次查询
29         select * from class where c_id=1;
30         select * from teacher where t_id=1;       //使用第一句中查询到的结果
31         select * from student where class_id=1;   //使用第一句中查询到的结果,这个与第二句并行
32      -->
33      <select id="selectClass" resultMap="getClasses2">
34          select * from class where c_id=#{id}
35      </select>
36      <select id="getTeacher" resultType="com.jun.bean.Teacher">      <!-- 使用resultType -->
37          select t_id id,t_name name from teacher where t_id=#{id}
38      </select>
39      <select id="getStudent" resultType="com.jun.bean.Student">      <!-- 使用resultType -->
40          select s_id id,s_name name from student where class_id=#{id} <!-- 使用的是class_id -->
41      </select>
42      <resultMap type="com.jun.bean.Classes" id="getClasses2">
43          <id property="id" column="c_id"/>
44         <result property="name" column="c_name"/>
45         <association property="teacher" column="teacher_id" select="getTeacher"></association>
46         <collection property="list" column="c_id" select="getStudent"></collection>
47      </resultMap>
48 </mapper>

 

2.测试类

 1 package com.jun.main;
 2 
 3 import java.io.IOException;
 4 import java.io.Reader;
 5 
 6 import org.apache.ibatis.io.Resources;
 7 import org.apache.ibatis.session.SqlSession;
 8 import org.apache.ibatis.session.SqlSessionFactory;
 9 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
10 import org.junit.Test;
11 import com.jun.bean.Classes;
12 public class MainTest {
13     /**
14      * 方式一
15      * @throws Exception
16      */
17     @Test
18     public void test1() throws Exception {
19         Reader reader=Resources.getResourceAsReader("com/jun/config/Configuration.xml");
20         SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);
21         SqlSession sqlSession=sqlSessionFactory.openSession(true);                //true后是自动提交
22         String statement="one2many.selectAll";
23         Classes classes=sqlSession.selectOne(statement, 1);
24         System.out.println(classes);
25         sqlSession.close();
26     }
27     /**
28      * 方式二
29      * @throws Exception
30      */
31     @Test
32     public void test2() throws Exception {
33         Reader reader=Resources.getResourceAsReader("com/jun/config/Configuration.xml");
34         SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);
35         SqlSession sqlSession=sqlSessionFactory.openSession(true);                //true后是自动提交
36         String statement="one2many.selectClass";
37         Classes classes=sqlSession.selectOne(statement, 1);
38         System.out.println(classes);
39         sqlSession.close();
40     }
41     
42 }

 

3.结果

  

 

  

 

转载于:https://www.cnblogs.com/juncaoit/p/8228311.html

<think>嗯,我现在要解决MySQL中的一对查询问题,特别是分页和条件查询时出现的重复记录或结果不正确的情况。首先,我得理解这个问题到底是怎么回事。用户提到,在分页查询时,主和关都有查询条件的话,结果要么条数不对,要么关中的的一方只能显示一条。这应该是因为查询时,关中的条记录导致主记录被重复,进而影响分页的总数。 比如,假设主是文章(article),关是标签(article_tag),通过中间(article_to_tag)连接。一篇文章可能有个标签。当进行查询时,每篇文章会对应个标签,导致同一篇文章出现次。这时候如果用LIMIT分页的话,实际获取到的文章数量就会比预期少,因为同一篇文章被次计数。例如,一篇文章有两个标签,那么在查询结果中会出现两条记录,分页时这两条都会被算作不同的行,但实际上用户可能希望每篇文章只出现一次。 接下来,解决方案可能包括两种方法:子查询优化和使用DISTINCT配合GROUP BY。第一种方法,子查询优化,应该是先在主中筛选符合条件的ID,再进行查询。这样可以先确定主的记录,避免后的数据膨胀。例如,先通过子查询获取满足条件的文章ID,然后再查询具体的数据。这样做的好处是分页的总数基于主的记录数,而不是后的结果,避免了重复。 第二种方法是使用DISTINCT和GROUP BY。在查询时,使用DISTINCT主ID或者GROUP BY主ID来合并重复的记录。不过需要注意的是,GROUP BY在MySQL中如果包含非聚合列,可能会引发错误,尤其是在sql_mode设置为only_full_group_by的情况下。所以可能需要调整sql_mode,或者在SELECT子句中使用聚合函数来处理其他列。 另外,用户提供的引用中提到了使用COUNT(DISTINCT(a.id))来统计总条数,这样可以正确计算主的记录数,而不会被关的重复记录影响。这一点很重要,因为分页时需要正确的总记录数来生成分页导航。 还有,性能方面的问题需要考虑。子查询可能在某些情况下效率不高,尤其是当子查询的结果集很大时。这时候可能需要优化索引,比如在主查询条件字段(如id、时间字段)和关的外键字段上建立索引,提高查询速度。此外,使用覆盖索引也可以减少回操作,加快查询。 实际应用中,可能还需要处理关查询条件。例如,用户可能希望根据标签名称来筛选文章,这时候需要在时添加相应的WHERE条件。但直接添加可能会导致主记录被错误地过滤,或者因为关的条件不满足而排除掉主记录。因此,可能需要使用LEFT JOIN并确保条件放在ON子句或者WHERE子句中的适当位置,以正确过滤结果。 举个例子,假设用户想查询所有带有“技术”标签的文章,并且文章创建时间在2023年以后。正确的做法可能是在子查询中先筛选出符合条件的文章ID,然后再获取标签信息。或者在时使用INNER JOIN来确保只有存在相关标签的文章被选中,但这可能会影响主的记录数,需要根据需求决定。 总结一下,解决一对查询问题的关键在于如何避免主记录因关条记录而重复,导致分页和计数错误。解决方法包括使用子查询先确定主记录,再获取详细信息;或者使用DISTINCT和GROUP BY来合并结果,同时注意性能优化和索引的使用。</think>### MySQL一对查询问题解决方案 #### 一、核心问题分析 1. **数据重复**:查询时,主记录因关条数据被次匹配,导致分页时总条数错误。例如:文章(`article`)与标签(`article_tag`)通过中间(`article_to_tag`)关,一篇文章对应个标签时,查询会生成条记录[^2][^3]。 2. **条件冲突**:若查询条件同时涉及主和关(如筛选特定标签的文章),直接可能导致主数据被错误过滤[^4]。 --- #### 二、解决方案 ##### 方法1:子查询优化(推荐) **步骤**: 1. **先筛选主数据**:通过子查询确定满足条件的主ID。 2. **再查询详细信息**:基于主ID获取关数据。 **示例**: ```sql -- 分页查询ID SELECT a.id FROM article a WHERE a.create_time > '2023-01-01' LIMIT 0, 10; -- 获取完整数据 SELECT a.*, at.tag_name FROM article a LEFT JOIN article_to_tag att ON a.id = att.article_id LEFT JOIN article_tag at ON att.tag_id = at.id WHERE a.id IN (上述子查询结果); ``` ##### 方法2:DISTINCT + GROUP BY **步骤**: 1. **去重主记录**:使用`DISTINCT`或`GROUP BY`合并重复的主数据。 2. **处理关条件**:通过聚合函数(如`GROUP_CONCAT`)合并关字段。 **示例**: ```sql SELECT a.id, a.title, GROUP_CONCAT(at.tag_name) AS tags FROM article a LEFT JOIN article_to_tag att ON a.id = att.article_id LEFT JOIN article_tag at ON att.tag_id = at.id WHERE at.tag_name LIKE '%技术%' GROUP BY a.id LIMIT 0, 10; ``` ##### 关键点: - **统计总数**:使用`COUNT(DISTINCT a.id)`避免重复计数[^4]。 ```sql SELECT COUNT(DISTINCT a.id) FROM article a ...; ``` - **索引优化**:为`article.id`、`article.create_time`及关的外键字段建立索引[^1]。 --- #### 三、性能优化建议 1. **索引策略**: - 主筛选字段(如时间、状态)添加索引。 - 关的外键字段(如`article_to_tag.article_id`)添加索引。 2. **避免全扫描**:优先通过子查询缩小主范围,再查询。 3. **分页优化**:对于深度分页(如`LIMIT 100000,10`),改用基于ID范围的分页方式。 --- #### 四、应用场景示例 **需求**:查询包含“MySQL”标签且发布时间在2023年的文章,每页10条。 ```sql -- 步骤1:获取主ID SELECT a.id FROM article a WHERE a.create_time BETWEEN '2023-01-01' AND '2023-12-31' AND a.id IN ( SELECT att.article_id FROM article_to_tag att JOIN article_tag at ON att.tag_id = at.id WHERE at.tag_name = 'MySQL' ) LIMIT 0, 10; -- 步骤2:获取详细信息 SELECT a.*, GROUP_CONCAT(at.tag_name) AS tags FROM article a LEFT JOIN article_to_tag att ON a.id = att.article_id LEFT JOIN article_tag at ON att.tag_id = at.id WHERE a.id IN (步骤1的结果) GROUP BY a.id; ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值