.NET で SQL Server/Database のbacpac を取得する

質問されたので書いときます。

DacFx 使ってサクッと出来ますよ~。
NuGet Gallery | Microsoft.SqlServer.DacFx.x64 150.4384.2

var connstr = @"~";
var dac = new DacServices(connstr);
dac.ExportBacpac(@"d:\TurorialDB.bacpac", "TutorialDB");

細かいオプション渡したい場合は、ドキュメント見てください。
ドキュメントは1年前のバージョンのようでちょっと古いですが、そんな大幅に変わってるとこは無さそう?
DacServices Class (Microsoft.SqlServer.Dac) | Microsoft Docs

注意点は .NET Core の DacFx は未だ正式提供されていないので、.NET Framework でお願いします。
※.NET Core のは Preview で提供されています。
NuGet Gallery | Microsoft.SqlServer.DACFx 150.4384.2-preview

これくらいなら PowerShell で書いてしまってもいいかもしれません。
アセンブリの参照だけ気を付けたらサクッと書けると思います。
Microsoft.SqlServer.Dac.dll は依存してる dll が幾つかあるので、それを読めるとこに置いとく必要あり。

メモ:コネクションプールを使ってる時の SqlConnection を Close した時の内部動作

GitHub 上でコード追っかけたのでメモ
※実行してトレースしたわけでは無いので間違ってるかも

生きてる SqlTransaction があっても、Rollback 呼んでそう。

SqlConnection.Close() で SqlConnection.CloseInnerConnection()
https://github.com/dotnet/corefx/blob/v2.2.3/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlConnection.cs#L683

SqlConnection.CloseInnerConnection() で InnerConnection.CloseConnection()
https://github.com/dotnet/corefx/blob/v2.2.3/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlConnection.cs#L637

SqlClient.InnerConnection の定義
https://github.com/dotnet/corefx/blob/v2.2.3/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlConnectionHelper.cs#L92

DbConnectionInternal.CloseConnection() で connectionPool.PutObject()
https://github.com/dotnet/corefx/blob/v2.2.3/src/System.Data.SqlClient/src/System/Data/ProviderBase/DbConnectionInternal.cs#L270

DbConnectionPool.PutObject() で DeactivateObject()
https://github.com/dotnet/corefx/blob/v2.2.3/src/System.Data.SqlClient/src/System/Data/ProviderBase/DbConnectionPool.cs#L1454

DbConnectionPool.DeactiveObject() で DbConnectionInternal.DeactivateConnection()
https://github.com/dotnet/corefx/blob/v2.2.3/src/System.Data.SqlClient/src/System/Data/ProviderBase/DbConnectionPool.cs#L762

DbConnectionInternal.DeactiveConnection() で Deactivate()
https://github.com/dotnet/corefx/blob/v2.2.3/src/Common/src/System/Data/ProviderBase/DbConnectionInternal.cs#L241

SqlInternalConnection.Deactivate() で InternalDeactivate()
https://github.com/dotnet/corefx/blob/v2.2.3/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlInternalConnection.cs#L262

SqlinternalConnectionTds.InternalDeactivate() で _parse.Deactivate()
https://github.com/dotnet/corefx/blob/v2.2.3/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlInternalConnectionTds.cs#L768

TdsParser.Deactivate() で currentTransaction.CloseFromConnection()
https://github.com/dotnet/corefx/blob/v2.2.3/src/System.Data.SqlClient/src/System/Data/SqlClient/TdsParser.cs#L924

SqlInternalTransaction.CloseFromConnection() で innerConnection.ExecuteTransaction()
https://github.com/dotnet/corefx/blob/v2.2.3/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlInternalTransaction.cs#L253

SqlInternalConnectionTds.ExecuteTransaction() で ExecuteTransactionYukon()
https://github.com/dotnet/corefx/blob/v2.2.3/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlInternalConnectionTds.cs#L857

SqlInternalConnectionTds.ExecuteTransactionYukon() で _parser.TdsExecuteTransactionManagerRequest() https://github.com/dotnet/corefx/blob/v2.2.3/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlInternalConnectionTds.cs#L986

TdsParser.TdsExecuteTransactionManagerRequest https://github.com/dotnet/corefx/blob/v2.2.3/src/System.Data.SqlClient/src/System/Data/SqlClient/TdsParser.cs#L6608

名前に Yukon とか出てきたけど、2005 のコードネームやっけ。

メモ:SQL Server 2019 Hybrid Buffer Pool

2019 の新機能見てて何やろ~?って思ったのでメモ。
Hybrid Buffer Pool - SQL Server | Microsoft Docs
2019 CTP2.1 から。

PMEM に データベース ファイル を置いてると、バッファープール に読み込まずに直接読むっぽい。
直接読むのは クリーン ページ だけ、ダーティページは今まで通りの動き。
ダーティページって何や~?ってサクッと書くと、変更があったけど未だ物理に書き込まれていない状態のページのこと。

NVDIMM-N 対応は、2016 SP1 で入った ログファイル を NVDIMM に置いてログバッファに使う Tail of Log ってのがあったけど、
Transaction Commit latency acceleration using Storage Class Memory in Windows Server 2016/SQL Server 2016 SP1 | SQL Database Engine Blog
Configuring Storage Spaces with a NVDIMM-N write-back cache - SQL Server | Microsoft Docs
今回のでデータファイルも PMEM に置いてパフォーマンス向上するって感じなんですかね。

環境無いので試せてないからアレ。

メモ:create table で filtered な unique index を作る

ドキュメントの構文に載ってないけどなんか出来たのでメモ。

SQL Server での unique 制約は unique index で実現されてます。
なので、unique 制約を作る = unique index を作る です。

UNIQUE 制約を作る場合
Create Unique Constraints - SQL Server | Microsoft Docs

後から UNIQUE INDEX 作る場合
Create Unique Indexes - SQL Server | Microsoft Docs

filtered ってなんやーですが、SQL Server では index を作る時に条件指定して index を作ることが出来ます。
Create Filtered Indexes - SQL Server | Microsoft Docs

特定の状態のデータに対して index を作りたい場合に使う感じ。
これを利用すると、複数行に null が含む列に対して unique 制約を付けることも出来ます。*1

で、create table の時にまとめて作るのはドキュメントでは出来なさそうだけど、何か書けたのでメモ。
CREATE TABLE (Transact-SQL) - SQL Server | Microsoft Docs

ドキュメントに無い構文だけど、エラーが出なかったクエリ例

create table [サンプル] (
  [有効なデータでは一意なId] bigint not null, 
  [状態] nchar(2) not null,
  index [UX_サンプル_Id] unique ([有効なデータでは一意なId]) where [状態] = N'有効'
)

ドキュメントでは

< table_constraint > ::=  
[ CONSTRAINT constraint_name ]   
{   
    { PRIMARY KEY | UNIQUE }   
        [ CLUSTERED | NONCLUSTERED ]   
        (column [ ASC | DESC ] [ ,...n ] )   
        [   
            WITH FILLFACTOR = fillfactor   
           |WITH ( <index_option> [ , ...n ] )   
        ]  
        [ ON { partition_scheme_name (partition_column_name)  
            | filegroup | "default" } ]   
    | FOREIGN KEY   
        ( column [ ,...n ] )   
        REFERENCES referenced_table_name [ ( ref_column [ ,...n ] ) ]   
        [ ON DELETE { NO ACTION | CASCADE | SET NULL | SET DEFAULT } ]   
        [ ON UPDATE { NO ACTION | CASCADE | SET NULL | SET DEFAULT } ]   
        [ NOT FOR REPLICATION ]   
    | CHECK [ NOT FOR REPLICATION ] ( logical_expression )  

< table_index > ::=   
{  
    {  
      INDEX index_name [ CLUSTERED | NONCLUSTERED ]   
         (column_name [ ASC | DESC ] [ ,... n ] )   
    | INDEX index_name CLUSTERED COLUMNSTORE  
    | INDEX index_name [ NONCLUSTERED ] COLUMNSTORE (column_name [ ,... n ] )  
    }  
    [ WITH ( <index_option> [ ,... n ] ) ]   
    [ ON { partition_scheme_name (column_name )   
         | filegroup_name  
         | default   
         }  
    ]   
    [ FILESTREAM_ON { filestream_filegroup_name | partition_scheme_name | "NULL" } ]  

}  

ってなってるので、constraint で unique 付ける場合に where は指定出来ないとなってるし、
index の方では、unique 指定出来ないし where もダメってなってる。

普通に unique index を作る場合は、

create unique index [UX_サンプル_Id] on [サンプル] ([有効なデータでは一意なId]) where [状態] = N'有効'

って構文なので、unique の位置が変わるのは注意点?

ただ、ドキュメントに載ってないのでどのバージョンで使えるのかは動かしてみないと分からない。。
とりあえず SQL Server 2016、SQL Database v12 では動いてる。
SQL DB v12 で動いてるから、新しい SQL Server なら問題なさそう。

*1:not null を条件に付ける

02/19(火) SQLWorld★大阪#49 開催します #sqlworld

2019年最初の SqlWorld :: SQLWorld★大阪#49 開催します! 31回目の平日夜開催で、前回同様 ハンズオン 形式行う予定です。

【日時】
2019年02月19日(火曜日) 19:00~21:00
 

【イベント概要】
2019年最初の SQLWorld、今回で31回目の平日夜開催~。今回も、みんなで SQL を書いてみようというハンズオン企画です!ブラウザがあれば参加出来るようにしていますので、iPad 等のタブレットでも大丈夫です。
 

【会場】
フェンリル株式会社さま大阪本社 http://www.fenrir-inc.com/
〒530-0011 大阪府大阪市北区大深町 3番1号 グランフロント大阪タワーB(オフィス)
 

【参加費】
無料
 

【持ち物】
パソコン/タブレット (DB のインストールは不要です。)
 

【参加可能人数】
13 人
 

お題に沿って、SQL を書いてみようという勉強会です。是非ご参加を~。
今回は初心者向けの優しめの問題を考え中です。

開催回数は増えていっていますが、続き物というわけでは無いので初めて参加される方でもお気軽にどぞー。

複合主キーを代理キー(サロゲートキー)に変えると、外部キー制約が弱くなる時がある

外部キー制約が弱くなるってなんやって話しですが、分かりやすいように例を交えて

取引先と取引先組織が親子関係、注文には2つの組織(請求先組織, 出荷先組織)を持つとした場合に、

取引先組織を複合主キーのまま定義するとこんな感じ。

create table [取引先] (
  [取引先Id] bigint not null primary key,
  [名称] nvarchar(100) not null
)
create table [取引先組織] (
  [取引先Id] bigint not null foreign key references [取引先] ([取引先Id]),
  [連番] bigint not null,
  [名称] nvarchar(100) not null,
  [住所] nvarchar(300) not null
  primary key ([取引先Id], [連番])
)
create table [注文] (
  [注文Id] bigint not null primary key,
  [取引先Id] bigint not null foreign key references [取引先] ([取引先Id]),
  [請求先組織連番] bigint not null,
  [出荷先組織連番] bigint not null,
  foreign key ([取引先Id], [請求先組織連番]) references [取引先組織] ([取引先Id], [連番]),
  foreign key ([取引先Id], [出荷先組織連番]) references [取引先組織] ([取引先Id], [連番])
)

これを複合主キーをやめて代理キーにするとこんな感じ。

create table [取引先] (
  [取引先Id] bigint not null primary key,
  [名称] nvarchar(100) not null
)
create table [取引先組織_サロゲートキー] (
  [取引先組織Id] bigint not null primary key,
  [取引先Id] bigint not null foreign key references [取引先] ([取引先Id]),
  [名称] nvarchar(100) not null,
  [住所] nvarchar(300) not null
)
create table [注文_サロゲートキー] (
  [注文Id] bigint not null primary key,
  [取引先Id] bigint not null foreign key references [取引先] ([取引先Id]),
  [請求先組織Id] bigint not null foreign key references [取引先組織_サロゲートキー] ([取引先組織Id]),
  [出荷先組織Id] bigint not null foreign key references [取引先組織_サロゲートキー] ([取引先組織Id])
)

一見代理キーの方がスッキリして見えるんだけど、注文の2組織が、「取引先に属している組織」かどうかが DB の制約で担保出来なくなってる。
複合主キーの場合は、外部キー制約の中に 取引先Id を含んでいるので、2組織とも取引先に属している組織って担保出来てる。

複合主キーだと in 句で指定出来ないとかデメリットもあるけど、制約で整合性を担保出来る範囲が広くなるケースもあるのでケースバイケースなんすかねー。