Delo z MS SQL od Powershella naprej Linux

Ta članek je zgolj praktične narave in je posvečen moji žalostni zgodbi.

Priprave na Zero Touch PRODUCT Za RDS (MS SQL), o katerem so vsi brenčali, sem imel predstavitev (POC - Proof of Concept) o avtomatizaciji: nabor PowerShell skriptov. Po predstavitvi, ko je bučen, dolgotrajen aplavz, ki se je spremenil v stoječe ovacije, potihnil, so mi rekli - vse to je lepo in prav, ampak zaradi ideoloških razlogov vsi naši Jenkinsovi sužnji tečejo pod pritiskom. Linux!

Je to sploh mogoče? Vzeti tako topel, cevast DBA od spodaj navzgor Windows in ga zapiči v zelo debelo plast PowerShella LinuxAli ni to kruto?

Delo z MS SQL od Powershella naprej Linux
Moral sem se poglobiti v to nenavadno kombinacijo tehnologij. Seveda je vseh 30+ mojih skript prenehalo delovati. Na moje presenečenje mi je uspelo vse popraviti v enem delovnem dnevu. To pišem, dokler so novice še sveže. Torej, na kakšne pasti lahko naletite pri selitvi skript PowerShell iz Windows pod Linux?

sqlcmd v primerjavi z Invoke-SqlCmd

Naj vas spomnim na glavno razliko med njima. Dobra stara uporabnost sqlcmd Deluje tudi v Linuxu, s skoraj identično funkcionalnostjo. Poizvedbi za izvedbo posredujemo -Q, vhodni datoteki -i in izhodni datoteki -o. Seveda so imena datotek občutljiva na velike in male črke. Če uporabljate -i, na konec datoteke dodajte naslednje:

GO
EXIT

Če na koncu ni IZHODA, bo sqlcmd nadaljeval s čakanjem na vnos, in če pred tem ni IZHODA EXIT ne bo GO, potem zadnji ukaz ne bo deloval. Ves izhod, izbire, sporočila, stavki za tiskanje itd. bodo zapisani v izhodno datoteko.

Invoke-SqlCmd vrne rezultate v obliki nabora podatkov (DataSet), tabel podatkov (DataTables) ali vrstic podatkov (DataRows). Če torej obdelujete rezultat preprostega stavka select, lahko uporabite tudi sqlcmd, po analizi njegovega izhoda je praktično nemogoče izpeljati kaj kompleksnega: obstaja Invoke-SqlCmdAmpak ta ekipa ima svoje šale:

  • Če ji prenesete datoteko prek -VhodnaDatoteka, Potem EXIT ni potrebno, poleg tega pa vrne sintaktično napako
  • -IzhodnaDatoteka Ne, ukaz vam vrne rezultat kot objekt.
  • Za določitev strežnika obstajata dve sintaksi: -Strežniški primerek -Uporabniško ime -Geslo -Zbirka podatkov in skozi -NizPovezaveNenavadno je, da v prvem primeru ni mogoče določiti drugih vrat kot 1433.
  • izpis besedila, kot je PRINT, ki ga je enostavno "prepoznati" sqlcmdza Invoke-SqlCmd je problem
  • In kar je najpomembneje: Najverjetneje vaš Linux nima tega ukaza »cmdlet«!

In to je glavna težava. Ta ukaz »cmdlet« se je pojavil šele marca. postalo na voljo za platforme, ki niso Windows, in končno lahko gremo naprej!

Zamenjava spremenljivke

sqlcmd ima zamenjavo spremenljivk z uporabo -v, takole:

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

V SQL skripti uporabljamo zamenjave:

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

Torej, v *nixu Zamenjave spremenljivk ne delujejo... Parameter -v prezrto. Invoke-SqlCmd prezrto -SpremenljivkeČeprav se parameter, ki sam nastavlja spremenljivke, prezre, same zamenjave delujejo – lahko uporabite katero koli spremenljivko iz lupine. Vendar sem se zaradi spremenljivk užalil in se odločil, da jih bom popolnoma ignoriral, pri čemer sem uporabil surov in primitiven pristop, na srečo so SQL skripti kratki:

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

Kot razumete, je to test iz Unixove različice.

Nalaganje datotek

V različici za Windows je bila vsaka izvedena operacija revidirana: zagnal sem sqlcmd, v izhodni datoteki dobil nekakšno napako in nato to datoteko priložil tabeli za revizijo. Na srečo se je SQL Server izvajal na istem strežniku kot Jenkins, zato je bilo to narejeno nekako takole:

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

Torej pogoltnemo celotno datoteko BCP in jo stlačimo v polje nvarchar(max) revizijske tabele. Seveda se je celoten sistem sesul, ker sem namesto SQL Serverja dobil RDS, BULK INSERT pa sploh ne deluje prek UNC, ker poskuša prevzeti ekskluzivno zaklepanje datoteke, z RDS pa je to že od samega začetka obsojeno na propad. Zato sem se odločil, da sistem preoblikujem in shranjujem revizijske podatke vrstico za vrstico:

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

In v to tabelo zapiši takole:

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

Za izbiro vsebine morate izbrati po ID-ju, in sicer po vrstnem redu n (identiteta).

V naslednjem članku bom podrobneje opisal, kako vse to vpliva na Jenkins.

Vir: www.habr.com

Kupite zanesljivo gostovanje za strani z DDoS zaščito, VPS VDS strežniki 🔥 Kupite zanesljivo spletno gostovanje z zaščito DDoS, VPS VDS strežniki | ProHoster