Schafft mat MS SQL vu Powershell op Linux

Dësen Artikel ass reng praktesch an ass fir meng traureg Geschicht gewidmet

Kréien prett fir Zero Touch PROD fir RDS (MS SQL), iwwer déi all eis Oueren geschwuer hunn, hunn ech eng Presentatioun (POC - Proof Of Concept) vun der Automatioun gemaach: eng Rei vu Powershell Scripten. No der Presentatioun, wéi de stiermeschen, verlängerten Applaus verstuerwen ass, an onopfälleg Applaus ëmgewandelt huet, hunn se mir gesot - dat alles ass gutt, awer nëmmen aus ideologesche Grënn, all eis Jenkins Sklaven schaffen op Linux!

Ass dëst méiglech? Huelt sou eng waarm, Lampe DBA vun ënner Windows a setzt se an der Hëtzt vun der Powershell ënner Linux? Ass dat net grausam?

Schafft mat MS SQL vu Powershell op Linux
Ech hu mech an dëser komescher Kombinatioun vun Technologien ënnerzegoen. Natierlech hunn all meng 30+ Skripte opgehalen ze schaffen. Zu menger Iwwerraschung hunn ech et fäerdeg bruecht alles an engem Aarbechtsdag ze fixéieren. Ech schreiwen an waarm Verfollegung. Also, wéi eng Falen kënnt Dir begéinen wann Dir Powershell Scripte vu Windows op Linux transferéiert?

sqlcmd vs Invoke-SqlCmd

Loosst mech Iech un den Haaptunterschied tëscht hinnen erënneren. Gutt al Utility sqlcmd Et funktionnéiert och ënner Linux, mat bal identescher Funktionalitéit. Mir passéieren d'Ufro fir -Q auszeféieren, d'Inputdatei als -i, an d'Ausgab als -o. Awer d'Dateiennimm sinn natierlech ka-sensibel gemaach. Wann Dir -i benotzt, da schreift an der Datei um Enn:

GO
EXIT

Wann et keen EXIT um Enn ass, da wäert sqlcmd weidergoen fir op Input ze waarden, a wann virdrun AUSGANG wäert net GO, da funktionnéiert de leschte Kommando net. D'Ausgabdatei enthält all d'Ausgab, wielt, Messagen, Drécken, asw.

Invoke-SqlCmd produzéiert d'Resultat als DataSet, DataTables oder DataRows. Dofir, wann Dir d'Resultat vun enger einfacher Auswiel veraarbecht, kënnt Dir benotzen sqlcmd, nodeems se seng Ausgab analyséiert hunn, ass et bal onméiglech eppes Komplexes ofzeleeden: dofir gëtt et Invoke-SqlCmd. Mä dës Equipe huet och seng eege Witzer:

  • Wann Dir Transfert engem Fichier un hir via -Input Datei, dann AUSGANG net néideg, Desweideren, et produzéiert engem Syntax Feeler
  • -Output Datei nee, de Kommando gëtt Iech d'Resultat als Objet zréck
  • Et ginn zwou Syntaxe fir e Server ze spezifizéieren: -ServerInstance -Benotzernumm -Passwuert -Datebank an duerch -ConnectionString. Komesch genuch, am éischte Fall ass et net méiglech en aneren Hafen wéi 1433 ze spezifizéieren.
  • Textausgang, Typ PRINT, wat einfach "gefänkt" ass sqlcmd, fir Invoke-SqlCmd ass e Problem
  • An am wichtegsten: Wahrscheinlech ass Äre Linux net dësen cmdlet!

An dëst ass den Haaptproblem. Nëmmen am Mäerz dëse cmdlet gouf fir Net-Windows Plattformen verfügbar, an endlech kënne mir weidergoen!

Variabel Substitution

sqlcmd huet variabel Ersatz mat -v, zum Beispill esou:

# $conn содержит начало команды sqlcmd
$cmd = $conn + " -i D:appsSlaveJobsKillSpid.sql -o killspid.res 
  -v spid =`"" + $spid + "`" -v age =`"" + $age + "`""
Invoke-Expression $cmd

Am SQL Skript benotze mir Auswiesselungen:

set @spid=$(spid)
set @age=$(age)

Also hei ass et. An *nix Variabel Auswiesselungen funktionnéieren net. Parameter -v ignoréiert. U Invoke-SqlCmd ignoréiert - Verännerlechen. Och wann de Parameter, deen d'Variabelen selwer spezifizéiert, ignoréiert gëtt, funktionnéieren d'Auswiesselungen selwer - Dir kënnt all Variablen aus Shell benotzen. Wéi och ëmmer, ech war beleidegt vun de Variablen an hunn decidéiert guer net vun hinnen ofhänken, an hunn ruppeg a primitiv gehandelt, well d'SQL Scripte kuerz sinn:

# prepend the parameters  
"declare @age int, @spid int" | Add-Content "q.sql"
"set @spid=" + $spid | Add-Content "q.sql"
"set @age=" + $age | Add-Content "q.sql"

foreach ($line in Get-Content "Sqlserver/Automation/KillSpid.sql") { 
  $line | Add-Content "q.sql" 
  }
$cmd = "/opt/mssql-tools/bin/" + $conn + " -i q.sql -o res.log"

Dëst, wéi Dir verstitt, ass en Test schonn aus der Unix Versioun.

Eroplueden Fichieren

An der Windows Versioun gouf all Operatioun vun engem Audit begleet: mir lafen sqlcmd, krut eng Aart vu Mëssbrauch an der Ausgabdatei, befestegt dës Datei un der Auditplack. Glécklecherweis huet de SQL Server um selwechte Server wéi Jenkins geschafft, et gouf sou eppes gemaach:

CREATE procedure AuditUpload
  @id int, @filename varchar(256)
as
  set nocount on
  declare @sql varchar(max)

  CREATE TABLE #multi (filer NVARCHAR(MAX))
  set @sql='BULK INSERT #multi FROM '''+@filename
    +''' WITH (ROWTERMINATOR = '' '',CODEPAGE = ''ACP'')'
  exec (@sql)
  select @sql=filer from #multi
  update JenkinsAudit set multiliner=@sql where ID=@id
  return

Also schlucken mir d'BCP Datei ganz an drécken se an d'nvarchar(max) Feld vun der Audittabell. Natierlech ass dee ganze System ausernee gefall, well ech amplaz vun engem SQL Server RDS krut, a BULK INSERT funktionéiert guer net iwwer UNC wéinst engem Versuch, en exklusive Spär op engem Fichier ze huelen, a mat RDS ass dëst allgemeng veruerteelt vun den Ufank. Also hunn ech decidéiert de Systemdesign z'änneren, d'Audit Linn fir Zeil ze späicheren:

CREATE TABLE AuditOut (
  ID int NULL,
  TextLine nvarchar(max) NULL,
  n int IDENTITY(1,1) PRIMARY KEY
  )

A schreift an dëser Tabell esou:

function WriteAudit([string]$Filename, [string]$ConnStr, 
     [string]$Tabname, [string]$Jobname)
{
  # get $lastid of the last execution  -- проскипано для статьи
	
  #create grid and populate it with data from file
  $audit =  Get-Content $Filename
  $DT = new-object Data.DataTable   

  $COL1 =  new-object Data.DataColumn; 
  $COL1.ColumnName = "ID"; 
  $COL1.DataType =  [System.Type]::GetType("System.Int32") 

  $COL2 =  new-object Data.DataColumn; 
  $COL2.ColumnName = "TextLine"; 
  $COL2.DataType =  [System.Type]::GetType("System.String") 
  
  $DT.Columns.Add($COL1) 
  $DT.Columns.Add($COL2) 
  foreach ($line in $audit) 
    { 
    $DR = $dt.NewRow()   
    $DR.Item("ID") = $lastid
    $DR.Item("TextLine") = $line
    $DT.Rows.Add($DR)   
    } 

  # write it to table
  $conn=new-object System.Data.SqlClient.SQLConnection 
  $conn.ConnectionString = $ConnStr
  $conn.Open() 
  $bulkCopy = new-object ("Data.SqlClient.SqlBulkCopy") $ConnStr
  $bulkCopy.DestinationTableName = $Tabname 
  $bulkCopy.BatchSize = 50000
  $bulkCopy.BulkCopyTimeout = 0
  $bulkCopy.WriteToServer($DT) 
  $conn.Close() 
  }  

Fir Inhalter auswielen, musst Dir duerch ID auswielen, wielt an Uerdnung n (Identitéit).

Am nächsten Artikel ginn ech méi detailléiert iwwer wéi dëst alles mam Jenkins interagéiert.

Source: will.com

Setzt e Commentaire