- PR -

【バグ】リンクサーバーから嘘の結果が返ってくる【SQL SERVER 2005】

投稿者投稿内容
ハニワ祭り
大ベテラン
会議室デビュー日: 2005/11/15
投稿数: 115
投稿日時: 2008-02-26 00:41
いつもお世話になっております。

2台のSQL SERVER 2005(SP2)でリンクサーバーを使っているのですが、
リンクサーバー越しに複数のテーブルを結合するクエリ(select文)
を流したところ、明らかに誤った結果が返ってきました。
具体的には抽出条件により、同一のレコードの内容が変化するというものです。

結合結果を全件出力すると
列1,列2
A, 1
B, 2
B, 3
C, 5

となるのに、列1=B の条件を追加すると
B, 2
B, 5

というようにありえない結果が返されてきます。
現象の再現性はあるものの、問題が発生するレコードは抽出条件によって変化します。

また、リンクサーバー側で直接同じクエリを流すと正しい結果が得られます。

これはSQL SERVER のバグでしょうか?
リンクサーバーのテーブルを結合すると正しい結果は得られないものなのでしょうか?

ご存知の方おられましたらよろしくお願いいたします。



[ メッセージ編集済み 編集者: ハニワ祭り 編集日時 2008-05-05 00:53 ]
上総
大ベテラン
会議室デビュー日: 2006/06/22
投稿数: 107
投稿日時: 2008-02-26 15:06
引用:

スレ主さんの書き込み(投稿日時: 2008-02-26 00:41)より
リンクサーバー越しに複数のテーブルを結合するクエリ(select文)
を流したところ、明らかに誤った結果が返ってきました
〜中略〜
これはSQL SERVER のバグでしょうか?
リンクサーバーのテーブルを結合すると正しい結果は得られないものなのでしょうか?


SQLが提示されてないので憶測になりますが、スレ主さんがおっしゃる処の全件出力時
のSQLすら正しくないのではないかと・・・。

まあ、「バグですか」と問うなら、最低でもSQL位は提示して下さい。
ハニワ祭り
大ベテラン
会議室デビュー日: 2005/11/15
投稿数: 115
投稿日時: 2008-02-27 01:11
上総さんのおっしゃるとおり、
確実に問題が発生するSQLを提示したいのですが、
実際には数万件のデータより数100件を抽出した際に問題が発生したため、
現象が発生する最小限の環境
(必要なデータ量、テーブル構成、インデックスの張り方、プロセッサ数など)
を特定できておりません。
公開可能な範囲で再現性のあるクエリ及びデータを特定出来た際に
再度報告させていただきます。

いちおうクエリの概要ですが、
簡単に書くと以下のような感じになりますが、
数件程度のデータで試しても恐らく現象は再現しません…

SELECT * FROM
(
SELECT
WK1.項目1 AS 列1
CASE WHEN 条件
THEN (SELECT 〜) ←項目1をキーに相関副問い合わせでテーブル「TA」より名称取得
 ELSE ''
END AS 列2 ,
CASE WHEN 条件
THEN SUBSTRING(WK1.項目1,5,4) ←項目1より文字の一部分を切り出す
 ELSE WK1.項目2
END AS 列3 ,
WK1.項目3 AS 列4
FROM
(
SELECT 〜 FROM T1, T2 ←キーで結合
UNION ALL
SELECT 〜 FROM T1, T2 ←キーで結合
UNION ALL
SELECT 〜 FROM T3
) AS WK1
) AS WK2
WHERE
 WK2.列3 = 'XXXX' ←ケース文の条件に当てはまった場合に切り出した文字で抽出

※T1,T2,T3,TAは全てリンクサーバー側のテーブルを指定してあるものと
 思ってください。

最終行のWHERE句を指定した場合のみ結果がおかしく
列1と列2が絶対にありえない(集合理論を無視した)組み合わせでデータが
出力されることがあります。


[ メッセージ編集済み 編集者: ハニワ祭り 編集日時 2008-02-27 01:24 ]
上総
大ベテラン
会議室デビュー日: 2006/06/22
投稿数: 107
投稿日時: 2008-02-27 12:15
SQLを拝見しましたが、確かに問題が発生する可能性は低そうです。
(列に副問い合わせを指定している箇所がなんとはなしに危なそうですが)

相関副問い合わせを使用せずに、『LEFT JOIN』を使用してみたらどうでしょうか?
取りあえず列2を取り出す箇所のみを修正しています。
コード:
SELECT      ALL
            *
FROM        (
            SELECT      ALL
                        WK1.項目1 AS 列1,
                        TA.から抽出する列 AS 列2,
                        CASE WHEN 条件
                        THEN SUBSTRING(WK1.項目1,5,4) ←項目1より文字の一部分を切り出す 
                       ELSE WK1.項目2
                        END AS 列3,
                        WK1.項目3 AS 列4
                        FROM        (
                                    SELECT 〜
                                    FROM        T1,
                                                T2 ←キーで結合
                                    UNION       ALL
                                    SELECT 〜
                                   FROM        T1,
                                                T2 ←キーで結合
                                    UNION       ALL
                                    SELECT 〜
                                   FROM        T3
                                    )
                        LEFT JOIN   TA
                        ON          相関副問い合わせで使用している条件
            ) AS WK2
WHERE      WK2.列3 = 'XXXX'

ハニワ祭り
大ベテラン
会議室デビュー日: 2005/11/15
投稿数: 115
投稿日時: 2008-02-27 19:06
現象の再現方法がわかりました。
手順と結果は以下のようになります。

--------------------------------------------------------
-- @準備処理(データベース「AAA」に対してテーブル作成)
--------------------------------------------------------

CREATE TABLE [dbo].[T2](
[ID] [char](3) NOT NULL,
[CODE] [char](2) NOT NULL
CONSTRAINT [PK_T1_1] PRIMARY KEY CLUSTERED
([ID] ASC)
)

GO

CREATE TABLE [dbo].[M2](
[CODE] [char](2) NOT NULL,
[NAME] [varchar](20) NOT NULL,
CONSTRAINT [PK_M2_1] PRIMARY KEY CLUSTERED
([CODE] ASC)
)

GO


INSERT INTO [dbo].[T2] VALUES ( '001', '66')
INSERT INTO [dbo].[T2] VALUES ( '002', '66')
INSERT INTO [dbo].[T2] VALUES ( '003', '66')
INSERT INTO [dbo].[T2] VALUES ( '004', '66')
INSERT INTO [dbo].[T2] VALUES ( '005', '77')
INSERT INTO [dbo].[T2] VALUES ( '006', '55')
INSERT INTO [dbo].[T2] VALUES ( '007', '88')
INSERT INTO [dbo].[T2] VALUES ( '008', '88')
INSERT INTO [dbo].[T2] VALUES ( '009', '77')
INSERT INTO [dbo].[T2] VALUES ( '010', '66')
INSERT INTO [dbo].[T2] VALUES ( '011', '77')
INSERT INTO [dbo].[T2] VALUES ( '012', '66')
INSERT INTO [dbo].[T2] VALUES ( '013', '66')
INSERT INTO [dbo].[T2] VALUES ( '014', '88')
INSERT INTO [dbo].[T2] VALUES ( '015', '88')
INSERT INTO [dbo].[T2] VALUES ( '016', '88')
INSERT INTO [dbo].[T2] VALUES ( '017', '22')
INSERT INTO [dbo].[T2] VALUES ( '018', '88')
INSERT INTO [dbo].[T2] VALUES ( '019', '88')
INSERT INTO [dbo].[T2] VALUES ( '020', '88')
INSERT INTO [dbo].[T2] VALUES ( '021', '44')
INSERT INTO [dbo].[T2] VALUES ( '022', '88')
INSERT INTO [dbo].[T2] VALUES ( '023', '66')
INSERT INTO [dbo].[T2] VALUES ( '024', '66')
INSERT INTO [dbo].[T2] VALUES ( '025', '77')
INSERT INTO [dbo].[T2] VALUES ( '026', '77')
INSERT INTO [dbo].[T2] VALUES ( '027', '77')
INSERT INTO [dbo].[T2] VALUES ( '028', '66')
INSERT INTO [dbo].[T2] VALUES ( '029', '66')
INSERT INTO [dbo].[T2] VALUES ( '030', '66')
INSERT INTO [dbo].[T2] VALUES ( '031', '44')
INSERT INTO [dbo].[T2] VALUES ( '032', '77')
INSERT INTO [dbo].[T2] VALUES ( '033', '44')
INSERT INTO [dbo].[T2] VALUES ( '034', '66')
INSERT INTO [dbo].[T2] VALUES ( '035', '88')
INSERT INTO [dbo].[T2] VALUES ( '036', '88')
INSERT INTO [dbo].[T2] VALUES ( '037', '66')
INSERT INTO [dbo].[T2] VALUES ( '038', '66')
INSERT INTO [dbo].[T2] VALUES ( '039', '77')
INSERT INTO [dbo].[T2] VALUES ( '040', '77')
INSERT INTO [dbo].[T2] VALUES ( '041', '77')
INSERT INTO [dbo].[T2] VALUES ( '042', '44')
INSERT INTO [dbo].[T2] VALUES ( '043', '77')
INSERT INTO [dbo].[T2] VALUES ( '044', '77')
INSERT INTO [dbo].[T2] VALUES ( '045', '66')
INSERT INTO [dbo].[T2] VALUES ( '046', '66')
INSERT INTO [dbo].[T2] VALUES ( '047', '77')
INSERT INTO [dbo].[T2] VALUES ( '048', '44')
INSERT INTO [dbo].[T2] VALUES ( '049', '66')
INSERT INTO [dbo].[T2] VALUES ( '050', '66')
INSERT INTO [dbo].[T2] VALUES ( '051', '66')
INSERT INTO [dbo].[T2] VALUES ( '052', '88')
INSERT INTO [dbo].[T2] VALUES ( '053', '66')
INSERT INTO [dbo].[T2] VALUES ( '054', '44')
INSERT INTO [dbo].[T2] VALUES ( '055', '88')
INSERT INTO [dbo].[T2] VALUES ( '056', '88')
INSERT INTO [dbo].[T2] VALUES ( '057', '88')
INSERT INTO [dbo].[T2] VALUES ( '058', '88')
INSERT INTO [dbo].[T2] VALUES ( '059', '22')
INSERT INTO [dbo].[T2] VALUES ( '060', '88')
INSERT INTO [dbo].[T2] VALUES ( '061', '11')
INSERT INTO [dbo].[T2] VALUES ( '062', '11')
INSERT INTO [dbo].[T2] VALUES ( '063', '11')
INSERT INTO [dbo].[T2] VALUES ( '064', '88')
INSERT INTO [dbo].[T2] VALUES ( '065', '77')
INSERT INTO [dbo].[T2] VALUES ( '066', '77')
INSERT INTO [dbo].[T2] VALUES ( '067', '66')
INSERT INTO [dbo].[T2] VALUES ( '068', '77')
INSERT INTO [dbo].[T2] VALUES ( '069', '77')
INSERT INTO [dbo].[T2] VALUES ( '070', '77')
INSERT INTO [dbo].[T2] VALUES ( '071', '33')
INSERT INTO [dbo].[T2] VALUES ( '072', '33')
INSERT INTO [dbo].[T2] VALUES ( '073', '66')
INSERT INTO [dbo].[T2] VALUES ( '074', '66')
INSERT INTO [dbo].[T2] VALUES ( '075', '66')
INSERT INTO [dbo].[T2] VALUES ( '076', '11')
INSERT INTO [dbo].[T2] VALUES ( '077', '77')
INSERT INTO [dbo].[T2] VALUES ( '078', '11')
INSERT INTO [dbo].[T2] VALUES ( '079', '66')
INSERT INTO [dbo].[T2] VALUES ( '080', '66')
INSERT INTO [dbo].[T2] VALUES ( '081', '66')
INSERT INTO [dbo].[T2] VALUES ( '082', '66')
INSERT INTO [dbo].[T2] VALUES ( '083', '66')
INSERT INTO [dbo].[T2] VALUES ( '084', '66')
INSERT INTO [dbo].[T2] VALUES ( '085', '66')
INSERT INTO [dbo].[T2] VALUES ( '086', '66')
INSERT INTO [dbo].[T2] VALUES ( '087', '66')
INSERT INTO [dbo].[T2] VALUES ( '088', '66')
INSERT INTO [dbo].[T2] VALUES ( '089', '77')
INSERT INTO [dbo].[T2] VALUES ( '090', '77')
INSERT INTO [dbo].[T2] VALUES ( '091', '66')
INSERT INTO [dbo].[T2] VALUES ( '092', '11')
INSERT INTO [dbo].[T2] VALUES ( '093', '88')
INSERT INTO [dbo].[T2] VALUES ( '094', '88')
INSERT INTO [dbo].[T2] VALUES ( '095', '44')
INSERT INTO [dbo].[T2] VALUES ( '096', '66')
INSERT INTO [dbo].[T2] VALUES ( '097', '77')
INSERT INTO [dbo].[T2] VALUES ( '098', '44')
INSERT INTO [dbo].[T2] VALUES ( '099', '77')
INSERT INTO [dbo].[T2] VALUES ( '100', '77')
INSERT INTO [dbo].[T2] VALUES ( '101', '88')
INSERT INTO [dbo].[T2] VALUES ( '102', '88')
INSERT INTO [dbo].[T2] VALUES ( '103', '88')
INSERT INTO [dbo].[T2] VALUES ( '104', '77')
INSERT INTO [dbo].[T2] VALUES ( '105', '66')
INSERT INTO [dbo].[T2] VALUES ( '106', '77')
INSERT INTO [dbo].[T2] VALUES ( '107', '88')
INSERT INTO [dbo].[T2] VALUES ( '108', '88')
INSERT INTO [dbo].[T2] VALUES ( '109', '11')


INSERT INTO [dbo].[M2] VALUES ( '11', 'A')
INSERT INTO [dbo].[M2] VALUES ( '22', 'B')
INSERT INTO [dbo].[M2] VALUES ( '33', 'C')
INSERT INTO [dbo].[M2] VALUES ( '44', 'D')
INSERT INTO [dbo].[M2] VALUES ( '55', 'E')
INSERT INTO [dbo].[M2] VALUES ( '66', 'F')
INSERT INTO [dbo].[M2] VALUES ( '77', 'G')
INSERT INTO [dbo].[M2] VALUES ( '88', 'H')


--------------------------------------------------------
-- Aローカルへリンクサーバーを張って以下のクエリを流す。
--------------------------------------------------------


WITH WK_CTE
(
[コード1]
, [コード2]
, [区分]
)
AS
(

SELECT
CODE [コード1]
, '' [コード2]
, '0' [区分]
FROM [127.0.0.1\SQL2005].[AAA].dbo.T2

UNION ALL

SELECT
'' [コード1]
, '' [コード2]
, '1' [区分]
FROM [127.0.0.1\SQL2005].[AAA].dbo.T2
where 1=0
)
,WK2 AS
(
SELECT
[コード1] as [コード1]

,CASE WHEN [区分] = '0'
THEN SUBSTRING(WK_CTE.[コード1],1,1)
ELSE ''
END AS [コード1前半]

,CASE WHEN [区分] = '0'
THEN SUBSTRING(WK_CTE.[コード1],2,1)
ELSE ''
END AS [コード1後半]

,CASE WHEN [区分] = '0'
THEN
ISNULL((
SELECT
M2_R.CODE
FROM [127.0.0.1\SQL2005].[AAA].dbo.M2 AS M2_R
WHERE M2_R.CODE = WK_CTE.[コード1]
),'')
ELSE
''
END AS [コード1コード]

,CASE WHEN [区分] = '0'
THEN
ISNULL((
SELECT
M2_R.[NAME]
FROM [127.0.0.1\SQL2005].[AAA].dbo.M2 AS M2_R
WHERE M2_R.CODE = WK_CTE.[コード1]
),'')
ELSE
''
END AS [コード1名]

FROM WK_CTE
)
select *
from WK2
where [コード1前半] = '7'
and [コード1後半] = '7'

----------------------------------
[結果]
----------------------------------
77 7 7 77 G
77 7 7 77 G
77 7 7 77 G
77 7 7 77 G
77 7 7 77 G
77 7 7 77 G
77 7 7 77 G
77 7 7 77 G
77 7 7 77 G
77 7 7 77 G
77 7 7 77 G
77 7 7 77 G
77 7 7 77 G
77 7 7 77 G
77 7 7 77 G
77 7 7 77 G
77 7 7 77 G
77 7 7 77 G
77 7 7 77 G
77 7 7 77 G
77 7 7 77 G
77 7 7 77 G
77 7 7 77 G
77 7 7 77 G
77 7 7 66 F ←存在しないはずの行が現れる!
77 7 7 66 F ←存在しないはずの行が現れる!
上総
大ベテラン
会議室デビュー日: 2006/06/22
投稿数: 107
投稿日時: 2008-02-27 22:54
引用:

スレ主さんの書き込み(投稿日時: 2008-02-27 19:06)より
--------------------------------------------------------
-- Aローカルへリンクサーバーを張って以下のクエリを流す。
--------------------------------------------------------
中略
FROM [127.0.0.1\SQL2005][AAA].dbo.T2
中略


上記の構文を打った場合、どのホスト端末で処理が実行されるんですか?
『127.0.0.1』は自分自身ですよね。

127.0.0.1が自動的にホスト名又は既存のネットワークアドレスに
変わっていれば問題ないとは思いますが、
ホスト名又は既存のネットワークアドレスに変わってなければ問題
が発生する可能性はあるかと・・・・
ハニワ祭り
大ベテラン
会議室デビュー日: 2005/11/15
投稿数: 115
投稿日時: 2008-02-27 23:16
今回の例は端末一台でネットワーク接続なしの環境でも試せるように
自分自身に対してリンクを張っていますが、
当然、自分のIPアドレスを直接指定してリンクサーバーを設定した際も、
別のSQLSERVERに対してリンクサーバーを設定しても同じ現象が発生します。

また、データ量が増えるほどこの現象の発生する頻度が高くなるようで、
数万件のデータより数百件を抽出した際には、半分以上がデタラメなデータに
なってしまいました。



[ メッセージ編集済み 編集者: ハニワ祭り 編集日時 2008-02-27 23:37 ]
未記入
大ベテラン
会議室デビュー日: 2008/02/07
投稿数: 115
投稿日時: 2008-02-28 10:55
ハニワ祭りさんの提示されたデータおよびスクリプトを用いて不具合事象が再現することを追試できました。(SQL Server 2005 SP2 GDR 9.0.3159)

上総さんは、ハニワ祭りさんの提示している内容を追試したのでしょうか? 追試もせずに「まさかバグのはずはない」「他の書き方してみたら」と思っているのなら口出しせずに黙っていて欲しい。邪魔だから。

SQL Server のいままでのリリースノートを見ていれば分かることですが、SQL Server は完全無欠ではありません。ほとんど毎回、リリースノートには「誤った結果セットを返すことがあった」というデータベースにあるまじき不具合が記載されています。

ハニワ祭りさんはどうしますか? この事象をマイクロソフトに投げてもらって、不具合を修正してもらいたいと私は考えています。ハニワ祭りさんが手間なら私のほうからマイクロソフトに投げても構いません。

最後に私が追試過程でまとめたスクリプトを載せておきます。ハニワ祭りさんのスクリプトからデータ量を減らしたり、複雑な共通テーブル式を除去してみました。(が、ちゃんと不具合は再現するスクリプトです。)

追試が面倒だという方のために、リンクサーバーの作成、後片付けもすべてスクリプトに含めておきました。SQL Server 2005 の累積パッチを当てている方、SQL Server 2008 CTP を入れている方にも動作を確認していただけるとありがたいです。

コード:
use master
go

SET NOCOUNT ON

--
-- 準備
--
if exists (select * from sys.objects where object_id = OBJECT_ID('T1'))
drop table T1

create table T1 (
	ID int primary key, 
	CODE_AB char(2) not null,
	CATEGORY char(1) not null default ('0')
) 

if exists (select * from sys.objects where object_id = OBJECT_ID('M1'))
drop table M1

create table M1 (
	CODE_AB char(2) primary key,
	[NAME] varchar(20) not null 
) 

insert into T1 (ID, CODE_AB) values (  1, '66') 
insert into T1 (ID, CODE_AB) values (  2, '66') 
insert into T1 (ID, CODE_AB) values (  3, '77')
insert into T1 (ID, CODE_AB) values (  4, '55') 
insert into T1 (ID, CODE_AB) values (  5, '88') 
insert into T1 (ID, CODE_AB) values (  6, '88') 
insert into T1 (ID, CODE_AB) values (  7, '77') 
insert into T1 (ID, CODE_AB) values (  8, '66') 
insert into T1 (ID, CODE_AB) values (  9, '77') 
insert into T1 (ID, CODE_AB) values ( 10, '66') 
insert into T1 (ID, CODE_AB) values ( 11, '66') 
insert into T1 (ID, CODE_AB) values ( 12, '88') 
insert into T1 (ID, CODE_AB) values ( 13, '88') 
insert into T1 (ID, CODE_AB) values ( 14, '88') 
insert into T1 (ID, CODE_AB) values ( 15, '22') 
insert into T1 (ID, CODE_AB) values ( 16, '88') 
insert into T1 (ID, CODE_AB) values ( 17, '88') 
insert into T1 (ID, CODE_AB) values ( 18, '88') 
insert into T1 (ID, CODE_AB) values ( 19, '44') 
insert into T1 (ID, CODE_AB) values ( 20, '88') 
insert into T1 (ID, CODE_AB) values ( 21, '66') 
insert into T1 (ID, CODE_AB) values ( 22, '66') 
insert into T1 (ID, CODE_AB) values ( 23, '77') 
insert into T1 (ID, CODE_AB) values ( 24, '77') 
insert into T1 (ID, CODE_AB) values ( 25, '77') 
insert into T1 (ID, CODE_AB) values ( 26, '66') 
insert into T1 (ID, CODE_AB) values ( 27, '66') 
insert into T1 (ID, CODE_AB) values ( 28, '66') 
insert into T1 (ID, CODE_AB) values ( 29, '44') 
insert into T1 (ID, CODE_AB) values ( 30, '77') 
insert into T1 (ID, CODE_AB) values ( 31, '44') 
insert into T1 (ID, CODE_AB) values ( 32, '66') 
insert into T1 (ID, CODE_AB) values ( 33, '88') 
insert into T1 (ID, CODE_AB) values ( 34, '88') 
insert into T1 (ID, CODE_AB) values ( 35, '66') 
insert into T1 (ID, CODE_AB) values ( 36, '66') 
insert into T1 (ID, CODE_AB) values ( 37, '77') 
insert into T1 (ID, CODE_AB) values ( 38, '77') 
insert into T1 (ID, CODE_AB) values ( 39, '77') 
insert into T1 (ID, CODE_AB) values ( 40, '44') 
insert into T1 (ID, CODE_AB) values ( 41, '77') 
insert into T1 (ID, CODE_AB) values ( 42, '77') 
insert into T1 (ID, CODE_AB) values ( 43, '66') 
insert into T1 (ID, CODE_AB) values ( 44, '66') 
insert into T1 (ID, CODE_AB) values ( 45, '77') 
insert into T1 (ID, CODE_AB) values ( 46, '44') 
insert into T1 (ID, CODE_AB) values ( 47, '66') 
insert into T1 (ID, CODE_AB) values ( 48, '66') 
insert into T1 (ID, CODE_AB) values ( 49, '66') 
insert into T1 (ID, CODE_AB) values ( 50, '88') 
insert into T1 (ID, CODE_AB) values ( 51, '66') 
insert into T1 (ID, CODE_AB) values ( 52, '44') 
insert into T1 (ID, CODE_AB) values ( 53, '88') 
insert into T1 (ID, CODE_AB) values ( 54, '88') 
insert into T1 (ID, CODE_AB) values ( 55, '88') 
insert into T1 (ID, CODE_AB) values ( 56, '88') 
insert into T1 (ID, CODE_AB) values ( 57, '22') 
insert into T1 (ID, CODE_AB) values ( 58, '88') 
insert into T1 (ID, CODE_AB) values ( 59, '11') 
insert into T1 (ID, CODE_AB) values ( 60, '11') 
insert into T1 (ID, CODE_AB) values ( 61, '11') 
insert into T1 (ID, CODE_AB) values ( 62, '88') 
insert into T1 (ID, CODE_AB) values ( 63, '77') 
insert into T1 (ID, CODE_AB) values ( 64, '77') 
insert into T1 (ID, CODE_AB) values ( 65, '66') 
insert into T1 (ID, CODE_AB) values ( 66, '77') 
insert into T1 (ID, CODE_AB) values ( 67, '77') 
insert into T1 (ID, CODE_AB) values ( 68, '77') 
insert into T1 (ID, CODE_AB) values ( 69, '33') 
insert into T1 (ID, CODE_AB) values ( 70, '33') 
insert into T1 (ID, CODE_AB) values ( 71, '66') 
insert into T1 (ID, CODE_AB) values ( 72, '66') 
insert into T1 (ID, CODE_AB) values ( 73, '66') 
insert into T1 (ID, CODE_AB) values ( 74, '11') 
insert into T1 (ID, CODE_AB) values ( 75, '77') 
insert into T1 (ID, CODE_AB) values ( 76, '11') 
insert into T1 (ID, CODE_AB) values ( 77, '66') 
insert into T1 (ID, CODE_AB) values ( 78, '66') 
insert into T1 (ID, CODE_AB) values ( 79, '66') 
insert into T1 (ID, CODE_AB) values ( 80, '66') 
insert into T1 (ID, CODE_AB) values ( 81, '66') 
insert into T1 (ID, CODE_AB) values ( 82, '66') 
insert into T1 (ID, CODE_AB) values ( 83, '66') 
insert into T1 (ID, CODE_AB) values ( 84, '66') 
insert into T1 (ID, CODE_AB) values ( 85, '66') 
insert into T1 (ID, CODE_AB) values ( 86, '66') 
insert into T1 (ID, CODE_AB) values ( 87, '77') 
insert into T1 (ID, CODE_AB) values ( 88, '77') 
insert into T1 (ID, CODE_AB) values ( 89, '66') 
insert into T1 (ID, CODE_AB) values ( 90, '11') 
insert into T1 (ID, CODE_AB) values ( 91, '88') 
insert into T1 (ID, CODE_AB) values ( 92, '88') 
insert into T1 (ID, CODE_AB) values ( 93, '44') 
insert into T1 (ID, CODE_AB) values ( 94, '66') 
insert into T1 (ID, CODE_AB) values ( 95, '77') 
insert into T1 (ID, CODE_AB) values ( 96, '44') 
insert into T1 (ID, CODE_AB) values ( 97, '77') 
insert into T1 (ID, CODE_AB) values ( 98, '77') 
insert into T1 (ID, CODE_AB) values ( 99, '88') 
insert into T1 (ID, CODE_AB) values (100, '88') 
insert into T1 (ID, CODE_AB) values (101, '88') 
insert into T1 (ID, CODE_AB) values (102, '77') 
insert into T1 (ID, CODE_AB) values (103, '66') 
insert into T1 (ID, CODE_AB) values (104, '77') 

insert into M1 values ('66', 'F') 
insert into M1 values ('77', 'G') 

if exists (select srv.name from sys.servers srv where srv.name = '127.0.0.1')
exec sp_dropserver @server = '127.0.0.1', @droplogins = 'droplogins'

exec sp_addlinkedserver @server = '127.0.0.1', @srvproduct = 'SQL Server'
exec sp_addlinkedsrvlogin @rmtsrvname = '127.0.0.1', @useself = 'True', @locallogin = NULL, @rmtuser = NULL, @rmtpassword = NULL

go

--
-- 実行
--
select *
from (

	select 
		ID,
		CODE_AB,
		CODE_A = case when CATEGORY = '0' then substring(T1.CODE_AB, 1, 1) end,
		CODE_B = case when CATEGORY = '0' then substring(T1.CODE_AB, 2, 1) end,
		CODE_NAME = case when CATEGORY = '0' then (
			select
				M1.CODE_AB + ' ' + M1.[NAME]
			from M1 
			where M1.CODE_AB = T1.CODE_AB
		) end
	from T1

) as TEMP
where CODE_A = '7' and CODE_B = '7'

select *
from (

	select 
		ID,
		CODE_AB,
		CODE_A = case when CATEGORY = '0' then substring(T1.CODE_AB, 1, 1) end,
		CODE_B = case when CATEGORY = '0' then substring(T1.CODE_AB, 2, 1) end,
		CODE_NAME = case when CATEGORY = '0' then (
			select
				M1.CODE_AB + ' ' + M1.[NAME]
			from [127.0.0.1].master.dbo.M1 as M1 
			where M1.CODE_AB = T1.CODE_AB
		) end
	from [127.0.0.1].master.dbo.T1 as T1

) as TEMP
where CODE_A = '7' and CODE_B = '7' --この条件をなくして全件出力すると正しい結果になる不思議。
--order by CODE_AB -- CODE_AB で並び替えると正しい結果になる不思議。

go

--
-- 後片付け
--
if exists (select srv.name from sys.servers srv where srv.name = '127.0.0.1')
exec sp_dropserver @server = '127.0.0.1', @droplogins = 'droplogins'

if exists (select * from sys.objects where object_id = OBJECT_ID('M1'))
drop table M1

if exists (select * from sys.objects where object_id = OBJECT_ID('T1'))
drop table T1


スキルアップ/キャリアアップ(JOB@IT)