Gweithio gydag MS SQL o Powershell ar Linux

Mae'r erthygl hon yn gwbl ymarferol ac yn ymroddedig i fy stori drist

Paratoi ar gyfer Zero Touch PROD ar gyfer RDS (MS SQL), yr oedd ein clustiau i gyd yn fwrlwm amdano, gwnes gyflwyniad (POC - Proof Of Concept) o awtomeiddio: set o sgriptiau cregyn pŵer. Ar ôl y cyflwyniad, pan fu farw’r gymeradwyaeth stormus, hirfaith, gan droi’n gymeradwyaeth ddi-baid, dywedasant wrthyf - mae hyn i gyd yn dda, ond dim ond am resymau ideolegol, mae ein holl gaethweision Jenkins yn gweithio ar Linux!

Ydy hyn yn bosibl? Cymerwch lamp DBA mor gynnes o dan Windows a'i gludo yng ngwres cragen y pwerau o dan Linux? Onid yw hyn yn greulon?

Gweithio gydag MS SQL o Powershell ar Linux
Roedd yn rhaid i mi ymgolli yn y cyfuniad rhyfedd hwn o dechnolegau. Wrth gwrs, stopiodd fy holl 30+ sgriptiau weithio. Er mawr syndod i mi, llwyddais i drwsio popeth mewn un diwrnod gwaith. Rwy'n ysgrifennu ar drywydd poeth. Felly, pa beryglon allwch chi ddod ar eu traws wrth drosglwyddo sgriptiau cregyn pwerau o Windows i Linux?

sqlcmd vs Invoke-SqlCmd

Gadewch imi eich atgoffa o'r prif wahaniaeth rhyngddynt. Hen ddefnyddioldeb da sqlcmd Mae hefyd yn gweithio o dan Linux, gyda swyddogaeth bron yn union yr un fath. Rydyn ni'n pasio'r ymholiad i weithredu -Q, y ffeil fewnbwn fel -i, a'r allbwn fel -o. Ond mae'r enwau ffeiliau, wrth gwrs, yn cael eu gwneud yn achos-sensitif. Os ydych chi'n defnyddio -i, yna yn y ffeil ysgrifennwch ar y diwedd:

GO
EXIT

Os nad oes EXIT ar y diwedd, yna bydd sqlcmd yn symud ymlaen i aros am fewnbwn, ac os o'r blaen EXIT ni fydd GO, yna ni fydd y gorchymyn olaf yn gweithio. Mae'r ffeil allbwn yn cynnwys yr holl allbwn, dewis, negeseuon, print, ac ati.

Mae Invoke-SqlCmd yn cynhyrchu'r canlyniad fel Set Ddata, DataTables neu DataRows. Felly, os ydych chi'n prosesu canlyniad dewis syml, gallwch chi ei ddefnyddio sqlcmd, wedi dosrannu ei gynnyrch, y mae bron yn anmhosibl tarddu rhywbeth dyrys : am hyny y mae Galw-SqlCmd. Ond mae gan y tîm hwn ei jôcs ei hun hefyd:

  • Os byddwch yn trosglwyddo ffeil iddi drwy -InputFile, Yna EXIT nid oes angen, ar ben hynny, mae'n cynhyrchu gwall cystrawen
  • -OutputFile na, mae'r gorchymyn yn dychwelyd y canlyniad i chi fel gwrthrych
  • Mae dwy gystrawen ar gyfer pennu gweinydd: -ServerInstance -Enw Defnyddiwr -Cyfrinair -Cronfa Ddata a thrwyddo -ConnectionString. Yn rhyfedd ddigon, yn yr achos cyntaf nid yw'n bosibl nodi porthladd heblaw 1433.
  • allbwn testun, teipiwch PRINT, sy'n cael ei “ddal” yn syml sqlcmdcanys Galw-SqlCmd yn broblem
  • Ac yn bwysicaf oll: Yn fwyaf tebygol, nid oes gan eich Linux y cmdlet hwn!

A dyma'r brif broblem. Dim ond ym mis Mawrth y cmdlet hwn daeth ar gael ar gyfer llwyfannau nad ydynt yn Windows, ac yn olaf gallwn symud ymlaen!

Amnewidiad amrywiol

Mae gan sqlcmd amnewidiad amrywiol gan ddefnyddio -v, er enghraifft fel hyn:

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

Yn y sgript SQL rydym yn defnyddio eilyddion:

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

Felly dyma hi. Yn * nix nid yw amnewidion newidiol yn gweithio. Paramedr -v anwybyddu. U Galw-SqlCmd anwybyddu -Amrywolion. Er bod y paramedr sy'n pennu'r newidynnau eu hunain yn cael ei anwybyddu, mae'r eilyddion eu hunain yn gweithio - gallwch ddefnyddio unrhyw newidynnau o Shell. Fodd bynnag, cefais fy siomi gan y newidynnau a phenderfynais beidio â dibynnu arnynt o gwbl, a gweithredais yn ddigywilydd ac yn gyntefig, gan fod y sgriptiau SQL yn fyr:

# 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"

Mae hwn, fel y deallwch, yn brawf eisoes o'r fersiwn Unix.

Llwytho ffeiliau i fyny

Yn y fersiwn Windows, roedd archwiliad yn cyd-fynd ag unrhyw weithrediad: gwnaethom redeg sqlcmd, derbyniwyd rhyw fath o gam-drin yn y ffeil allbwn, atododd y ffeil hon i'r plât archwilio. Yn ffodus, roedd gweinydd SQL yn gweithio ar yr un gweinydd â Jenkins, fe'i gwnaed rhywbeth fel hyn:

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

Felly, rydym yn llyncu'r ffeil BCP yn gyfan gwbl ac yn ei gwthio i faes nvarchar(max) yn y tabl archwilio. Wrth gwrs, syrthiodd y system gyfan hon ar wahân, oherwydd yn lle gweinydd SQL cefais RDS, ac nid yw BULK INSERT yn gweithio o gwbl trwy UNC oherwydd ymgais i gymryd clo unigryw ar ffeil, a chyda RDS mae hyn yn cael ei dynghedu yn gyffredinol. y cychwyn cyntaf. Felly penderfynais newid cynllun y system, gan storio'r archwiliad fesul llinell:

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

Ac ysgrifennwch yn y tabl hwn fel hyn:

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() 
  }  

I ddewis cynnwys, mae angen i chi ddewis yn ôl ID, gan ddewis trefn n (hunaniaeth).

Yn yr erthygl nesaf byddaf yn mynd i fwy o fanylion am sut mae hyn i gyd yn rhyngweithio â Jenkins.

Ffynhonnell: hab.com

Ychwanegu sylw