Linux'ta Powershell'den MS SQL ile çalışma

Bu makale tamamen pratiktir ve üzücü hikayeme adanmıştır

için hazırlanmak Sıfır Dokunuş PROD Tüm kulaklarımızın çınladığı RDS (MS SQL) için otomasyonun bir sunumunu (POC - Konsept Kanıtı) yaptım: bir dizi powershell betiği. Sunumdan sonra, fırtınalı, uzun süreli alkışlar dinip aralıksız alkışlara dönüştüğünde bana şunu söylediler: tüm bunlar iyi, ancak yalnızca ideolojik nedenlerden dolayı, tüm Jenkins kölelerimiz Linux üzerinde çalışıyor!

Mümkün mü? Windows'un altından bu kadar sıcak, lambalı bir DBA'yı alıp Linux'un altındaki powershell'in sıcaklığına mı yapıştıracaksınız? Bu zalimce değil mi?

Linux'ta Powershell'den MS SQL ile çalışma
Kendimi bu tuhaf teknoloji kombinasyonuna kaptırmak zorunda kaldım. Tabii ki, 30'dan fazla senaryomun tümü çalışmayı bıraktı. Şaşırtıcı bir şekilde, her şeyi bir iş gününde düzeltmeyi başardım. Sıcak takipte yazıyorum. Peki powershell betiklerini Windows'tan Linux'a aktarırken ne gibi tuzaklarla karşılaşabilirsiniz?

sqlcmd vs Invoke-SqlCmd

Aralarındaki temel farkı hatırlatayım. İyi eski yardımcı program Sqlcmd Aynı zamanda Linux altında da neredeyse aynı işlevlerle çalışır. Sorguyu -Q'yu yürütmek için, giriş dosyasını -i olarak ve çıktıyı -o olarak iletiyoruz. Ancak dosya adları elbette büyük/küçük harfe duyarlı hale getirilmiştir. -i kullanırsanız dosyanın sonuna şunu yazın:

GO
EXIT

Sonunda EXIT yoksa, sqlcmd girişi beklemeye devam edecek ve daha önce ise EXIT değil GO, bu durumda son komut çalışmaz. Çıktı dosyası tüm çıktıları, seçimleri, mesajları, yazdırmayı vb. içerir.

Invoke-SqlCmd, sonucu DataSet, DataTables veya DataRows olarak üretir. Bu nedenle basit bir seçimin sonucunu işlerseniz kullanabilirsiniz. Sqlcmdçıktısını ayrıştırdıktan sonra karmaşık bir şey türetmek neredeyse imkansızdır: bunun için Çağır-SqlCmd. Ancak bu takımın da kendine has şakaları var:

  • Eğer ona bir dosya aktarırsanız -Giriş dosyasıSonra EXIT gerekli değil, ayrıca sözdizimi hatası üretiyor
  • -Çıktı dosyası hayır, komut size sonucu bir nesne olarak döndürür
  • Bir sunucuyu belirtmek için iki sözdizimi vardır: -ServerInstance -Kullanıcı adı -Şifre -Veritabanı Ve aracılığıyla -Bağlantı dizisi. Garip bir şekilde, ilk durumda 1433 dışında bir bağlantı noktası belirtmek mümkün değildir.
  • metin çıktısı, basitçe "yakalanan" PRINT yazın Sqlcmdiçin Çağır-SqlCmd bir sorun
  • Ve en önemlisi: Büyük olasılıkla Linux'unuzda bu cmdlet yoktur!

Ve bu asıl sorundur. Yalnızca Mart ayında bu cmdlet Windows dışı platformlar için kullanılabilir hale geldive sonunda ilerleyebiliriz!

Değişken Değiştirme

sqlcmd'de -v kullanılarak değişken ikamesi vardır, örneğin şöyle:

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

SQL betiğinde ikameleri kullanırız:

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

İşte burada. *nix'te değişken ikameleri işe yaramıyor... Parametre -v görmezden gelindi. sen Çağır-SqlCmd görmezden gelindi -Değişkenler. Değişkenleri belirten parametreler göz ardı edilse de, ikamelerin kendileri işe yarar; Shell'deki herhangi bir değişkeni kullanabilirsiniz. Ancak değişkenlerden rahatsız oldum ve onlara hiç güvenmemeye karar verdim ve SQL komut dosyaları kısa olduğu için kaba ve ilkel davrandım:

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

Bu, anladığınız gibi, zaten Unix sürümünden bir testtir.

Dosya yükleme

Windows sürümünde, herhangi bir işleme bir denetim eşlik ediyordu: sqlcmd'yi çalıştırdık, çıktı dosyasında bir tür suiistimal aldık, bu dosyayı denetim plakasına ekledik. Neyse ki SQL sunucusu Jenkins ile aynı sunucuda çalışıyordu, şöyle bir şey yapıldı:

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

Böylece BCP dosyasını tamamen yutup denetim tablosunun nvarchar(max) alanına atıyoruz. Tabii ki, tüm bu sistem çöktü, çünkü SQL sunucusu yerine RDS aldım ve BULK INSERT, bir dosyaya özel bir kilit alma girişimi nedeniyle UNC aracılığıyla hiç çalışmıyor ve RDS ile bu genellikle mahkumdur. En başta. Bu yüzden denetim satırını satır satır saklayarak sistem tasarımını değiştirmeye karar verdim:

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

Ve bu tabloya şu şekilde yazın:

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

İçeriği seçmek için, n (kimlik) sırasına göre kimliğe göre seçim yapmanız gerekir.

Bir sonraki makalede tüm bunların Jenkins ile nasıl etkileşime girdiğini daha ayrıntılı olarak ele alacağım.

Kaynak: habr.com

Yorum ekle