SQLServerネタ:m行目からn件取得するSQL

チャレンジしましたよ。えぇ。


下記のテーブルを作成。

if exists ( select * 
            from  dbo.sysobjects 
            where id = object_id(N'[dbo].[T_RowN_CountM]') 
            and OBJECTPROPERTY(id, N'IsUserTable') = 1)
      drop table [dbo].[T_RowN_CountM]
GO

CREATE TABLE [dbo].[T_RowN_CountM] (
      [IDKey] [int] NOT NULL ,
      [Name] [varchar] (8000) COLLATE Japanese_BIN NULL ,
      CONSTRAINT [PK_T_RowN_CountM] PRIMARY KEY  CLUSTERED 
      (
            [IDKey]
      )  ON [PRIMARY] 
) ON [PRIMARY]
GO

INSERT T_RowN_CountM VALUES(6,'a')
INSERT T_RowN_CountM VALUES(7,'b')
INSERT T_RowN_CountM VALUES(8,'c')
INSERT T_RowN_CountM VALUES(11,'d')
INSERT T_RowN_CountM VALUES(12,'e')
INSERT T_RowN_CountM VALUES(14,'f')
INSERT T_RowN_CountM VALUES(15,'g')
INSERT T_RowN_CountM VALUES(16,'h')
INSERT T_RowN_CountM VALUES(17,'i')
GO

まず最初に考えたのが
「m行目から」→「上からm番目以降のレコード取得」の部分
SQLServerにはRecordIDや物理Noなんてないから、
昇順なキーがあるものを前提としてSQL発行による
取得を行う。

さてSQL解釈的にやろうとすることを分解すると
1.TOPの指定=n
2.1の中で最大値をもつIDを取る。
3.2を持つレコードがそれ。
になると思うのでまずは1から。
具体的に2行目から3件取得を例にやってみる。

[SQL]
SELECT      TOP 2 IDKey
FROM        T_RowN_CountM

[Result]
6
7

これで1はOK。
次に2。

[SQL]
SELECT      MAX(IDKey)
FROM  (     SELECT      TOP 2 IDKey
            FROM  T_RowN_CountM
      ) AS V_Row

すると...
[Result]
17

( ゜Д゜)ハァ?なんでじゃ?と試行錯誤。実はORDERが必要だと
いう結論に達した(ナゼかは知らぬ)。

>推測だが、TOPは「取得した部分の一部を返す」という性質
>から実はWHERE句がないと全件取得していると思われる。
>その内の最大なのだから...?にしてもORDERとの結びが...
>あーやっぱわからんわぁ。

あとは3でのINNER JOINにより出来たのがρ(・д・*)コレ

[SQL]
SELECT      *
FROM  T_RowN_CountM
INNER JOIN  (     SELECT      MAX(IDKey) AS RowN
                  FROM  (     SELECT      TOP 2 IDKey
                              FROM  T_RowN_CountM
                              ORDER BY IDKey
                        ) AS V_Row
            ) AS V_RowN
      ON    T_RowN_CountM.IDKey     =     V_RowN.RowN

[Result]
7	b	7

うし。次に、m件の方か。そのまんまTOPになるんじゃねーの?
nのIDKey以上かつTOP mでどう?

[2行目から3件とる場合]
SELECT	TOP 3 *
FROM	T_RowN_CountM
INNER JOIN	(	SELECT	MAX(IDKey) AS RowN
			FROM	(	SELECT	TOP 2 IDKey
					FROM	T_RowN_CountM
					ORDER BY IDKey
				) AS V_Row
		) AS V_RowN
	ON	T_RowN_CountM.IDKey	>=	V_RowN.RowN
ORDER BY IDKey

暇つぶし?に応用してみた。
ランダムにnレコード目を取得するのがρ(・д・*)コレ

DECLARE     @vchCrLf    AS    VARCHAR(2)
DECLARE     @intCount   AS    INT
DECLARE     @vchSQL           AS    VARCHAR(8000)

      SET   @vchCrLf    =     CHAR(13) + CHAR(10)

      SELECT      @intCount = COUNT(*)
      FROM  T_RowN_CountM

      SET   @vchSQL     =     'SELECT     TOP 1 * ' + @vchCrLf + 
                              'FROM T_RowN_CountM' + @vchCrLf + 
                              'INNER JOIN (     SELECT      MAX(IDKey) AS RowN' + @vchCrLf + 
                              '                 FROM  (     SELECT      TOP ' + CAST(FLOOR(RAND() * @intCount) + 1 AS VARCHAR(100)) + ' IDKey' + @vchCrLf + 
                              '                             FROM  T_RowN_CountM' + @vchCrLf + 
                              '                             ORDER BY IDKey' + @vchCrLf + 
                              '                       ) AS V_Row' + @vchCrLf + 
                              '           ) AS V_RowN' + @vchCrLf + 
                              '     ON    T_RowN_CountM.IDKey     >=    V_RowN.RowN' + @vchCrLf
      EXEC  (@vchSQL)

      PRINT @vchSQL

これをベースに
・サイコロ
・ジャンケン
・今日の占い
・明日の天気  Σ(゜д゜)オイオイ
などに応用できるのでは...