役立つSQL技 for SQL Server
索引
・【参照可能なデータベースサーバー一覧の取得】
・【データベース一覧を取得】
・【セッション一覧の取得】
・【自身のセッションIDを取得】
・【ストアドプロシージャ一覧取得】
・【ストアードプロシジャー内容の取得】
・【ストアードプロシジャー内容の取得(各行の先頭にID付加)】
・【ストアードプロシジャーを行単位で取得】
・【テーブル一覧取得】
・【テーブル構造取得】
・【ビューの定義を取得】
・【実行済みSQL取得】
・【基本的な再帰】
・【値の横縦変換】
・【値の横縦変換(依存型)】
・【値の横縦変換(速い?)】
・【一番多く使われている値を取得】
・【次のレコードを使用する(パフォーマンスは保証できない)】
・【カンマで区切られてデータが入っているフィールドを別々のフィールドに分割】
・【CSV形式のデータを各フィールドに分解】

索引へ

【参照可能なデータベースサーバー一覧の取得】

select
/* 参照可能なデータベースサーバー一覧の取得 */
 name
,data_source 
from sys.servers
WHERE 1=1
--and name like @'%此処に対象を指定%'
現在接続中のSQL-SERVERから接続可能なSQL-SERVERをリスト
上記のsqlでは「@」のところにサーバー名を指定可能

実際にデータを見るには
select * from [リストされたSQL-SERVER].[master].[dbo].[MSreplication_options]

索引へ

【データベース一覧を取得】

select
/* データベース一覧を取得 */
 name 
from sys.databases
WHERE 1=1
--and name like @'%此処に対象を指定%'
現在接続中のSQL-SERVERに存在するデータベースをリスト
上記のsqlでは「@」のところにデータベース名を指定可能

索引へ

【セッション一覧の取得】

select 
/* セッション一覧の取得 */
 spid
,hostname
,loginame
,program_name
,login_time
,last_batch
,status
from sys.sysprocesses
現在接続中のSQL-SERVERで使用中のセッションをリスト

索引へ

【自身のセッションIDを取得】

select
/* 自身のセッションIDを取得 */
 @@spid
あらかじめ自身で使用しているセッションIDがわかっていれば
「KILL」をしなければならない状況になっても不安なくKILLすることができる
ただしKILLしなければならない状況になってからでは取得できないので注意!!

索引へ

【ストアドプロシージャ一覧取得】

SELECT
/* ストアドプロシージャ一覧取得 */
 cast(sys_o.name as varchar) as name
FROM sysobjects sys_o
where sys_o.xtype = 'P'
--and sys_o.name like @'%此処に対象を指定%'
現在使用中(カレント)のデータベースにあるストアドプロシージャ一覧をリスト
上記ではsysobjects.xtypeに'P'を指定してストアド プロシージャに限定

 オブジェクトの種類には下記のようなものがあります 
AF = 集計関数 (CLR) 
C = CHECK 制約 
D = Default 制約または DEFAULT 制約 
F = FOREIGN KEY 制約 
L = ログ 
FN = スカラー関数 
FS = アセンブリ (CLR) スカラー関数 
FT = アセンブリ (CLR) テーブル値関数 
IF = インライン テーブル関数 
IT = 内部テーブル 
P = ストアド プロシージャ 
PC = アセンブリ (CLR) ストアド プロシージャ 
PK = PRIMARY KEY 制約 (type は K) 
RF = レプリケーション フィルター ストアド プロシージャ 
S = システム テーブル 
SN = シノニム 
SQ = サービス キュー 
TA = アセンブリ (CLR) DML トリガー 
TF = テーブル関数 
TR = SQL DML トリガー 
TT = テーブルの種類 
U = ユーザー テーブル 
UQ = UNIQUE 制約 (typeは K) 
V = ビュー 
X = 拡張ストアド プロシージャ

索引へ

【ストアードプロシジャー内容の取得】

SELECT
/* ストアードプロシジャー内容の取得 */
'--[[['+cast(sys_o.name as NCHAR(110))+']]]--'+NCHAR(13)+NCHAR(10)+ /* 先頭行にIDを付加する場合 */
sys_c.definition as SpStr
FROM sys.sql_modules sys_c ,
sys.objects sys_o
WHERE sys_c.object_id = sys_o.object_id
and type = 'P'
--and sys_o.name like @'%此処に対象を指定%'
ストアドプロシージャのソースを取り出し
複数のプロシージャも1度に取得

上記SQLでは3行目をコメントにすればオリジナルのソースが取得可能
SSMSEで実行後、全選択しCSVに出力すればOK

索引へ

【ストアードプロシジャー内容の取得(各行の先頭にID付加)】

SELECT
/* ストアードプロシジャー内容の取得(各行の先頭にID付加) */
 cast(name+char(9)+replace(SpStr,char(10),char(10)+name+char(9)) as text)/*先頭にプログラムIDを付ける時*/
from (
  select
  /* ストアードプロシジャー内容の取得 */
   SpStr
  ,name
  from (
    SELECT
     sys_c.definition as SpStr
    ,sys_o.name
    FROM sys.sql_modules sys_c
    left outer join sys.objects sys_o
    on sys_c.object_id = sys_o.object_id
    where 1 = 1
    and type = 'P'
    --and sys_o.name like @'%此処に対象を指定%'
  ) a
) a
ストアドプロシージャのソースを取り出しかつ各行の先頭にIDを付加
IDの後にTABを付加しているのでソースとIDの分離も可能

SSMSEで実行後、全選択しCSVに出力すればOK

索引へ

【ストアードプロシジャーを行単位で取得】

--select
/* ストアードプロシジャーを行単位で取得 */
WITH LineStr(StrName,StrData,StrPos_st,StrPos_en)
as (
  SELECT
   sys_o.name        as StrName
  ,sys_c.definition  as StrData
  ,cast(1 as bigint) as StrPos_st
  ,CHARINDEX(NCHAR(13) + NCHAR(10),sys_c.definition) as StrPos_en
  FROM sys.sql_modules        sys_c
  left outer join sys.objects sys_o
  on sys_o.object_id = sys_c.object_id
  WHERE type = 'P'
  union all
  SELECT
   StrName
  ,StrData
  ,StrPos_en+2 as StrPos_st
  ,CHARINDEX(NCHAR(13)+NCHAR(10),StrData,StrPos_en+2) as StrPos_en
  from LineStr
  where StrPos_en > 0
)
select 
 StrName
,Line
,LineSrc
from (
  select 
   StrName
  ,ROW_NUMBER() OVER (PARTITION BY StrName ORDER BY StrPos_st) as Line
  ,SUBSTRING(StrData,StrPos_st,case when StrPos_en > 0 then StrPos_en else len(StrData)+10 end-StrPos_st) as LineSrc
  from LineStr
) Src
where 1=1
--and  StrName   like @'%此処に対象を指定%'
--and  LineSrc   like @'%検索する文字列を指定%'
order by
 StrName
,Line
option (MAXRECURSION 32767)
ストアドプロシージャのソースを行単位でレコードに分割
SQLでGrepのようなことが可能

SSMSEで実行後、全選択しCSVに出力すればOK

索引へ

【テーブル一覧取得】

select 
/* テーブル一覧取得 */
 cast(a.name as varchar(50)) as テーブル名 
,case  
 when a.xtype = 'S'  then 'SysTable' 
 when a.xtype = 'U'  then 'UserTable' 
 when a.xtype = 'V'  then 'View' 
 end  as TBLタイプ 
from dbo.sysobjects a 
WHERE 1=1
--and a.name like @'%此処に対象を指定%' 
and a.xtype in ('S','U','V')
order by テーブル名
現在使用中(カレント)のデータベースにあるテーブル、ビューをリスト

索引へ

【テーブル構造取得】

select 
/* テーブル構造取得 */
 cast(a.name as varchar(50)) as テーブル名 
,case  
 when a.xtype = 'S'  then 'SysTable' 
 when a.xtype = 'U'  then 'UserTable' 
 when a.xtype = 'V'  then 'View' 
 end  as TBLタイプ 
,cast(b.colid as int) as フィールド順 
,cast(b.name as varchar(50)) as フィールド名 
,cast(c.name as varchar(20)) as タイプ 
,b.prec  as 桁数 
,b.scale as 少数部 
,d.keyno as キー順 
,case when b.isnullable = 0 then 'not null' end as null値 
from dbo.sysobjects a 
left outer join dbo.syscolumns b 
on a.id = b.id 
left outer join dbo.systypes c 
on b.xtype = c.xtype 
and b.usertype = c.usertype 
left outer join dbo.sysindexkeys d 
on a.id = d.id 
and b.colid =d.colid 
and d.indid = 1 
WHERE 1=1
--and a.name like @'%此処に対象を指定%' 
and a.xtype in ('S','U','V')
order by テーブル名,フィールド順
現在使用中(カレント)のデータベースにあるテーブル、ビューの構造を取得

索引へ

【ビューの定義を取得】

SELECT
/* ビューの定義を取得 */
 '--[[['+cast(schemas.name + '.' + views.name as CHAR(110))+']]]--'+NCHAR(13)+NCHAR(10)+ /* 先頭行にIDを付加する場合 */
+modules.definition                   AS Definition
FROM sys.views views
INNER JOIN sys.objects objects
ON views.object_id = objects.object_id
INNER JOIN sys.schemas schemas
ON objects.schema_id = schemas.schema_id
INNER JOIN sys.sql_modules modules
ON objects.object_id = modules.object_id
where 1 = 1
--and views.name like @'%此処に対象を指定%'
ORDER BY
 schemas.name + '.' + views.name
現在使用中(カレント)のデータベースにあるビューの定義を取得

索引へ

【実行済みSQL取得】

SELECT
/* 実行済みSQL取得 */
*
FROM sys.dm_exec_query_stats AS qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st
ORDER BY last_execution_time DESC
masterをカレントのデータベースにすると取得できる
use master

索引へ

【カレントデータベース確認】

SELECT
/* カレントデータベース確認 */
DB_NAME() as カレントデータベース





索引へ

【基本的な再帰】

--select
/* 基本的な再帰 */
with Row(p) AS (
 select 1 as p
 UNION ALL
 select p+1 as p from Row
 where p < 32767
)
select p from Row
option (MAXRECURSION 32767)
1〜32767の番号を持つレコードを発生させています
年間の日付を作る時などに便利
↓↓↓こんな感じ↓↓↓
--select
/* 基本的な再帰 */
with Row(p) AS (
 select 1 as p
 UNION ALL
 select p+1 as p from Row
 where p < 400
)
select cast('20120101' as datetime) + p-1 as 日付 from Row
where year(cast('20120101' as datetime) + p-1) = '2012'
option (MAXRECURSION 32767)

索引へ

【値の横縦変換】

--select
/* 値の横縦変換 */
WITH Row(p) AS (
 select 0 as p
 UNION ALL
 select p+1 as p from Row
 where p < 32767
)
,
ItemList as (
 select 
  substring(Item
 ,p+1
 ,charindex(',',substring(Item+',',p+1,LEN(Item)))-1
 ) as v
 from (
--    select @ItemList as Item
   select '010000,020,030,040000,050000' as Item
 ) a
 left outer join Row
 on substring(Item,p,1)=',' or p=0
)
select * from ItemList
option (MAXRECURSION 32767)
カンマで区切られたデータを仮想のテーブルのレコードのように分割する
使い方としてはストアドプロシージャに1つの引数としてわたってきたものを分割し
SQLで容易に使えるようにする
複数の引数を分割する場合上記SQLで記述すると比較的わかりやすいものとなる
(ItemListのインラインビューを複数用意すると可能)

索引へ

【値の横縦変換(依存型)】

--select
/* 値の横縦変換(依存型) */
WITH ListStr(Str) AS (
--    select @Str as Str
 select '010000,020,030,040000,050000' as Str
 UNION ALL
 select 
  substring(Str,charindex(',',Str+',')+1,LEN(Str)) as Str
 from ListStr
 where len(Str) > 1
)
,
VStr as (
 select
  substring(Str,1,charindex(',',Str+',')-1) as Str 
 from ListStr
 where Str <> ''
)
select * from VStr
option (MAXRECURSION 32767)
カンマで区切られたデータを仮想のテーブルのレコードのように分割する
使い方としてはストアドプロシージャに1つの引数としてわたってきたものを分割し
SQLで容易に使えるようにする
上記SQLはきれいでないので参考までに!!

索引へ

【値の横縦変換(速い?)】

--select
/* 値の横縦変換(速い?) */
WITH Row(p,Val,Str) AS (
 select
   0 as p
  ,cast('' as Nvarchar(100)) as val
 ,'010000,020,030,040000,050000' as str
 UNION ALL
 select
  p+1 as p 
 ,cast(substring(Str,1,charindex(',',Str+',')-1) as nvarchar(100)) as Val
 ,substring(Str,charindex(',',Str+',')+1,LEN(Str)) as Str
 from Row
 where len(Str)>1
)
select * from Row
where p>0
カンマで区切られたデータを仮想のテーブルのレコードのように分割する
使い方としてはストアドプロシージャに1つの引数としてわたってきたものを分割し
SQLで容易に使えるようにする
複数の引数を分割する場合上記SQLで記述すると比較的わかりやすいものとなる
(Rowのインラインビューを複数用意すると可能)

索引へ

【一番多く使われている値を取得】

select 
/* 一番多く使われている値を取得 */
 TableName
,TypeName
from (
  select
   a.name as TableName
  ,c.name as TypeName
  ,ROW_NUMBER() OVER (PARTITION BY a.name ORDER BY count(*) desc) as seq
  from dbo.sysobjects a 
  left outer join dbo.syscolumns b 
  on a.id = b.id 
  left outer join dbo.systypes c 
  on b.xtype = c.xtype 
  and b.usertype = c.usertype 
  WHERE 1=1
  and a.xtype = 'U'
  group by 
   a.name
,c.name
) a
where seq = 1  --ここが味噌
コードが同じだが名称が違うデータがある場合、
代表名称にもっとも出現回数が多かった名称を使用する場合に使用
「ORDER BY count(*) desc」をアレンジすれば最新などの取得も可能になる

索引へ

【次のレコードを使用する(パフォーマンスは保証できない)】

--select
/* 次のレコードを使用する(パフォーマンスは保証できない) */
WITH Data AS (
select 'A' as CD,cast('20000101' as datetime) as Start UNION ALL
select 'A' as CD,cast('20000201' as datetime) as Start UNION ALL
select 'A' as CD,cast('20000301' as datetime) as Start UNION ALL
select 'A' as CD,cast('20000401' as datetime) as Start UNION ALL
select 'A' as CD,cast('20000501' as datetime) as Start UNION ALL
select 'A' as CD,cast('20000601' as datetime) as Start UNION ALL
select 'A' as CD,cast('20000701' as datetime) as Start UNION ALL
select 'A' as CD,cast('20000801' as datetime) as Start UNION ALL
select 'A' as CD,cast('20000901' as datetime) as Start UNION ALL
select 'A' as CD,cast('20001001' as datetime) as Start UNION ALL
select 'A' as CD,cast('20001101' as datetime) as Start UNION ALL
select 'A' as CD,cast('20001201' as datetime) as Start UNION ALL
select 'B' as CD,cast('20000101' as datetime) as Start UNION ALL
select 'B' as CD,cast('20000201' as datetime) as Start UNION ALL
select 'B' as CD,cast('20000301' as datetime) as Start UNION ALL
select 'B' as CD,cast('20000401' as datetime) as Start UNION ALL
select 'B' as CD,cast('20000501' as datetime) as Start UNION ALL
select 'B' as CD,cast('20000601' as datetime) as Start UNION ALL
select 'B' as CD,cast('20000701' as datetime) as Start UNION ALL
select 'B' as CD,cast('20000801' as datetime) as Start UNION ALL
select 'B' as CD,cast('20000901' as datetime) as Start UNION ALL
select 'B' as CD,cast('20001001' as datetime) as Start UNION ALL
select 'B' as CD,cast('20001101' as datetime) as Start UNION ALL
select 'B' as CD,cast('20001201' as datetime) as Start
)
select 
 a.CD
,a.Start as 開始
,isnull(b.Start,'99991231') as 終了
from (
select 
ROW_NUMBER() OVER (PARTITION BY cd ORDER BY Start) as seq
,CD
,Start
from Data
) a
left outer join (
select 
ROW_NUMBER() OVER (PARTITION BY cd ORDER BY Start) as seq
,CD
,Start
from Data
) b
on  b.CD = a.CD
and b.Seq = a.Seq + 1
where a.CD = 'A'
次のレコードの値を必要とする場合に使用
レコード内に開始日しか持っていない場合終了日を取得する場合に使用

索引へ

【カンマで区切られてデータが入っているフィールドを別々のフィールドに分割】

--select
/* カンマで区切られてデータが入っているフィールドを別々のフィールドに分割 */
with CSVDATA as (
select '1,2,3,4,5,666,7,8,9,0' as csv
union all
select '2,2,3,4,5,6,7,8,9,0' as csv
union all
select '3,2,3,4,5,6,7,8,9,0' as csv
union all
select '4,2,3,4,5,6,7,8,9,0' as csv
union all
select '4,2,3,4,5,6,7,8,9,0' as csv
)
,
Row(p) AS (
 select 1 as p
 UNION ALL
 select p+1 as p from Row
 where p < 32767
)
select 
 no
,csv
,max(case when seq = 01 then val else null end) as val001
,max(case when seq = 02 then val else null end) as val002
,max(case when seq = 03 then val else null end) as val003
,max(case when seq = 04 then val else null end) as val004
,max(case when seq = 05 then val else null end) as val005
,max(case when seq = 06 then val else null end) as val006
,max(case when seq = 07 then val else null end) as val007
,max(case when seq = 08 then val else null end) as val008
,max(case when seq = 09 then val else null end) as val009
,max(case when seq = 10 then val else null end) as val010
,max(case when seq = 12 then val else null end) as val012
,max(case when seq = 13 then val else null end) as val013
,max(case when seq = 14 then val else null end) as val014
,max(case when seq = 15 then val else null end) as val015
,max(case when seq = 16 then val else null end) as val016
,max(case when seq = 17 then val else null end) as val017
from (
  select
   no
  ,substring(csv,2,Len(csv)-2) as csv
  ,ROW_NUMBER() OVER (PARTITION BY no,csv ORDER BY p) as seq
  ,replace(substring(csv,p+1,charindex(',',substring(csv,p+1,len(csv)))),',','') as val
  from (
    select
     ROW_NUMBER() OVER (ORDER BY csv) as no  --データを個別に識別するために必要
    ,','+csv+',' as csv                      --処理しやすいように対象を加工
    from CSVDATA
  ) as CSVDATA
  left outer join Row
  on substring(csv,p,1)=','
) CSVDATA
group by 
 no
,csv
option (MAXRECURSION 32767)
カンマで区切られてデータが入っているフィールドを別々のフィールドに分割
1フィールドにCSVデータが1レコードが入っている場合の分割に使用

索引へ

【CSV形式のデータを各フィールドに分解】

--select
/* CSV形式のデータを各フィールドに分解 */
with 
dt as (                                                                  --サンプルデータ
  select '"",123,456,"",789,"ABC","E,"","",FG","HIJ",0' as csv union all --サンプルデータ
  select '"",000,456,"",789,"ABC","E,"","",FG","HIJ",0' as csv union all --サンプルデータ
  select '"",999,456,"",789,"ABC","E,"","",FG","HIJ",0' as csv union all --サンプルデータ
  select '"",789,456,"",789,"ABC","E,"","",FG","HIJ",0' as csv union all --サンプルデータ
  select '"",123,456,"",789,"ABC","E,"","",FG","HIJ",0' as csv           --サンプルデータ
),                                                                       --サンプルデータ
Row(RowNo,csv,s,e,csv_z) AS (
  select
   RowNo
  ,csv
  ,1 as s
  ,case
   when substring(csv,1,2) = ',"' then charindex('",',replace('  '+substring(csv,1+2,len(csv)),'""','^^'))
   else                                charindex(',' ,replace(     substring(csv,1+1,len(csv)),',' ,',' ))
   end + 1 as e
  ,substring(csv,charindex(',',csv),len(csv)) as csv_z 
  from (
    select
     ROW_NUMBER() OVER (ORDER BY csv) as RowNo
    ,','+csv+',' as csv 
    from dt
  ) dt
  UNION ALL
  select
   RowNo
  ,csv
  ,e as s
  ,case
   when substring(csv,e,2) = ',"' then charindex('",',replace('  '+substring(csv,e+2,len(csv)),'""','^^'))
   else                                charindex(',' ,replace(     substring(csv,e+1,len(csv)),',' ,',' ))
   end + e as e
  ,substring(csv,charindex(',',csv,e+1),len(csv)) as csv_z
  from Row
  where charindex(',',csv,e+1) <> 0
),
CsvData as (
  select
   RowNo
  ,csv
  ,max(case when ColNo = 001 then val end) as val001
  ,max(case when ColNo = 002 then val end) as val002
  ,max(case when ColNo = 003 then val end) as val003
  ,max(case when ColNo = 004 then val end) as val004
  ,max(case when ColNo = 005 then val end) as val005
  ,max(case when ColNo = 006 then val end) as val006
  ,max(case when ColNo = 007 then val end) as val007
  ,max(case when ColNo = 008 then val end) as val008
  ,max(case when ColNo = 009 then val end) as val009
  ,max(case when ColNo = 010 then val end) as val010
  ,max(case when ColNo = 011 then val end) as val011
  ,max(case when ColNo = 012 then val end) as val012
  ,max(case when ColNo = 013 then val end) as val013
  ,max(case when ColNo = 014 then val end) as val014
  ,max(case when ColNo = 015 then val end) as val015
  ,max(case when ColNo = 016 then val end) as val016
  ,max(case when ColNo = 017 then val end) as val017
  ,max(case when ColNo = 018 then val end) as val018
  ,max(case when ColNo = 019 then val end) as val019
  ,max(case when ColNo = 020 then val end) as val020
  ,max(case when ColNo = 021 then val end) as val021
  ,max(case when ColNo = 022 then val end) as val022
  ,max(case when ColNo = 023 then val end) as val023
  ,max(case when ColNo = 024 then val end) as val024
  ,max(case when ColNo = 025 then val end) as val025
  ,max(case when ColNo = 026 then val end) as val026
  ,max(case when ColNo = 027 then val end) as val027
  ,max(case when ColNo = 028 then val end) as val028
  ,max(case when ColNo = 029 then val end) as val029
  ,max(case when ColNo = 030 then val end) as val030
  from (
    select
     RowNo
    ,ROW_NUMBER() OVER (PARTITION BY RowNo ORDER BY s) as ColNo
    ,csv
    ,s
    ,e
    ,case 
     when substring(csv,s+1,1)='"' then replace(substring(csv,s+2,e-s-3),'""','"')
     else                               substring(csv,s+1,e-s-1)
     end as val
    from Row
  ) a
  group by RowNo,csv
)
select
 * 
from CsvData
order by RowNo
option (MAXRECURSION 10000)
1つのフィールドに入っているCSV形式のデータを個別のフィールドに分解し格納
サンプルデータ部分に実際のテーブルに置き換えれば実際に使えます
(データが格納されているフィールドは「csv」の名前にあわせます)
フィールドの数にあわせて
,max(case when ColNo = 999 then val end) as val999
の数を調整

想定するCSVは
[,]区切り
文字フィールドは["]文字で開始/終了
["]文字で挟まれた中にある[,]は区切り文字として扱わない
["]文字で挟まれた中にある["]は2重表現[""]されている
































102