资料案例
N+1查询问题
假设有如下的代码,获取10个客户对象,并把客户的邮编打印出来
clients = Client.limit(10)
clients.each do |client|
puts client.address.postcode
end
商品的代码看起来很好,但问题在于查询的总次数。上述代码总共会执行1(获取10个客户记录)+10(分别获取10个客户的地址)= 11次查询
N+1查询的解决办法
我们使用includes方法可以在取出10个客户记录的同时把这些记录关联的客户地址一次性取出来,这样我们在访问客户地址时就不用再去执行查询语句了,因为第一次已经把所需数据全部查询出来了。
使用includes的代码:
clients = Client.includes(:address).limit(10)
clients.each do |client|
puts client.address.postcode
end
和前面的 11 次查询不同,上述代码只会执行 2 次查询:
SELECT * FROM clients LIMIT 10
SELECT addresses.* FROM addresses
WHERE (addresses.client_id IN (1,2,3,4,5,6,7,8,9,10))
构造案例
创建项目
rails _4.2.2_ new active_record_first -d=mysql
修改Gemfile文件,把gem源替换为source 'https://gems.ruby-china.org'
,把gem 'mysql2'替换为gem 'mysql2', '~> 0.3.18'
把database.yml文件里面的数据库密码设置为本地mysql数据库的密码
cd active_record_first
bundle install
rake db:create
构造数据
需要两张表,先创建两张表的迁移文件
rails g migration CreateProducts
rails g migration CreateProductSecondTags
商品表 | products |
---|---|
商品二级标签表 | product_second_tags |
---|---|
我们数据表不需要默认生成的id属性,所以添加上id: false
我们使用rails默认的数据类型,定义就用t.string、t.integer的形式。
我们需要sql语句去定义类型时,定义就用t.column后面跟上sql数据类型定义。
数据表中的外键经常用于查询,所以我们一般都会加上索引。
凡是ID字段,都用CHAR(36)的类型。
添加字段
rails g migration AddSecondTagIdToProducts
我们往products表添加一个新的字段TagID,不过我们在迁移文件里面写的时候迁移文件名是AddSecondTagIdToProducts,而不是AddSecondTagIDToProducts
填充数据表数据
添加模型文件
product模型需要访问它关联的数据表记录,所以数据关联belongs_to需要写在product模型文件。而我们目前不需要通过product_second_tag.products的访问方式,所以product_second_tag不需要添加has_many数据关联。
添加路由文件
添加控制器文件
Rails c模型下调试
注意:在rails c模型下,无论是代码发生变更,或者我们在navicat里面插入新的数据,都需要重新进入rails c控制台才能生效。
字段大小写
我们在find_by中根据字段进行查找时,字段不区分大小写
我们访问查询结果的字段时只有id(ID)属性不区分大小写,其他字段区分大小写
N+1查询问题
每次each,都执行一次sql查询,所以下面执行6次each,造成6次查询(n次查询)
使用includes的话,在得到products记录的时候一次性把products关联的二级标签记录都得到了。不管有没有each里面的访问都是两条sql查询
视图文件下进行调试
遗留问题,为什么会出现CACHE,我们就算在配置文件关闭缓存,添加如下代码config.action_controller.perform_caching = false还是会有CACHE。
可以看到,查询ProductSecondTag执行多条SQL语句
使用includes之后,就算不访问关联表,也执行2次查询
我们只有操作数据时才会执行查询语句,如下不进行访问就没有查询语句
我们访问数据的长度(就算不在视图中<%= %>进行显示),这时就执行了查询语句。
我们在第一个语句并不执行sql语句。后面第一次使用数据时执行两条sql语句获取到所有数据了。所有第二次、第三次。。。第n次使用数据时都不再执行sql查询语句。
使用Git做版本控制
进入项目根目录
.../active_record_first# git init
Initialized empty Git repository in /home/**********/active_record_first/.git/
git add -A
git commit -m "ActiveRecordFirst"
然后创建一个git仓库
提交
git push -u https://github.com/xiaohuacc/active_record.git master
然后我们就可以在git仓库看到我们提交的代码了
在本地有时
取消提交(慎用)
我们有时从远程仓库拷贝代码到本地之后,在本地提交了自己的多次修改,但是需求变动之后这些修改不需要了。我们可以取消这些修改,使用命令git reset --hard加上提交版本号;不过要注意我们使用该命令回退到指定A版本之后push到远程仓库,A版本后面的commit历史就没了----(无法通过commit历史看到A版本之后的修改提交了)。一般这个命令要慎用,而且只用于回到自己最近一次本地的commit版本(该commit未push到远程仓库)。
我们在首次提交之后,又在视图文件添加了一下代码。现在我们不要这些首次提交后的修改又很难一个个找到变动的地方改回来,于是使用
首次提交,使用git log
命令查看
commit bf2bd5a242d64564cd85383e17caa1af9ce786c8
Author: ***
Date: Sun Nov 27 14:59:05 2016 +0800
ActiveRecordFirst
然后又做了一下修改并提交了
git add .
git commit -m "测试git取消修改功能"
[master 77f455d] 测试git取消修改功能
2 files changed, 4 insertions(+), 4 deletions(-)
git log
commit 77f455d78967518c9db4f49812d739d3d0285224
Author: ***
Date: Sun Nov 27 15:11:53 2016 +0800
测试git取消修改功能
commit bf2bd5a242d64564cd85383e17caa1af9ce786c8
Author: ***
Date: Sun Nov 27 14:59:05 2016 +0800
ActiveRecordFirst
我们回到首次提交时的状态,去掉第二次提交的代码修改
git reset --hard bf2bd5a242d64564cd85383e17caa1af9ce786c8
HEAD is now at bf2bd5a ActiveRecordFirst
然后回到IDE点击YES重新加载项目代码
然后我们git log
可以看到没有第二次提交的log,这也意味着我们想要回到第二次的提交也是不行了。
commit bf2bd5a242d64564cd85383e17caa1af9ce786c8
Author: ***
Date: Sun Nov 27 14:59:05 2016 +0800
ActiveRecordFirst