<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Discrete Information &#187; Pagination</title>
	<atom:link href="http://www.discreteinformation.com/tag/pagination/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.discreteinformation.com</link>
	<description>Providing a finite amount of information.</description>
	<lastBuildDate>Sun, 23 Aug 2009 03:42:50 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Paging Stored Procedure in MS SQL, By Page</title>
		<link>http://www.discreteinformation.com/2009/05/ms-sql-server-paging-method-1/</link>
		<comments>http://www.discreteinformation.com/2009/05/ms-sql-server-paging-method-1/#comments</comments>
		<pubDate>Sun, 24 May 2009 19:31:39 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[MS SQL]]></category>
		<category><![CDATA[Pagination]]></category>

		<guid isPermaLink="false">http://www.discreteinformation.com/?p=15</guid>
		<description><![CDATA[MS SQL has the TOP clause, and it works very similar to MySQL&#8217;s LIMIT clause. However, the TOP clause in MS SQL provides no sort of OFFSET equivalent. Thus the TOP clause is virtually useless for a proper paging control. Let&#8217;s see one way we can work around that limitation:
For reference, here are the CREATE [...]]]></description>
			<content:encoded><![CDATA[<p>MS SQL has the <code>TOP</code> clause, and it works very similar to MySQL&#8217;s <code>LIMIT</code> clause. However, the <code>TOP</code> clause in MS SQL provides no sort of <code>OFFSET</code> equivalent. Thus the <code>TOP</code> clause is virtually useless for a proper paging control. Let&#8217;s see one way we can work around that limitation:</p>
<p>For reference, here are the <code>CREATE</code> and <code>INSERT</code> statements for the <code>[users]</code> table used in this example.</p>
<pre>
CREATE TABLE [users] (
  user_id INT PRIMARY KEY,
  username VARCHAR(16),
  password VARCHAR(16),
  email VARCHAR(128)
);

INSERT INTO [users] (user_id, username, password, email)
SELECT 1, 'bob', 'bob!password', 'bob@gmail.com'
UNION ALL
SELECT 2, 'john', 'john!password', 'john@yahoo.com'
UNION ALL
SELECT 3, 'kim', 'kim!password', 'kim@msn.com'
UNION ALL
SELECT 4, 'rachel', 'rachel!password', 'rachel@aol.com'
UNION ALL
SELECT 5, 'frank', 'frank!password', 'frank@email.com';
</pre>
<p>Now, let&#8217;s create a stored procedure to search our <code>[users]</code> table. It takes three paramaters. The <code>@email</code> paramater is a full or partial e-mail to search. <code>@page</code> is the page to be displayed, and <code>@per_page</code> is the number of results per page.</p>
<pre>
CREATE PROC searchUsersByEmail
  @email VARCHAR(128),
  @page SMALLINT,
  @per_page SMALLINT
AS

SET @last_page = (@nbr_results - 1) / @per_page + 1;

SELECT
  @nbr_results = COUNT(*)
FROM [users] AS u
WHERE u.email LIKE '%' + @email + '%';

SET @last_page = @nbr_results / @per_page; 

IF (@page = 1)
BEGIN
  -- Special first page case:
  SELECT TOP (@per_page)
    u.user_id,
    u.username,
    u.email
  FROM [users] AS u
  WHERE u.email LIKE '%' + @email + '%'
  ORDER BY u.email ASC;
END;
ELSE IF (@page = @last_page)
BEGIN
  -- Special last page case:
  SELECT s0.*
  FROM (
    SELECT TOP (@nbr_results - (@last_page - 1) * @per_page)
      u.user_id,
      u.username,
      u.email
    FROM [users] AS u
    WHERE u.email LIKE '%' + @email + '%'
    ORDER BY u.email DESC
  ) AS s0
  ORDER BY s0.email ASC;
END;
ELSE IF (@page > @last_page)
BEGIN
  -- Error / Empty set case, replace with error if desired.
  SELECT
    u.user_id,
    u.username,
    u.email
  FROM [users] AS u
  WHERE 1 = 0;
END;
ELSE
BEGIN
  -- All other cases:
  SELECT s1.*
  FROM (
    SELECT TOP (@per_page) s0.*
    FROM (
      SELECT TOP (@page * @per_page)
        u.user_id,
        u.username,
        u.email
      FROM [users] AS u
      WHERE u.email LIKE '%' + @email + '%'
      ORDER BY u.email ASC
    ) AS s0
    ORDER BY s0.email DESC
  ) AS s1
  ORDER BY s1.email ASC;
END;
</pre>
<p>Now let&#8217;s test our search procedure by searching for all e-mails with &#8220;.com&#8221;:</p>
<pre>
[searchUsersByEmail] @email = '.com', @page = 1, @per_page = 3;

user_id     username email
----------- -------- ----------------
1           bob      bob@gmail.com
5           frank    frank@email.com
2           john     john@yahoo.com

[searchUsersByEmail] @email = '.com', @page = 2, @per_page = 3;

user_id     username email
----------- -------- ----------------
3           kim      kim@msn.com
4           rachel   rachel@aol.com
</pre>
<p>That&#8217;s it. Now you have a functional procedure in MS SQL as an alternative to MySQL&#8217;s <code>LIMIT x OFFSET y</code> clause.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.discreteinformation.com/2009/05/ms-sql-server-paging-method-1/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
