Error when executing SQL-Query from Powershell - sql-server

I always get an error when executing this script:
Incorrect syntax near '#values'
I have the following function to execute my SQL statement:
$Server = "Server"
$Database = "Database"
$i = 0
function ExecuteSQLQuery(){
$Connection = New-Object System.Data.SqlClient.SqlConnection
$Connection.ConnectionString = "server='$Server';database='$Database'; trusted_connection=true;"
$Connection.Open()
$query = #
"INSERT INTO [Database].[dbo].[Folder](FolderPath,UserGroup,Permission,AccessControllType) VALUES #values
"#
$command = $connection.CreateCommand()
$command.CommandText = $query
$command.Parameters.Add("#values", $values)
$command.ExecuteNonQuery()
}
And this is the rest of the code in which I am getting the Data for the INSERT
$RootPath = "\\server\folder1\folder2\folder3"
$Folders = dir $RootPath -recurse | where {$_.psiscontainer -eq $true}
#root folder permission
$ACLs = get-acl $RootPath | ForEach-Object { $_.Access }
Foreach ($ACL in $ACLs) {
#Add-Content -Value $OutInfo -Path $OutFile
$FolderPath = $RootPath
$User = $ACL.IdentityReference
$Permission = $ACL.FileSystemRights
$AccessControllType = $ACL.AccessControlType
if($i -gt 50) {
Invoke-sqlcmd #params -Query $InsertResult
$i=0
$values = ""
}
elseif($i -eq 0) {
$values += "('$FolderPath','$User','$Permission','$AccessControllType')"
$i ++
}
else {
$values += ",('$FolderPath','$User','$Permission','$AccessControllType')"
$i ++
}
}
#subfolders
foreach ($Folder in $Folders) {
$ACLs = get-acl $Folder.fullname | ForEach-Object { $_.Access }
Foreach ($ACL in $ACLs) {
$FolderPath = $Folder.Fullname
$User = $ACL.IdentityReference
$Permission = $ACL.FileSystemRights
$AccessControllType = $ACL.AccessControlType
$IsInherited = $ACL.IsInherited
if ($i -gt 50) {
ExecuteSQLQuery
$i=0
$values = ""
}
elseif ($i -eq 0) {
$values += "('$FolderPath','$User','$Permission','$AccessControllType')"
$i ++
}
else {
$values += ",('$FolderPath','$User','$Permission','$AccessControllType')"
$i ++
}
}
}
I am new to Powershell so I don't now if the error is because of my code or if the $values contains something Powershell or SQL can't handle.
Do you guys have any ideas?

Related

Copy Azure Storage Table Service data to SQL Server

How can I copy huge amounts of data from Azure Storage Table Service to SQL Server using PowerShell?
AzTable which Microsoft recommends using does not support incremental load without underlying information about partition keys, and the documentation website is down...:
https://learn.microsoft.com/en-us/azure/storage/tables/table-storage-how-to-use-powershell
I am answering this question myself, because I have lots of trouble findind a solution online, and would like to help other people with this crap.
$StorageAccountResourceGroup = "MyResourceGroup"
$StorageAccountName = "MyStorageAccount"
$TableName = "MyStorageTableName"
$SqlConnectionString = "MySqlConnectionString"
$BulkCopy = [System.Data.SqlClient.SqlBulkCopy]::new()
$BulkCopy.DestinationTableName = "[myschema].[mytablename]"
$DataTable = <<Generate datatable from SQL-table>> # Code not included
Connect-AzAccount
$StorageAccountKey = (Get-AzStorageAccountKey -ResourceGroupName $StorageAccountResourceGroup -AccountName $StorageAccountName | Where-Object -FilterScript {$_.KeyName -eq "Key1"}).Value
$Context = New-AzStorageContext -StorageAccountName $StorageAccountName -StorageAccountKey $StorageAccountKey
$CloudTable = (Get-AzStorageTable -Context $Context -Name $TableName).CloudTable
$TableQuery = [Microsoft.Azure.Cosmos.Table.TableQuery]::new()
$Token = $null
BulkCounter = 0
do{
$ReturnObject = $CloudTable.ExecuteQuerySegmented($TableQuery, $Token)
$Token = $ReturnObject.ContinuationToken
foreach($Entry in $ReturnObject){
$Row = $DataTable.NewRow()
foreach($Column in $DataTable.Columns){
if($Column.ColumnName -eq "TimeStamp"){
$Value = $Entry.TimeStamp
} elseif($Column.ColumnName -eq "RowKey"){
$Value = $Entry.RowKey
} elseif($Column.ColumnName -eq "PartitionKey"){
$Value = $Entry.PartitionKey
} else {
if($Column.DataType -eq [System.Decimal]){
$Value = $Entry.Properties.$($Column.ColumnName).DoubleValue
} elseif($Column.DataType -eq [System.String]){
$Value = $Entry.Properties.$($Column.ColumnName).StringValue
} elseif($Column.DataType -eq [System.Guid]){
$Value = $Entry.Properties.$($Column.ColumnName).GuidValue
} elseif($Column.DataType -eq [System.datetimeoffset]){
$Value = $Entry.Properties.$($Column.ColumnName).DateTimeOffsetValue
} elseif($Column.DataType -eq [System.Int32]){
$Value = $Entry.Properties.$($Column.ColumnName).Int32Value
} elseif($Column.DataType -eq [System.Int64]){
$Value = $Entry.Properties.$($Column.ColumnName).Int64Value
} elseif($Column.DataType -eq [System.Boolean]){
$Value = $Entry.Properties.$($Column.ColumnName).BooleanValue
} elseif($Column.DataType -eq [System.Binary]){
$Value = $Entry.Properties.$($Column.ColumnName).BinaryValue
}
}
if([System.String]::IsNullOrWhiteSpace($Value)){
$Value = [System.DBNull]::value
}
$Row.($Column.ColumnName) = $Value
}
$DataTable.Rows.Add($Row)
$Counter++
}
$BulkCounter ++
if($BulkCounter % 25 -eq 0 -and $BulkCounter -ne 0){
$BulkCopy.WriteToServer($Datatable.CreateDataReader()) | Out-Null
$Datatable.Clear() | Out-Null
$BulkCounter = 0
}
} while ($Token)
if($Datatable.rows.count -gt 0){
$BulkCopy.WriteToServer($Datatable.CreateDataReader()) | Out-Null
$Datatable.Clear() | Out-Null
}
Here is also some functions for automatically generating tables from storage account tables:
function ConvertTo-SqlTypeFromEDM {
param (
$EDMType
)
if($EDMType -eq "String"){
return "varchar(100)"
} elseif($EDMType -eq "Guid"){
return "uniqueidentifier"
} elseif ($EDMType -eq "DateTime"){
return "datetimeoffset(0)"
} elseif ($EDMType -eq "Binary"){
return "varbinary(max)"
} elseif ($EDMType -eq "Int32"){
return "int"
} elseif ($EDMType -eq "Int64"){
return "long"
} elseif ($EDMType -eq "Double"){
return "decimal(25,5)"
} elseif ($EDMType -eq "Boolean"){
return "bit"
} else {
throw "Tybe $EDMType not implemented"
}
}
function Get-SqlQueryFromStorageTableSerivce {
param(
$CloudTable,
$SchemaName
)
$Query = [Microsoft.Azure.Cosmos.Table.TableQuery]::new()
$Query.TakeCount = 1
$Token = $null
$Result = $CloudTable.ExecuteQuerySegmented($Query, $Token)
$TableName = $CloudTable.Name
$TableString = "DROP TABLE IF EXISTS [$SchemaName].[$TableName]`n"
$TableString += "CREATE TABLE [$SchemaName].[$TableName] (`n`t"
$ColumnString = #()
$TableKeys = #()
if($Result.PartitionKey){
$ColumnString += "[PartitionKey] varchar(100) NOT NULL"
$TableKeys += "[PartitionKey]"
}
if($Result.RowKey){
$ColumnString += "[RowKey] varchar(100) NOT NULL"
$TableKeys += "[RowKey]"
}
if($Result.Timestamp){
$ColumnString += "[Timestamp] datetimeoffset(0) NULL"
}
foreach($Column in $Result.Properties.Keys){
$ColumnString += "[$Column] $(ConvertTo-SqlTypeFromEDM -EDMType $Result.Properties.$Column.PropertyType) NULL"
}
$TableString += $ColumnString -join ",`n`t"
$TableString += "`nCONSTRAINT [PK_1_$($TableName)_1] PRIMARY KEY CLUSTERED`n"
$TableString += "(`n`t" + ($TableKeys -join ",`n`t")
$TableString += "`n)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF, DATA_COMPRESSION = PAGE) ON [PRIMARY]"
$TableString += "`n) ON [PRIMARY]`n"
return $TableString
}

Powershell Invoke-Sqlcmd : Conversion failed when converting date and/or time from character string

I am trying to import a datetime from PowerShell into a SQL Table. It's the only thing stored in that table.
When I run this code: (The value stored in the variable is: 11/19/2020 09:51:40.3244656)
$location = 'path'
Set-Location $location
$c = Get-ChildItem 'path' | Sort { $_.LastWriteTime } | select -last 1 | foreach { $a = $_; $b = Get-Acl $_.FullName; Add-Member -InputObject $b -Name "LastWriteTime" -MemberType NoteProperty -Value $a.LastWriteTime; $b }
$c.LastWriteTime
$date = $c.LastWriteTime
Function Get-Datetime([datetime]$Date = $date ) {
$DT = Get-Date -Date $Date
[string]$DateTime2 = $DT.Month.ToString() + '/'
[string]$DateTime2 += $DT.Day.ToString() + '/'
[string]$DateTime2 += $DT.Year.ToString() + ' '
[string]$DateTime2 += $DT.TimeOfDay.ToString()
return $DateTime2
}
$finaldate = Get-Datetime
#$dateFormatted = $date -format
Invoke-Sqlcmd -ServerInstance "Server" -Database "db" -query "Update [server].[schema].[table] Set [columnName] = '$finaldate'"
I get this error:
Powershell Invoke-Sqlcmd : Conversion failed when converting date and/or time from character string.
How can I get the Powershell command to update the table?
Thank you for your help.
#NekoMusume helped me get to the answer to this specific question. After adding this line, (modified from #NekoMusume's comment): Thanks #NekoMusume! (If you want to answer, I'll mark it for you.)
$finaldate = Get-Date $finaldate -Format "yyyy/MM/dd HH:mm"
The code worked. Final Working code
$location = 'path'
Set-Location $location
$c = Get-ChildItem 'path' | Sort { $_.LastWriteTime } | select -last 1 | foreach { $a = $_; $b = Get-Acl $_.FullName; Add-Member -InputObject $b -Name "LastWriteTime" -MemberType NoteProperty -Value $a.LastWriteTime; $b }
$c.LastWriteTime
$date = $c.LastWriteTime
Function Get-Datetime([datetime]$Date = $date ) {
$DT = Get-Date -Date $Date
[string]$DateTime2 = $DT.Month.ToString() + '/'
[string]$DateTime2 += $DT.Day.ToString() + '/'
[string]$DateTime2 += $DT.Year.ToString() + ' '
[string]$DateTime2 += $DT.TimeOfDay.ToString()
return $DateTime2
}
$finaldate = Get-Datetime
$finaldate = Get-Date $finaldate -Format "yyyy/MM/dd HH:mm"
#$dateFormatted = $date -format
Invoke-Sqlcmd -ServerInstance "Server" -Database "db" -query "Update [server].[schema].[table] Set [columnName] = '$finaldate'"

New-Object : Cannot bind parameter 'Property'. Cannot convert the "" value of type PSCustomObject to type IDictionary

I'm having a hard time converting results from Invoke-SqlCmd to a PSCustomobject.
So, I input my query and SQL server, then I run the Invoke-SqlCmd function, and then I try to add data from that (the database logical name, and the autogrowth status) to my PSCustomobject, so I can return it back to my modules public function.
$sqlinstance = "---"
$query = "---"
if ($sqlInstance -match "---") {
$dbAutogrowIGP = Invoke-Sqlcmd -Query $query -ServerInstance $sqlInstance
if ($dbAutogrowIGP.Growth -gt 100 -and $dbAutogrowIGP.Growth -lt 500) {
$autogrowStatus = [PSCustomObject]#{
'SQL_Instance' = $dbAutogrowIGP.LogicalName
'Check' = "Autogrow"
'Status' = "green"
'Status_reason' = ""
}
New-Object -Type Dictionary -Property $autogrowStatus
}
foreach ($db in $dbAutogrowIGP) {
if ($db.Growth -lt 100 -or $db.Growth -gt 500 -and $db.Growth -notlike "%") {
$autogrowStatus = [PSCustomObject]#{
'SQL_Instance' = $db.LogicalName
'Check' = "Autogrow"
'Status' = "red"
'Status_reason' = "$($db.LogicalName) has autogrowth set to $($db.Growth)."
}
New-Object -Type Dictionary -Property $autogrowStatus
}
if ($db.Growth -like "%") {
$autogrowStatus = [PSCustomObject]#{
'SQL_Instance' = $db.LogicalName
'Check' = "Autogrow"
'Status' = "yellow"
'Status_reason' = "$($db.LogicalName) has autogrowth set percentually, it should be an absolute number."
}
New-Object -Type Dictionary -Property $autogrowStatus
}
}
}
return $autogrowStatus
I've debugged it, and I've noticed it fails on the New-object call. I've tried both Dictionary and PSObject/PSCustomObject - however neither works
In my other functions, this works as expected, however in those, I'm using dbatools to make a call.
$getLogSizeIGP = Get-DbaDbLogSpace -sqlInstance $sqlInstance
if ($getLogSizeIGP.LogSize.Gigabyte -lt 10 -and $getLogSizeIGP.LogSpaceUsedPercent -lt 50) {
$logStatus = #{
'SQL_Instance' = $getLogSizeIGP.SqlInstance
'Check' = "Log_size"
'Status' = [gmEnvStatuses]::green
'Status_reason' = ""
}
New-Object -Type PSObject -Property $logStatus
}
How would I go about solving this issue?
This is the whole error message:
New-Object : Cannot bind parameter 'Property'. Cannot convert the "#{SQL_Instance=Maintenance_log; Check=Autogrow; Status=red; Status_reason=Maintenance_log has autogrowth set to 10%.}" value of type "System.Management.Automation.PSCustomObject" to type
"System.Collections.IDictionary".
At C:\Users\---\Desktop\autogrowth.ps1:50 char:55
+ New-Object -Type Dictionary -Property $autogrowStatus
+ ~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [New-Object], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.NewObjectCommand
Thanks!
The easiest way of collecting this data is by capturing it all at the beginning of the if ($sqlInstance -match "---") { statement and simply output the PsCustomObjects without trying to convert them.
Something like
$sqlinstance = "---"
$query = "---"
$autogrowStatus = if ($sqlInstance -match "---") {
$dbAutogrowIGP = Invoke-Sqlcmd -Query $query -ServerInstance $sqlInstance
if ($dbAutogrowIGP.Growth -gt 100 -and $dbAutogrowIGP.Growth -lt 500) {
# output the object to be captured in the $autogrowStatus variable
[PSCustomObject]#{
'SQL_Instance' = $dbAutogrowIGP.LogicalName
'Check' = "Autogrow"
'Status' = "green"
'Status_reason' = ""
}
}
foreach ($db in $dbAutogrowIGP) {
if ($db.Growth -lt 100 -or $db.Growth -gt 500 -and $db.Growth -notlike "%") {
[PSCustomObject]#{
'SQL_Instance' = $db.LogicalName
'Check' = "Autogrow"
'Status' = "red"
'Status_reason' = "$($db.LogicalName) has autogrowth set to $($db.Growth)."
}
}
if ($db.Growth -like "%") {
[PSCustomObject]#{
'SQL_Instance' = $db.LogicalName
'Check' = "Autogrow"
'Status' = "yellow"
'Status_reason' = "$($db.LogicalName) has autogrowth set percentually, it should be an absolute number."
}
}
}
}
return $autogrowStatus
The variable $autogrowStatus will become an array of PSCustomObjects to handle in the rest of your functions.
Hope this helps

Using powershell to handle multiple SQL resultsets

I have several queries that I use for identifying issues in a SQL database but I'm trying to create at powershell script that I can use to do this automatically. The trouble I am having is that when I invoke my SQL scripts there are multiple result sets and my script only seems to capture the first set. I'm wondering what I need to do to cycle through all the results. This is the code with just some simple selects
$dataSource = 'Server'
$database = "DB"
$sqlcommand = #"
Select TOP 1000 * from tblA;
Select TOP 1000 * from tblB
"#
Function Convert-Dataset
{
Param
(
[Parameter(Mandatory=$true)]
$dataset
)
Begin
{
$return=#()
For($r = 0; $r -lt $dataset.tables[0].rows.Count; $r++)
{
$table= new-object psobject
If($dataset.tables[0].columns.ColumnName.Count -le 1)
{
$colName = [String]$dataset.tables[0].columns.ColumnName
If($dataset.tables[0].rows.Count -eq 1)
{
$colValue = [string]$dataset.tables[0].rows.$colName
}
Else
{
$colValue = [string]$dataset.tables[0].rows[$r].$colName
}
$table | Add-Member -memberType noteproperty -Name $colName -Value $colValue
}
Else{
For($c = 0; $c -lt $dataset.tables[0].columns.ColumnName.Count; $c++)
{
$colName = [String]$dataset.tables[0].columns.ColumnName[$c]
$colValue = [string]$dataset.tables[0].rows[$r][$c]
$table | Add-Member -memberType noteproperty -Name $colName -Value $colValue
}
}
$return +=$table
}
}
End
{
Return $return
}
}
$connectionString = "Data Source=$dataSource; " +
"Integrated Security=True; " +
"Initial Catalog=$database"
$connection = new-object system.data.SqlClient.SQLConnection($connectionString)
$command = new-object system.data.sqlclient.sqlcommand($sqlCommand,$connection)
$connection.Open()
$adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command
$dataset = New-Object System.Data.DataSet
$adapter.Fill($dataSet) | Out-Null
$connection.Close()
$return=Convert-Dataset -dataset $dataset
$return | Out-GridView
I figured it out
$connectionString = "Data Source=$dataSource; " +
"Integrated Security=True; " +
"Initial Catalog=$database"
$connection = new-object system.data.SqlClient.SQLConnection($connectionString)
$command = new-object system.data.sqlclient.sqlcommand($sqlCommand,$connection)
$connection.Open()
$adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command
$dataset = New-Object System.Data.DataSet
$adapter.Fill($dataSet) | Out-Null
$connection.Close()
ForEach($table in $dataset.Tables)
{
$table |Out-GridView -PassThru
}

Splitting .mdf database into several files

I need to split database .bak into several files, using powershell. How best to do it?
Any examples?
Something like this might work:
$filename = "C:\path\to\your.bak"
$chunksize = 20480
$totalsize = (Get-Item $filename).Length
$stream = New-Object IO.FileStream($filename, [IO.FileMode]::Open)
$reader = New-Object IO.BinaryReader($stream)
$chunk = New-Object byte[] $chunksize
$size = $chunksize
$i = 0
do {
$rest = $totalsize - ($i * $chunksize)
$i++
if ($chunksize -gt $rest) {
$size = $rest
$chunk = New-Object byte[] $size
}
$reader.Read($chunk, 0, $size) | Out-Null
[IO.File]::WriteAllBytes(({0}.{1:d3}" -f ($filename, $i)), $chunk)
} until ($chunksize -gt $rest)
$reader.Close()
$stream.Close()

Resources