Linux жүйесінде Powershell-тен MS SQL-мен жұмыс істеу

Бұл мақала тек практикалық және менің қайғылы оқиғаға арналған

Дайындалуда Zero Touch PROD RDS (MS SQL) үшін, ол туралы біздің барлық құлағымызды шуылдатып, автоматтандырудың презентациясын (POC - Proof Of Concept) жасадым: Powershell сценарийлерінің жиынтығы. Тұсаукесерден кейін, дауылды, ұзаққа созылған қол шапалақтау тоқтаусыз шапалақтауға айналғанда, олар маған айтты - мұның бәрі жақсы, бірақ тек идеологиялық себептермен біздің Дженкинстің барлық құлдары Linux-та жұмыс істейді!

Бұл мүмкін бе? Windows астынан осындай жылы, шамды DBA алыңыз және оны Linux жүйесіндегі Powershell-дің өте қызуына жабыстырыңыз ба? Бұл қатыгездік емес пе?

Linux жүйесінде Powershell-тен MS SQL-мен жұмыс істеу
Маған осы біртүрлі технологиялар комбинациясына енуге тура келді. Әрине, менің барлық 30+ сценарийім жұмысын тоқтатты. Бір таң қалғаным, бір жұмыс күнінде бәрін жөндеп алдым. Мен қызу ізденіспен жазып отырмын. Сонымен, Powershell сценарийлерін Windows жүйесінен Linux жүйесіне тасымалдау кезінде қандай қателіктерге тап болуыңыз мүмкін?

sqlcmd және Invoke-SqlCmd

Олардың арасындағы негізгі айырмашылықты еске сала кетейін. Жақсы ескі утилита sqlcmd Ол сондай-ақ бірдей дерлік функционалдығы бар Linux астында жұмыс істейді. Сұрауды -Q орындау үшін, кіріс файлын -i, шығысты -o ретінде береміз. Бірақ файл атаулары, әрине, регистрді ескере отырып жасалған. Егер сіз -i қолдансаңыз, файлдың соңына жазыңыз:

GO
EXIT

Егер соңында EXIT болмаса, sqlcmd енгізуді күтуді жалғастырады, ал егер бұрын болса ШЫҒУ болмайды GO, содан кейін соңғы пәрмен жұмыс істемейді. Шығару файлы барлық шығыстарды, таңдауларды, хабарламаларды, басып шығаруды және т.б.

Invoke-SqlCmd нәтижені DataSet, DataTables немесе DataRows ретінде шығарады. Сондықтан, егер сіз қарапайым таңдау нәтижесін өңдесеңіз, пайдалана аласыз sqlcmd, оның нәтижесін талдап, күрделі нәрсені шығару мүмкін емес: бұл үшін бар Invoke-SqlCmd. Бірақ бұл команданың да өз әзілдері бар:

  • Егер сіз оған файлды жіберсеңіз -InputFileсодан кейін ШЫҒУ қажет емес, сонымен қатар ол синтаксистік қатені тудырады
  • -Шығыс файлы жоқ, пәрмен нәтижені нысан ретінде қайтарады
  • Серверді көрсету үшін екі синтаксис бар: -ServerInstance -Пайдаланушы аты -Пароль -Дерекқор және арқылы -ConnectionString. Бір қызығы, бірінші жағдайда 1433-тен басқа портты көрсету мүмкін емес.
  • мәтінді шығару, PRINT теріңіз, ол жай ғана «ұсталады» sqlcmd, үшін Invoke-SqlCmd проблема болып табылады
  • Және ең бастысы: Сіздің Linux жүйесінде бұл командлет жоқ болуы мүмкін!

Ал бұл басты мәселе. Тек наурыз айында бұл командлет Windows емес платформалар үшін қолжетімді болды, және соңында біз алға жылжи аламыз!

Айнымалы алмастыру

sqlcmd -v көмегімен ауыспалы ауыстыруға ие, мысалы:

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

SQL сценарийінде біз ауыстыруларды қолданамыз:

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

Солай. *nix ішінде айнымалы алмастырулар жұмыс істемейді... Параметр -v еленбейді. У Invoke-SqlCmd еленбеді -Айнымалылар. Айнымалылардың өздерін көрсететін параметр еленбегенімен, ауыстырулардың өзі жұмыс істейді — Shell-тен кез келген айнымалы мәндерді пайдалануға болады. Дегенмен, мен айнымалыларға ренжідім және оларға мүлдем тәуелді болмауға шешім қабылдадым және дөрекі және қарабайыр әрекет еттім, өйткені SQL сценарийлері қысқа:

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

Бұл, сіз түсінгеніңіздей, Unix нұсқасынан алынған сынақ.

Файлдарды жүктеп салу

Windows нұсқасында кез келген операция аудитпен бірге жүрді: біз sqlcmd іске қостық, шығыс файлында қандай да бір теріс пайдалануды алдық, бұл файлды аудит тақтасына тіркедік. Бақытымызға орай, SQL сервері Дженкинс сияқты бір серверде жұмыс істеді, ол келесідей орындалды:

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

Осылайша, біз BCP файлын толығымен жұтып, оны аудит кестесінің nvarchar(max) өрісіне итереміз. Әрине, бұл бүкіл жүйе ыдырап кетті, өйткені мен SQL серверінің орнына RDS алдым, ал BULK INSERT файлға эксклюзивті құлып алу әрекетіне байланысты UNC арқылы мүлде жұмыс істемейді, ал RDS-те бұл әдетте жойылады. ең басы. Сондықтан мен аудитті жол бойынша сақтай отырып, жүйе дизайнын өзгертуді шештім:

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

Және осы кестеге келесідей жазыңыз:

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

Мазмұнды таңдау үшін n (идентификатор) ретін таңдап, идентификатор бойынша таңдау керек.

Келесі мақалада мен мұның бәрі Дженкинспен қалай әрекеттесетіні туралы толығырақ айтып беремін.

Ақпарат көзі: www.habr.com

пікір қалдыру