Linux-da Powershell-dən MS SQL ilə işləmək

Bu məqalə tamamilə praktikdir və mənim kədərli hekayəmə həsr edilmişdir

üçün hazırlanır Zero Touch PROD Bütün qulaqlarımızın uğultusunda olduğu RDS (MS SQL) üçün avtomatlaşdırmanın təqdimatını (POC - Proof Of Concept) etdim: bir sıra powershell skriptləri. Təqdimatdan sonra, fırtınalı, uzun sürən alqışlar sönəndə, aramsız alqışlara çevriləndə, onlar mənə dedilər - bütün bunlar yaxşıdır, amma yalnız ideoloji səbəblərə görə, bütün Jenkins qullarımız Linux-da işləyirlər!

Bu mümkündürmü? Windows altından belə isti, lampa DBA götürün və Linux altında powershell-in çox istisində yapışdırın? Bu qəddarlıq deyilmi?

Linux-da Powershell-dən MS SQL ilə işləmək
Mən özümü bu qəribə texnologiyalar birləşməsinə qərq etməli oldum. Təbii ki, mənim bütün 30+ skriptim işləməyi dayandırdı. Təəccüblüm odur ki, bir iş günündə hər şeyi düzəldə bildim. Mən qaynar təqibdə yazıram. Beləliklə, Powershell skriptlərini Windows-dan Linux-a köçürərkən hansı tələlərlə qarşılaşa bilərsiniz?

sqlcmd vs Invoke-SqlCmd

Onların arasındakı əsas fərqi xatırlatmaq istərdim. Yaxşı köhnə kommunal sqlcmd O, həmçinin demək olar ki, eyni funksionallıqla Linux altında işləyir. Sorğunu yerinə yetirmək üçün -Q, giriş faylını -i, çıxışı isə -o kimi ötürürük. Ancaq fayl adları, əlbəttə ki, böyük hərflərə həssasdır. -i istifadə edirsinizsə, faylın sonunda yazın:

GO
EXIT

Sonda ÇIXIŞ yoxdursa, sqlcmd daxil etməyi gözləməyə davam edəcək və əgər əvvəl EXIT olmaz GO, onda sonuncu əmr işləməyəcək. Çıxış faylı bütün çıxışları, seçmələri, mesajları, çapı və s.

Invoke-SqlCmd nəticəni DataSet, DataTables və ya DataRows kimi istehsal edir. Buna görə də, sadə bir seçimin nəticəsini emal etsəniz, istifadə edə bilərsiniz sqlcmd, onun çıxışını təhlil edərək, mürəkkəb bir şey əldə etmək demək olar ki, mümkün deyil: bunun üçün var Invoke-SqlCmd. Amma bu komandanın da öz zarafatları var:

  • vasitəsilə ona bir fayl köçürsəniz -Giriş faylı, Sonra EXIT lazım deyil, üstəlik, sintaksis xətası yaradır
  • -Çıxış faylı yox, əmr sizə nəticəni obyekt kimi qaytarır
  • Serveri təyin etmək üçün iki sintaksis var: -ServerInstance -İstifadəçi adı -Parol -Verilənlər bazası və vasitəsilə -ConnectionString. Qəribədir ki, birinci halda 1433-dən başqa bir port təyin etmək mümkün deyil.
  • mətn çıxışı, PRINT yazın, bu sadəcə “tutulur” sqlcmdüçün Invoke-SqlCmd problemdir
  • Və ən əsası: Çox güman ki, Linux-da bu cmdlet yoxdur!

Və bu əsas problemdir. Yalnız mart ayında bu cmdlet qeyri-Windows platformaları üçün əlçatan oldu, və nəhayət, irəli gedə bilərik!

Dəyişən əvəzetmə

sqlcmd -v istifadə edərək dəyişən əvəzetmə var, məsələn, bu kimi:

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

SQL skriptində biz əvəzetmələrdən istifadə edirik:

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

Beləliklə, budur. *nix-də dəyişən əvəzetmələr işləmir. Parametr -v nəzərə alınmadı. U Invoke-SqlCmd nəzərə alınmadı -Dəyişənlər. Dəyişənlərin özlərini təyin edən parametr nəzərə alınmasa da, əvəzetmələrin özləri işləyir - Shell-dən istənilən dəyişənlərdən istifadə edə bilərsiniz. Bununla belə, dəyişənlərdən incidim və onlardan heç asılı olmamaq qərarına gəldim və SQL skriptləri qısa olduğu üçün kobud və primitiv 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, başa düşdüyünüz kimi, artıq Unix versiyasından bir testdir.

Fayllar yüklənir

Windows versiyasında istənilən əməliyyat auditlə müşayiət olunurdu: biz sqlcmd-ni işə saldıq, çıxış faylında bir növ sui-istifadə aldıq, bu faylı audit lövhəsinə əlavə etdik. Xoşbəxtlikdən, SQL server Jenkins ilə eyni serverdə işləyirdi, buna bənzər bir şey edildi:

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

Beləliklə, biz BCP faylını tamamilə udub audit cədvəlinin nvarchar(max) sahəsinə itələyirik. Əlbəttə ki, bütün bu sistem dağıldı, çünki SQL serverinin əvəzinə mən RDS aldım və BULK INSERT faylda eksklüziv kilid götürmək cəhdi səbəbindən UNC vasitəsilə ümumiyyətlə işləmir və RDS ilə bu, ümumiyyətlə, məhv edilir. ən başlanğıcı. Beləliklə, auditi sətir-sətir saxlayaraq sistem dizaynını dəyişdirmək qərarına gəldim:

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

Və bu cədvələ belə 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() 
  }  

Məzmunu seçmək üçün n (şəxsiyyət) sırasını seçərək ID-yə görə seçməlisiniz.

Növbəti məqalədə bunların hamısının Jenkins ilə necə qarşılıqlı əlaqəsi haqqında daha ətraflı məlumat verəcəyəm.

Mənbə: www.habr.com

Добавить комментарий