ASP.NET MVC AD認証ログイン
注意
このポストはまだ成功確認が取れていない状態のメモです。 環境が整い次第テストを行う予定で、そのときのための準備になります。
AD認証でのログインについて調べる
Visual StudioのテンプレートではUseOpenIdConnectAuthenticationにより、OpenIdを利用したADログインになるため、 やりたいことの参考が出来なかったため、調べた事をメモする。
MVC Web ApplicationをMVCで認証なしで作成する。
プロジェクトにNugetから追加する。
参照にPrincipalContextを使うためのライブラリを追加
- System.DirectoryServices
- System.DirectoryServices.AccountManagement
User
IUserを継承したユーザを作成する。 ただし、生成にはUserPrincpalから取得するようにする。
using Microsoft.AspNet.Identity; using System.DirectoryServices.AccountManagement; using System.Threading.Tasks; public class AppUser : IUser<string> { private UserPrincipal _adUser; public AppUser(UserPrincipal adUser) { this._adUser = adUser; } public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<AppUser> manager) { var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie); return userIdentity; } #region IUser<string> Members public string Id => _adUser.SamAccountName; public string UserName { get => _adUser.SamAccountName; set { throw new System.NotImplementedException(); } } #endregion }
UserStore
ユーザ関連のCLUD処理を行うStoreクラスを定義する。 PrincipalContextからFindByIdentityにより、ユーザの取得を行う。
using Microsoft.AspNet.Identity; using System.DirectoryServices.AccountManagement; using System.Threading.Tasks; public class AppUserStore : IUserStore<AppUser>, IUserStore<AppUser, string> { private readonly PrincipalContext _context; private AppUserStore(PrincipalContext context) { _context = context; } // context.Get<PrincipalContext>()でADのcontextを取得 public static AppUserStore Create(IdentityFactoryOptions<AppUserStore> options, IOwinContext context) { // PrincipalContextをOwinContextから取得するようにする。StartUpでCreatePerOwinContextに追加しておく //var principalContext = new PrincipalContext(ContextType.Domain); var principalContext = context.Get<PrincipalContext>(); return new AppUserStore(principalContext); } #region IUserStore<MyUser, string> Members public Task CreateAsync(AppUser user) { throw new NotImplementedException(); } public Task DeleteAsync(AppUser user) { throw new NotImplementedException(); } public void Dispose() { throw new NotImplementedException(); } // UserPrincipal.FindByIdentityにより、検索する public Task<AppUser> FindByIdAsync(string userId) { var user = UserPrincipal.n(_context, userId); return Task.FromResult<AppUser>(new AppUser(user)); } public Task<AppUser> FindByNameAsync(string userName) { var user = UserPrincipal.FindByIdentity(_context, userName); return Task.FromResult<AppUser>(new AppUser(user)); } public Task UpdateAsync(AppUser user) { throw new NotImplementedException(); } #endregion }
UserMananger
UserStoreクラスの総裁を行うクラス
using Microsoft.AspNet.Identity; using System.DirectoryServices.AccountManagement; using System.Threading.Tasks; public class AppUserMananger : UserManager<AppUser> { private readonly PrincipalContext _context; private AppUserMananger(IUserStore<AppUser> store, PrincipalContext context) : base(store) { _context = context; } public static AppUserMananger Create(IdentityFactoryOptions<AppUserMananger> options, IOwinContext context) { var userStore = context.Get<AppUserStore>(); var principalContext = context.Get<PrincipalContext>(); return new AppUserMananger(userStore, principalContext); } public override async Task<bool> CheckPasswordAsync(AppUser user, string password) { return await Task.FromResult(_context.ValidateCredentials(user.UserName, password, ContextOptions.Negotiate)); } }
SignInManager
ログイン状態を管理するクラス
using Microsoft.AspNet.Identity; using System.DirectoryServices.AccountManagement; using System.Threading.Tasks; public class AppSignInManager : SignInManager<AppUser, string> { UserManager<AppUser, string> _manager; private AppSignInManager(UserManager<AppUser, string> userManager, IAuthenticationManager authenticationManager) : base(userManager, authenticationManager) { _manager = UserManager; } public static AppSignInManager Create(IdentityFactoryOptions<AppSignInManager> options, IOwinContext context) { // UserManagerはOwinContextから取得する。 AppUserMananger manager = context.GetUserManager<AppUserMananger>(); return new AppSignInManager(manager, context.Authentication); } public override Task<ClaimsIdentity> CreateUserIdentityAsync(AppUser user) { return user.GenerateUserIdentityAsync((AppUserMananger)UserManager); } public override async Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout) { var result = SignInStatus.Failure; try { var user = await this.UserManager.FindAsync(userName, password); if (user != null) { await this.SignInAsync(user, isPersistent, true); result = SignInStatus.Success; } } catch { result = SignInStatus.Failure; } return result; } }
StartUp
using Owin; using Microsoft.Owin.Security; using Microsoft.Owin.Security.Cookies; using Microsoft.AspNet.Identity; using System.Threading.Tasks; using Microsoft.AspNet.Identity.Owin; using Microsoft.Owin; using System.DirectoryServices.AccountManagement; using System.Security.Claims; [assembly: OwinStartup(typeof(MvcWeb.OwinStartUp))] public class OwinStartUp { public void Configuration(IAppBuilder app) { app.CreatePerOwinContext(() => new PrincipalContext(ContextType.Domain)); app.CreatePerOwinContext<AppUserMananger>(AppUserMananger.Create); app.CreatePerOwinContext<AppSignInManager>(AppSignInManager.Create); app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType); app.UseCookieAuthentication(new CookieAuthenticationOptions() { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/Account/Login"), Provider = new CookieAuthenticationProvider { OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<AppUserMananger, AppUser>( validateInterval: TimeSpan.FromMinutes(30), regenerateIdentity: (manager,user) => user.GenerateUserIdentityAsync(manager)) } }); } }
LoginViewModel
public class LoginViewModel { [Required] [Display(Name = "ユーザID")] public string UserID { get; set; } [Required] [DataType(DataType.Password)] [Display(Name = "パスワード")] public string Password { get; set; } [Display(Name = "このアカウントを記憶する")] public bool RememberMe { get; set; } }
AccountController
http://localhost/Acount/LoginからADのユーザ名、パスワードでログインする処理
[Authorize] public class AccountController : Controller { // // GET: /Account/Login [AllowAnonymous] public ActionResult Login(string returnUrl) { ViewBag.ReturnUrl = returnUrl; return View(); } // // POST: /Account/Login [HttpPost] [AllowAnonymous] public async Task<ActionResult> Login(LoginViewModel model, string returnUrl) { if (!ModelState.IsValid) { return View(model); } AppSignInManager signInManager = HttpContext.GetOwinContext().Get<AppSignInManager>(); AppUserMananger userManager = HttpContext.GetOwinContext().GetUserManager<AppUserMananger>(); // これは、アカウント ロックアウトの基準となるログイン失敗回数を数えません。 // パスワード入力失敗回数に基づいてアカウントがロックアウトされるように設定するには、shouldLockout: true に変更してください。 var result = await signInManager.PasswordSignInAsync(model.UserID, model.Password, model.RememberMe, shouldLockout: false); switch (result) { case SignInStatus.Success: return RedirectToLocal(returnUrl); case SignInStatus.LockedOut: return View("Lockout"); case SignInStatus.RequiresVerification: return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe }); case SignInStatus.Failure: default: ModelState.AddModelError("", "無効なログイン試行です。"); return View(model); } } private ActionResult RedirectToLocal(string returnUrl) { if (Url.IsLocalUrl(returnUrl)) { return Redirect(returnUrl); } return RedirectToAction("Index", "Home"); } }
Login View
@using MvcWeb.Models @model LoginViewModel @{ ViewBag.Title = "ログイン"; } <h2>@ViewBag.Title.</h2> <div class="row"> <div class="col-md-8"> <section id="loginForm"> @using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" })) { @Html.AntiForgeryToken() <h4>ローカル アカウントを使用してログインします。</h4> <hr /> @Html.ValidationSummary(true, "", new { @class = "text-danger" }) <div class="form-group"> @Html.LabelFor(m => m.UserID, new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.TextBoxFor(m => m.UserID, new { @class = "form-control" }) @Html.ValidationMessageFor(m => m.UserID, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.PasswordFor(m => m.Password, new { @class = "form-control" }) @Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <div class="checkbox"> @Html.CheckBoxFor(m => m.RememberMe) @Html.LabelFor(m => m.RememberMe) </div> </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="ログイン" class="btn btn-default" /> </div> </div> <p> @Html.ActionLink("新しいユーザーとして登録する", "Register") </p> @* これを有効にする前に、パスワード リセット機能に対するアカウント確認を有効にしてください。 <p> @Html.ActionLink("パスワードを忘れた場合", "ForgotPassword") </p>*@ } </section> </div> <div class="col-md-4"> <section id="socialLoginForm"> @Html.Partial("_ExternalLoginsListPartial", new ExternalLoginListViewModel { ReturnUrl = ViewBag.ReturnUrl }) </section> </div> </div> @section Scripts { @Scripts.Render("~/bundles/jqueryval") }
ASP.Net MVC Identity 認証
ASP.NET MVC プロジェクトを作成
認証を選択するとデフォルトの認証ロジックが貼るため、認証なしで作成する。
プロジェクトにNugetから追加する。
AppUser
UserNameとPasswordのみの簡単なものにします。 適当なフォルダーに作成します。
public class AppUser : IUser<string> { public string Id { get; set; } = Guid.NewGuid().ToString(); public string UserName { get; set; } public string Password { get; set; } }
UserManager
UserStoreクラスを制御するラッピングクラス
Create(IdentityFactoryOptions, IOwinContext)を実装して、インスタンス生成時に呼ばれる様にしている。 現状としてはどんな仕組みで呼び出し時にCreateでパラメータを渡さずに動いているのかがさっぱりわからない。
using Microsoft.AspNet.Identity; public class AppUserManager : UserManager<AppUser> { private AppUserManager(IUserStore<AppUser> store) : base(store) { } public static AppUserMananger Create(IdentityFactoryOptions<AppUserMananger> options, IOwinContext context) { return new AppUserMananger(new AppUserStore()); } }
IRole
public class AppRole : IRole<string> { public string Id { get; set; } = Guid.NewGuid().ToString(); public string Name { get; set ; } }
RoleMananager
using Microsoft.AspNet.Identity; public class AppRoleManager : RoleManager<AppRole> { public AppRoleManager(IRoleStore<AppRole, string> store) : base(store) { } }
SignInManager
using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.Owin; using Microsoft.Owin; using Microsoft.Owin.Security; public class AppSignInManager : SignInManager<AppUser, string> { private AppSignInManager(UserManager<AppUser, string> userManager, IAuthenticationManager authenticationManager) : base(userManager, authenticationManager) { } public static AppSignInManager Create(IdentityFactoryOptions<AppSignInManager> options, IOwinContext context) { AppUserMananger manager = context.GetUserManager<AppUserMananger>(); return new AppSignInManager(manager, context.Authentication); } }
UserStore
ユーザー情報を扱うためのクラス(CRUD処理)
public class AppUserStore : IUserStore<AppUser>, IUserStore<AppUser,string>, IUserPasswordStore<AppUser,string>, IUserRoleStore<AppUser, string>, IRoleStore<AppRole, string> { // DBの代わりにダミーデータ設定 // ユーザマスタ private static List<AppUser> Users { get; } = new List<AppUser> { new AppUser { Id = "1", UserName = "user1", Password="abc" }, new AppUser { Id = "2", UserName = "user2", Password="def" } }; // ロールマスタ private static List<AppRole> Roles { get; } = new List<AppRole> { new AppRole { Id = "1", Name = "emploryee"}, new AppRole { Id = "999", Name = "admin" } }; // ユーザー・ロールマスタ private static List<Tuple<string, string>> UserRoleMap { get; } = new List<Tuple<string, string>> { Tuple.Create("1", "1"), Tuple.Create("2","999") }; public Task AddToRoleAsync(AppUser user, string roleName) { var role = Roles.FirstOrDefault(x => x.Name == roleName); if (role == null) { throw new InvalidOperationException(); } var userRoleMap = UserRoleMap.FirstOrDefault(x => x.Item1 == user.Id && x.Item2 == role.Id); if (userRoleMap == null) { UserRoleMap.Add(Tuple.Create(user.Id, role.Id)); } return Task.Delay(0); } public Task CreateAsync(AppUser user) { Users.Add(user); return Task.Delay(0); } public Task CreateAsync(AppRole role) { Roles.Add(role); return Task.Delay(0); } public Task DeleteAsync(AppUser user) { Users.Remove(Users.First(x => x.Id == user.Id)); return Task.Delay(0); } public Task DeleteAsync(AppRole role) { Roles.Remove(Roles.First(x => x.Id == role.Id)); return Task.Delay(0); } public void Dispose() { } public Task<AppUser> FindByIdAsync(string userId) { return Task.FromResult(Users.FirstOrDefault(u => u.Id == userId)); } public Task<AppUser> FindByNameAsync(string userName) { return Task.FromResult(Users.FirstOrDefault(u => u.UserName == userName)); } public Task<string> GetPasswordHashAsync(AppUser user) { return Task.FromResult(new PasswordHasher().HashPassword(user.Password)); } public Task<IList<string>> GetRolesAsync(AppUser user) { IList<string> roleNames = UserRoleMap.Where(x => x.Item1 == user.Id) .Select(x => x.Item2) .Select(x => Roles.First(y => y.Id == x)) .Select(x => x.Name) .ToList(); return Task.FromResult(roleNames); } public Task<bool> HasPasswordAsync(AppUser user) { return Task.FromResult(user.Password != null); } public async Task<bool> IsInRoleAsync(AppUser user, string roleName) { var roles = await this.GetRolesAsync(user); return roles.FirstOrDefault(x => x.ToUpper() == roleName.ToUpper()) != null; } public Task RemoveFromRoleAsync(AppUser user, string roleName) { var role = Roles.FirstOrDefault(x => x.Name == roleName); if (role == null) { return Task.FromResult(default(object)); } var userRoleMap = UserRoleMap.FirstOrDefault(x => x.Item1 == user.Id && x.Item2 == role.Id); if (UserRoleMap != null) { UserRoleMap.Remove(userRoleMap); } return Task.Delay(0); } public Task SetPasswordHashAsync(AppUser user, string passwordHash) { user.Password = passwordHash; return Task.Delay(0); } public async Task UpdateAsync(AppUser user) { var target = await this.FindByIdAsync(user.Id); if (target == null) { return; } target.UserName = user.UserName; target.Password = user.Password; return; } public Task UpdateAsync(AppRole role) { var t = Roles.FirstOrDefault(r => r.Id == role.Id); if (t == null) { return Task.FromResult(default(object)); } t.Name = role.Name; return Task.FromResult(default(object)); } Task<AppRole> IRoleStore<AppRole, string>.FindByIdAsync(string roleId) { return Task.FromResult(Roles.FirstOrDefault(u => u.Id == roleId)); } Task<AppRole> IRoleStore<AppRole, string>.FindByNameAsync(string roleName) { return Task.FromResult(Roles.FirstOrDefault(u => u.Name == roleName)); } }
StartUp
ルートのStartUp.csを作成します。
using System; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.Owin; using Microsoft.Owin; using Microsoft.Owin.Security; using Microsoft.Owin.Security.Cookies; using Owin; [assembly: OwinStartup(typeof(MvcWeb2.OwinStartUp))] namespace MvcWeb2 { public class OwinStartUp { public void Configuration(IAppBuilder app) { // 後から「HttpContext.GetOwinContext().Get」で取り出して使える。 // Visual Studioのテンプレートに書いているコメントですが、だそうです。(DBは実装していないので、DbContextは作成していません。 app.CreatePerOwinContext<AppUserMananger>(AppUserMananger.Create); app.CreatePerOwinContext<AppSignInManager>(AppSignInManager.Create); app.CreatePerOwinContext<AppRoleManager>((options, context) => new AppRoleManager(context.Get<AppUserStore>())); app.CreatePerOwinContext<AppSignInManager>((options, context) => new AppSignInManager(context.GetUserManager<AppUserManager>(), context.Authentication)); app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/Auth/Login") }); } } }
Controller
using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.Owin; using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using System.Web; using System.Web.Mvc; [Authorize] public class AuthController : Controller { private AppSignInManager _appSignInManager; private AppUserManager _appUserManager; public AppSignInManager SignInManager { get { // StartUp.csでCreatePerOwinContext登録したオブジェクトを取り出す。 return _appSignInManager ?? HttpContext.GetOwinContext().Get<AppSignInManager>(); } private set { _appSignInManager = value; } } public AppUserManager UserManager { get { // StartUp.csでCreatePerOwinContext登録したオブジェクトを取り出す。 return _appUserManager ?? HttpContext.GetOwinContext().Get<AppUserManager>(); } private set { _appUserManager = value; } } // GET: Auth/Login [AllowAnonymous] public ActionResult Login(string returnUrl) { ViewBag.ReturnUrl = returnUrl; return View(new LoginViewModel()); } // // POST: /Auth/Login [AllowAnonymous] [HttpPost] public async Task<ActionResult> Login(LoginViewModel model, string returnUrl) { if (!this.ModelState.IsValid) { ViewBag.ReturnUrl = returnUrl; return View(model); } // 認証 (SignInManagerを使用しない場合) //var userManager = new UserManager<AppUser>(new AppUserStore()); //var user = await userManager.FindAsync(model.UserName, model.Password); //if (user == null) //{ // // 認証失敗したらエラーメッセージを設定してログイン画面を表示する // this.ModelState.AddModelError("", "ユーザ名かパスワードが違います"); // ViewBag.ReturnUrl = returnUrl; // return View(model); //} //// クレームベースのIDを作って //var identify = await userManager.CreateIdentityAsync( // user, // DefaultAuthenticationTypes.ApplicationCookie); //// 認証情報を設定 //var authentication = this.HttpContext.GetOwinContext().Authentication; //authentication.SignIn(identify); // SignInManagerを利用する場合 var user = await UserManager.FindAsync(model.UserName, model.Password); if (user == null) { this.ModelState.AddModelError("", "ユーザ名かパスワードが違います"); return View(model); } await SignInManager.SignInAsync(user, false, false); // 元のページへリダイレクト return Redirect(returnUrl); } }
Auth/LoginView
@model MvcWeb2.Controllers.LoginViewModel @{ ViewBag.Title = "Login"; } <h2>Login</h2> @Html.ValidationSummary(true) @* ReturnUrlをパラメータに渡すようにしたフォームを作る *@ @using (Html.BeginForm(new { ReturnUrl = ViewBag.ReturnUrl })) { <div class="form-group"> @Html.LabelFor(m => m.UserName) @Html.TextBoxFor(m => m.UserName) @Html.ValidationMessageFor(m => m.UserName) </div> <div class="form-group"> @Html.LabelFor(m => m.Password) @Html.PasswordFor(m => m.Password) @Html.ValidationMessageFor(m => m.Password) </div> <input type="submit" value="ログイン" class="btn-default" /> }
Auth/LoginViewModel
public class LoginViewModel { [Required(ErrorMessage = "ユーザー名を入れてください")] [Display(Name = "ユーザー名")] public string UserName { get; set; } [Required(ErrorMessage = "パスワードを入れてください")] [Display(Name = "パスワード")] public string Password { get; set; } }
SQL ServerのIdentityカラムの最大値
Identity カラム設定
int型で生成した場合、最大値は「21億(正確には2,147,483,647)」まで採番可能になる。
CREATE TABLE [table1] ( [IDNO] int NOT NULL IDENTITY (1, 1), CONSTRAINT [PK_T_SYSTEM_MON_NEW_HISTORY] PRIMARY KEY CLUSTERED ( [IDNO] ASC ) )
ただし、最大値を超えた場合はエラーとなる。
Identity 最大値を超えた場合
- 型をより大きい型(bigintなど)へ変更する。
- DBCC CHECKIDENTコマンドで番号を初期化する。
-- ID値を0に設定 DBCC CHECKIDENT ('table1', RESEED, 0); -- ID値を100に設定 DBCC CHECKIDENT ('table1', RESEED, 100);
ファイルを分割する。
splitを利用し、大きいファイルを分割する。
split -b 2000m filename.zip filename.zip.
-> 結果 filename.zip.ab, filename.zip.ac, ... 2GB単位で分割される。
分割されたファイルを結合する
cat filename.zip.* > filename.zip
tar圧縮したファイルをそのまま分割する。
tar cvf - *.* | split -b 200m - filename.tar_
-> 結果 filename.tar_ab, filenametar_ac, ... 200MB単位で分割される。
分割りしたファイルを結合と同時に解凍する。
cat filename.tar_* | tar -xvf -
SQL Server LocalDb利用する
LocalDbの作成
Visual Studioから「表示」-「SQL Server オブジェクトエキスプローラー」を表示する。
ローカルDBのデータベースより、新しいデータベースを作成
DB名とファイルの場所を指定して、作成する。
Visual Studioのプロジェクトに追加する。
一番簡単だったのはEntityFrameworkを利用する場合だった。 プロジェクトに新しい項目追加で「ADO.NET Entity Data Model」を追加する。 エータベースからEF Desiginerを選択し、対象のファイルを選択する。
新しいファイルを選択する場合は「新しい接続」より、対象ファイルを選択する。
プロジェクトにモデルデザイナが追加され、「App.config」に「connectionStrings」が追加されている事が確認できる。
<connectionStrings> <add name="App1DBEntities" connectionString="metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;provider connection string="data source=(LocalDB)\MSSQLLocalDB;attachdbfilename=|DataDirectory|App1DB.mdf;integrated security=True;connect timeout=30;MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient" /> </connectionStrings>
自動でプロジェクトにmdfファイルがコピーされて入り、filepathがDataDirectoryになってしまう。
※ 特定フォルダーのファイルを参照したい場合は、|DataDirectory|をローカルパスに変更し、プロジェクト内のmdfファイルは削除する。
※ connectionStringsをLocalDBのプロパティの接続文字列を設定しても同じ動作になる。
<connectionStrings> <add name="App1DBEntities" connectionString="(LocalDB)\MSSQLLocalDB;AttachDbFilename=C:\LocalDB\App1DB.mdf;Integrated Security=True;Connect Timeout=30" providerName="System.Data.EntityClient" /> </connectionStrings>
ソース
App1DBEntities db = new App1DBEntities(); foreach (Users item in db.Users) { Console.WriteLine(item.name); }
DBの最新情報更新
DBの最新状態をソースに反映する方法、Entity Data Model画面上で「エータベースからモデルを更新」で最新化できる。
SQL Serverで外部ファイルをコマンドライン実行する
外部ファイルをコマンドラインから実行
サンプル db_nameに接続し、script.sqlを実行し、処理結果をscript.logファイルに出力する。
sqlcmd -U "sa" -P "password" -S "localhost" -d db_name -i script.sql -o script.log
サーバー認証の場合、-E
sqlcmd -S "localhost" -E -d db_name -i script.sql -o script.log
直接SQL文を実行する場合
sqlcmd -S "localhost" -E -d db_name -Q "select getdate(); -o script.log
- -qと-Qの違いQの場合、処理後、SQLCMDを終了する。
複数ファイルを実行する場合、カンマ具切りつなぐだけ
sqlcmd -S "localhost" -E -d db_name -i script1.sql,script2.sql,script3.sql -o script.log
ログ・ファイルを追記にする
sqlcmd -S "localhost" -E -d db_name -Q "select getdate(); >> script.log
SQL ServerのManagement Studioでは早いがアプリケーションで実行すると遅くなる
Management Studioでのクエリ実行とPGの中での実行の違い
主に遅い原因は実行プランの違い、問題はなぜ違いが発生するかだが、原因としてManagement Studioでの実行とPGでの実行ではPGで頻繁に実行されるとSQLが再利用される。実行プランも再利用されるため、データによっては遅くなる場合がある。(らしい)
- ARITHABORTがManagement StudioではデフォルトONだが、PG実行時はデフォルトOFFになっている。
PGのSQL実行時に意図的にONにしてあげると、早くなる場合あり。(効果なしの場合も多い)
SET ARITHABORT ON -- 実行SQL SET ARITHABORT OFF
SQL文のエンコーディング問題
PGからSBサーバーへ送られるクエリのエンコーディングによっては以下の問題が発生しているみたい。varcharのほうが圧倒的多いはずだし、なんとかMSが頑張ってほしいな
- SQL ServerではvarcharがSJIS、nvarcharがUnicode扱い
- SQLがnvarcharとして実行されてた場合、varcharカラムに対して暗黙的にキャストが行われるらしい