Powershell-eko MS SQL-rekin lan egiten Linux-en

Artikulu hau guztiz praktikoa da eta nire istorio tristeari eskainia dago

Prestatzen Zero Touch PROD RDS-rako (MS SQL), zeinari buruz gure belarri guztiak burrunbaka ari ziren, automatizazioaren aurkezpena (POC - Proof Of Concept) egin nuen: powershell script-en multzoa. Aurkezpenaren ostean, txalo ekaitz luzeak itzali zirenean, etengabeko txalo bihurtu zirenean, esan zidaten - hau guztia ona da, baina arrazoi ideologikoengatik bakarrik, gure Jenkins esklabo guztiek Linux-en lan egiten dute!

Hau posible al da? Hartu Windows azpiko DBA lanpara bero bat eta itsatsi Linux-en powershell-en beroan? Hau ez al da krudela?

Powershell-eko MS SQL-rekin lan egiten Linux-en
Teknologien konbinazio bitxi honetan murgildu behar izan nuen. Jakina, nire 30+ script guztiek funtzionatzeari utzi zioten. Nire harridurarako, lanegun batean dena konpontzea lortu nuen. Atzetik ari naiz idazten. Beraz, zer arazo aurki ditzakezu powershell scriptak Windows-etik Linuxera transferitzean?

sqlcmd vs Invoke-SqlCmd

Gogorarazten dizut haien arteko desberdintasun nagusia. Antzinako erabilgarritasun ona sqlcmd Linux-en ere funtzionatzen du, funtzionaltasun ia berdinekin. -Q exekutatzeko kontsulta pasatzen dugu, sarrera fitxategia -i gisa eta irteera -o gisa. Baina fitxategien izenak, noski, maiuskulak eta minuskulak bereizten dira. -i erabiltzen baduzu, idatzi fitxategian amaieran:

GO
EXIT

Amaieran IRTEERA ez badago, sqlcmd-k sarreraren zain jarraituko du, eta aurretik bada EXIT ez du GO, orduan azken komandoak ez du funtzionatuko. Irteera fitxategiak irteera guztiak, hautaketak, mezuak, inprimatzea, etab.

Invoke-SqlCmd-ek emaitza DataSet, DataTables edo DataRows gisa sortzen du. Hori dela eta, aukeraketa sinple baten emaitza prozesatzen baduzu, erabil dezakezu sqlcmd, bere irteera aztertuta, ia ezinezkoa da zerbait konplexua ateratzea: horretarako dago Invoke-SqlCmd. Baina talde honek bere txantxak ere baditu:

  • Fitxategi bat transferitzen badiozu haren bidez -InputFitxategiaondoren EXIT ez da beharrezkoa, gainera, sintaxi-errore bat sortzen du
  • -IrteeraFitxategia ez, komandoak emaitza objektu gisa itzultzen dizu
  • Zerbitzari bat zehazteko bi sintaxi daude: -ZerbitzariInstantzia -Erabiltzaile izena -Pasahitza -Datubasea eta bidez -ConnectionString. Bitxia bada ere, lehenengo kasuan ezin da 1433 ez den atakarik zehaztu.
  • testuaren irteera, idatzi PRINT, hau da, besterik gabe, "harrapatuta" sqlcmdegiteko Invoke-SqlCmd arazo bat da
  • Eta garrantzitsuena: Litekeena da zure Linux-ek cmdlet hau ez izatea!

Eta hau da arazo nagusia. Martxoan bakarrik cmdlet hau Windows ez diren plataformetarako erabilgarri bihurtu zen, eta azkenean aurrera egin dezakegu!

Aldagaien ordezkapena

sqlcmd-k aldagaiaren ordezkapena du -v erabiliz, adibidez, honela:

# $conn содСрТит Π½Π°Ρ‡Π°Π»ΠΎ ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ sqlcmd
$cmd = $conn + " -i D:appsSlaveJobsKillSpid.sql -o killspid.res 
  -v spid =`"" + $spid + "`" -v age =`"" + $age + "`""
Invoke-Expression $cmd

SQL script-ean ordezkapenak erabiltzen ditugu:

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

Beraz, hemen dago. *nix-en aldagaien ordezkapenek ez dute funtzionatzen. Parametroa -v baztertu. U Invoke-SqlCmd baztertu -Aldagaiak. Aldagaiak zehazten dituen parametroa ez ikusi arren, ordezkapenek beraiek funtzionatzen dute; Shell-eko edozein aldagai erabil dezakezu. Hala ere, aldagaiek mindu egin ninduten eta haiengandik ez egotea erabaki nuen, eta zakar eta primitiboki jokatu nuen, SQL script-ak laburrak baitira:

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

Hau, ulertzen duzunez, dagoeneko Unix bertsioko proba bat da.

Fitxategiak kargatzen

Windows bertsioan, edozein eragiketa auditoria batekin batera joaten zen: sqlcmd exekutatu genuen, irteerako fitxategian tratu txarrak jaso genituen, fitxategi hau ikuskaritza-plakan erantsi genuen. Zorionez, SQL zerbitzariak Jenkins-en zerbitzari berean lan egin zuen, honelako zerbait egin zen:

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

Horrela, BCP fitxategia guztiz irensten dugu eta auditoria-taularen nvarchar(max) eremuan sartzen dugu. Jakina, sistema osoa hautsi egin zen, SQL zerbitzari baten ordez RDS lortu nuen eta BULK INSERT-ek ez du batere funtzionatzen UNC bidez, fitxategi bati blokeo esklusibo bat hartzeko saiakeraren ondorioz, eta RDS-rekin hori, oro har, kondenatuta dago. hasiera bera. Beraz, sistemaren diseinua aldatzea erabaki nuen, auditoria lerroz lerro gordeta:

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

Eta idatzi taula honetan honela:

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

Edukia hautatzeko, IDaren arabera hautatu behar duzu, n (identitatea) ordenan aukeratuz.

Hurrengo artikuluan hau guztia Jenkins-ekin nola elkarreragiten duen xehetasun gehiago azalduko dut.

Iturria: www.habr.com

Gehitu iruzkin berria