Linuxda Powershell'dan MS SQL bilan ishlash

Ushbu maqola mutlaqo amaliy va mening qayg'uli hikoyamga bag'ishlangan

Tayyorlanmoqda Zero Touch PROD Barcha qulog'imiz g'ashillab turgan RDS (MS SQL) uchun men avtomatlashtirish taqdimotini (POC - Proof Of Concept) qildim: Powershell skriptlar to'plami. Taqdimotdan so'ng, bo'ronli, uzoq davom etgan qarsaklar to'xtab, tinimsiz olqishlarga aylanganda, ular menga aytishdi - bularning barchasi yaxshi, lekin faqat mafkuraviy sabablarga ko'ra bizning Jenkins qullarimiz Linuxda ishlaydi!

Bu mumkinmi? Windows ostidan shunday issiq, chiroqli DBA ni olib, Linux ostida powershellning juda issiqligiga yopishib olasizmi? Bu shafqatsizlik emasmi?

Linuxda Powershell'dan MS SQL bilan ishlash
Men texnologiyalarning bu g'alati kombinatsiyasiga sho'ng'ishim kerak edi. Albatta, mening barcha 30+ skriptim ishlamay qoldi. Ajablanarlisi shundaki, men bir ish kunida hammasini tuzatishga muvaffaq bo'ldim. Men qizg'in izlanishda yozyapman. Shunday qilib, Powershell skriptlarini Windows-dan Linux-ga o'tkazishda qanday tuzoqlarga duch kelishingiz mumkin?

sqlcmd va Invoke-SqlCmd

Ularning orasidagi asosiy farqni eslatib o'taman. Yaxshi eski yordamchi dastur sqlcmd Bundan tashqari, u deyarli bir xil funksionallik bilan Linux ostida ishlaydi. Biz so'rovni -Q, kirish faylini -i, chiqishni esa -o sifatida bajaramiz. Lekin fayl nomlari, albatta, katta-kichik harflarni hisobga oladi. Agar siz -i dan foydalansangiz, faylning oxiriga yozing:

GO
EXIT

Agar oxirida EXIT bo'lmasa, sqlcmd kiritishni kutishga davom etadi va agar oldin bo'lsa CHIQISH bolmaydi GO, keyin oxirgi buyruq ishlamaydi. Chiqish fayli barcha chiqish, tanlash, xabarlar, chop etish va hokazolarni o'z ichiga oladi.

Invoke-SqlCmd natijani DataSet, DataTables yoki DataRows sifatida ishlab chiqaradi. Shuning uchun, agar siz oddiy tanlov natijasini qayta ishlasangiz, foydalanishingiz mumkin sqlcmd, uning chiqishini tahlil qilib, murakkab narsani olish deyarli mumkin emas: buning uchun mavjud Invoke-SqlCmd. Ammo bu jamoaning o'ziga xos hazillari ham bor:

  • Agar faylni unga orqali o'tkazsangiz - Kirish fayliso'ng CHIQISH kerak emas, bundan tashqari, u sintaksis xatosini keltirib chiqaradi
  • - Chiqish fayli yo'q, buyruq sizga natijani ob'ekt sifatida qaytaradi
  • Serverni belgilash uchun ikkita sintaksis mavjud: -ServerInstance -Foydalanuvchi nomi -Parol -Ma'lumotlar bazasi va orqali -ConnectionString. G'alati, birinchi holatda 1433 dan boshqa portni ko'rsatish mumkin emas.
  • matn chiqishi, PRINT yozing, bu oddiygina "ushlangan" sqlcmduchun Invoke-SqlCmd muammo hisoblanadi
  • Va eng muhimi: Ehtimol, sizning Linux-da bu cmdlet yo'q!

Va bu asosiy muammo. Faqat mart oyida bu cmdlet Windows bo'lmagan platformalar uchun mavjud bo'ldi, va nihoyat biz oldinga siljishimiz mumkin!

O'zgaruvchan almashtirish

sqlcmd -v yordamida o'zgaruvchan almashtirishga ega, masalan:

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

SQL skriptida biz almashtirishlardan foydalanamiz:

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

Demak, bu yerda. * nix ichida o'zgaruvchan almashtirishlar ishlamaydi. Parametr -v e'tiborga olinmagan. U Invoke-SqlCmd e'tiborga olinmagan -O'zgaruvchilar. Garchi o'zgaruvchilarning o'zini ko'rsatadigan parametr e'tiborga olinmasa ham, almashtirishlarning o'zi ishlaydi - siz Shell'dan istalgan o'zgaruvchilardan foydalanishingiz mumkin. Biroq, men o'zgaruvchilardan xafa bo'ldim va ularga umuman qaram bo'lmaslikka qaror qildim va qo'pol va ibtidoiy ish qildim, chunki SQL skriptlari qisqa:

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

Siz tushunganingizdek, bu allaqachon Unix versiyasidan sinov.

Fayllar yuklanmoqda

Windows versiyasida har qanday operatsiya audit bilan birga edi: biz sqlcmd-ni ishga tushirdik, chiqish faylida qandaydir suiiste'mollikni oldik, ushbu faylni audit plitasiga biriktirdik. Yaxshiyamki, SQL server Jenkins bilan bir xil serverda ishlagan, u shunday qilingan:

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

Shunday qilib, biz BCP faylini butunlay yutib yuboramiz va uni audit jadvalining nvarchar(max) maydoniga joylashtiramiz. Albatta, bu butun tizim parchalanib ketdi, chunki SQL-server o'rniga men RDS oldim va BULK INSERT faylga eksklyuziv qulfni olishga urinish tufayli UNC orqali umuman ishlamaydi va RDS bilan bu umuman yo'q bo'lib ketadi. eng boshlanishi. Shunday qilib, men tizim dizaynini o'zgartirishga qaror qildim, auditni satr bo'yicha saqladim:

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

Va bu jadvalga shunday yozing:

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

Kontentni tanlash uchun siz n (identifikatsiya) tartibida tanlab, ID bo'yicha tanlashingiz kerak.

Keyingi maqolada men bularning barchasi Jenkins bilan qanday bog'liqligi haqida batafsilroq ma'lumot beraman.

Manba: www.habr.com

a Izoh qo'shish