设为首页 收藏本站
查看: 406|回复: 0

[经验分享] 在Rails下连接SQL Server 2005 分页

[复制链接]

尚未签到

发表于 2016-11-3 11:38:26 | 显示全部楼层 |阅读模式
  For a Rails/SQLServer application I’m working on, I had to deal with pagination with custom queries because of the different joins. The mislav-will_paginate plugin works great for MySQL, but for SQL Server, the paginated query generated by the current SQL Server Adapter (I’m using activerecord-sqlserver-adapter-1.0.0.9250) does not work very well. The current implementation is targetted really for SQL Server 2000 and older versions since these versions do not have support for ROW_NUMBER() method. It is a major pain in the butt to do pagination with these databases. With the newer SQL Sever 2005, the job is a bit easier. Microsoft implemented the ROW_NUMBER() method with a convoluted syntax to have better support for pagination, but it is still a drag because of the weird syntax.
  Semergence wrote in his blog about patching the SQLServerAdapter to support pagination. Based on his post, I improved ActiveRecord::ConnectionAdapters::SQLServerAdapter::add_limit_offset! to make the query work in a more general way with free-form queries, e.g. queries ran with the paginate_by_sql() method provided by mislav-will_paginate
  Include this script in your environment.rb file, or an external file and “require” the file within environment.rb.

view plaincopy to clipboardprint?




  • # monkey-patching SQLServerAdapter to support SQL Server 2005-style pagination   
  • module ActiveRecord   
  •   module ConnectionAdapters   
  •     class SQLServerAdapter   
  •       def add_limit_offset!(sql, options)   
  •         puts sql   
  •         options[:offset] ||= 0   
  •         options_limit = options[:limit] ? "TOP #{options[:limit]}" : ""  
  •         options[:order] ||= if order_by = sql.match(/ORDER BY(.*$)/i)   
  •                               order_by[1]   
  •                             else  
  •                               sql.match('FROM (.+?)\b')[1] + '.id'  
  •                             end  
  •         sql.sub!(/ORDER BY.*$/i, '')   
  •         sql.sub!(/SELECT/i, "SELECT #{options_limit} * FROM ( SELECT ROW_NUMBER() OVER( ORDER BY #{options[:order] } ) AS row_num, ")   
  •         sql << ") AS t WHERE row_num > #{options[:offset]}”   
  •         puts sql   
  •         sql   
  •       end  
  •     end  
  •   end  
  • end  


  # monkey-patching SQLServerAdapter to support SQL Server 2005-style pagination
module ActiveRecord
module ConnectionAdapters
class SQLServerAdapter
def add_limit_offset!(sql, options)
puts sql
options[:offset] ||= 0
options_limit = options[:limit] ? "TOP #{options[:limit]}" : ""
options[:order] ||= if order_by = sql.match(/ORDER BY(.*$)/i)
order_by[1]
else
sql.match('FROM (.+?)\b')[1] + '.id'
end
sql.sub!(/ORDER BY.*$/i, '')
sql.sub!(/SELECT/i, "SELECT #{options_limit} * FROM ( SELECT ROW_NUMBER() OVER( ORDER BY #{options[:order] } ) AS row_num, ")
sql << ") AS t WHERE row_num > #{options[:offset]}”
puts sql
sql
end
end
end
end

  The method above monkey-patches the SQLServerAdapter by overwriting the add_limit_offset! method.
  Here’s a custom query that I used and the transformed result:

view plaincopy to clipboardprint?




  • Resource.paginate_by_sql([   
  •       %!SELECT  resources.*   
  •             ,skills_count.skill_count   
  •         FROM resources   
  •             ,(   
  •                 SELECT resource_id   
  •                     , COUNT(*) AS skill_count   
  •                 FROM resource_skills   
  •             WHERE meta_skill_id IN (1,2,3,4,5,6,7,8,9,10)   
  •                 GROUP BY resource_id   
  •             ) AS skills_count   
  •         WHERE resources.is_active = ?   
  •           AND resources.id = skills_count.resource_id   
  •         ORDER BY skill_count DESC  
  •       !, true ], :page => page, :per_page => per_page  


Resource.paginate_by_sql([
%!SELECT  resources.*
,skills_count.skill_count
FROM resources
,(
SELECT resource_id
, COUNT(*) AS skill_count
FROM resource_skills
WHERE meta_skill_id IN (1,2,3,4,5,6,7,8,9,10)
GROUP BY resource_id
) AS skills_count
WHERE resources.is_active = ?
AND resources.id = skills_count.resource_id
ORDER BY skill_count DESC
!, true ], :page => page, :per_page => per_page

  With :page = 1, :per_page = 2, the resulted SQL is:

view plaincopy to clipboardprint?




  • SELECT TOP 2 * FROM ( SELECT ROW_NUMBER() OVER( ORDER BY skill_count DESC ) AS row_num, resources.*   
  •     ,skills_count.skill_count   
  •  FROM resources   
  •     ,(   
  •         SELECT resource_id   
  •             , COUNT(*) AS skill_count   
  •         FROM resource_skills   
  •  WHERE meta_skill_id IN (1,2,3,4,5,6,7,8,9,10)   
  •         GROUP BY resource_id   
  •     ) AS skills_count   
  •  WHERE resources.is_active = 1   
  •  AND resources.id = skills_count.resource_id   
  •   
  •  ) AS t WHERE row_num > 0  


SELECT TOP 2 * FROM ( SELECT ROW_NUMBER() OVER( ORDER BY skill_count DESC ) AS row_num, resources.*
,skills_count.skill_count
FROM resources
,(
SELECT resource_id
, COUNT(*) AS skill_count
FROM resource_skills
WHERE meta_skill_id IN (1,2,3,4,5,6,7,8,9,10)
GROUP BY resource_id
) AS skills_count
WHERE resources.is_active = 1
AND resources.id = skills_count.resource_id
) AS t WHERE row_num > 0

  The will_pagination’s COUNT query is

view plaincopy to clipboardprint?




  • SELECT COUNT(*) FROM (   
  •  SELECT resources.*   
  •     ,skills_count.skill_count   
  •  FROM resources   
  •     ,(   
  •         SELECT resource_id   
  •             , COUNT(*) AS skill_count   
  •         FROM resource_skills   
  •  WHERE meta_skill_id IN (21,22)   
  •         GROUP BY resource_id   
  •     ) AS skills_count   
  •  WHERE resources.is_active = 1   
  •  AND resources.id = skills_count.resource_id   
  •  ) AS count_table  


SELECT COUNT(*) FROM (
SELECT resources.*
,skills_count.skill_count
FROM resources
,(
SELECT resource_id
, COUNT(*) AS skill_count
FROM resource_skills
WHERE meta_skill_id IN (21,22)
GROUP BY resource_id
) AS skills_count
WHERE resources.is_active = 1
AND resources.id = skills_count.resource_id
) AS count_table

  The ORDER BY part is automatically removed from the main query (which becomes a sub-select) by the plugin to speed up the query. This in turns sanatizes the sql so that SQL Server doesn’t not complain about nested “ORDER BY” within a sub-select. Neat!
  The only catch with the current add_limit_offset! is that it does not support ALIAS-ing, because the aliasing confuses the reqex to parse out the ORDER BY condition in the OVER() part of the query.
  For regular find() queries, here’s a sample result

view plaincopy to clipboardprint?




  • Resource.find(:first)   
  • # original query:  SELECT * FROM resources   
  • # transformed:   SELECT TOP 1 * FROM ( SELECT ROW_NUMBER() OVER( ORDER BY resources.id ) AS row_number, * FROM resources ) AS t WHERE row_num > 0  


Resource.find(:first)
# original query:  SELECT * FROM resources
# transformed:   SELECT TOP 1 * FROM ( SELECT ROW_NUMBER() OVER( ORDER BY resources.id ) AS row_number, * FROM resources ) AS t WHERE row_num > 0

  Hope this helps and cheers!

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-295185-1-1.html 上篇帖子: SQL Server 2005作业未定义作业服务器 下篇帖子: 重装SQL SERVER 2005遇到的一些问题
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表