PowerShell Code failing to retrieve the entire XML column data - database

I have a requirement to read a XML column from a SQL Server table, and update a table in a different SQL Server database.
Following is the simplified version of the code. What is happening is I’m NOT getting the entire XML value. I need a way to increase XML variable length. The value in $TargetXMLPValue gets truncated.
$sqlSource = "select XMLColumn from T1 where Id = 788"
$result = Invoke-Sqlcmd -ServerInstance 'SourceServer' -Query $sql -ConnectionTimeout 60 -QueryTimeout 99999
$TargetXMLValue = $result.XMLColumn
$sqlTarget = "Update t2 where AnotherXMLColumn = $TargetXMLValue where ID=788"
Invoke-Sqlcmd -ServerInstance 'TargetServer' -Query $sqlTarget -ConnectionTimeout 60 -QueryTimeout 99999

Found the answer! It is nothing to do with XML data type. I had to use MAXCHARLENGTH switch to increase the width of resulting column.
$result = Invoke-Sqlcmd -ServerInstance 'SourceServer' -Query $sql -ConnectionTimeout 60 -QueryTimeout 99999 -MaxCharLength 20000

Related

SQL Server Query in PowerShell, limit my results based on date

I am using PowerShell to connect to a SQL Server database, and I want to limit my result.
I use the SqlServer Module, and Invoke-Sqlcmd. I don't want data where the date is older than 90 days. The following code is used to get the complete data
$DataRange = ((Get-date) + (New-TimeSpan -Days -90))
$Database = DB02_Tool.dbo.Cert
$ServerInstans = DBClu01\DbClu01Cert
$Query = "SELECT encoded, not_before, not_after FROM " + $Database
$Data = Invoke-SqlCmd -ServerInstance $ServerInstans -Query $Query -As DataRows
The limit, I want to implement is on the not_before, which is of the datatype datetime, and I want it to limit on dates not older than 90 days back, as the $Daterange is used for.How do I limit the query? Any ideas?
You can do this in the SQL Query. Using DATEADD to add or substract days, and GetDate() to get the current datetime.
$Query = "SELECT encoded, not_before, not_after FROM " + $Database + " WHERE not_before >= DATEADD(Day, -90, GetDate())"
Or you can pass in a parameter from Powershell using -Variable switch. See the example 3 in the documentation.
You first need to convert the string to a universal datetime using ToString("u")
$StringArray = "MYVAR1='$($DataRange.ToString("u"))'"
$Query = "SELECT encoded, not_before, not_after FROM " + $Database + " WHERE not_before >= `$(MYVAR1)"
$Data = Invoke-SqlCmd -ServerInstance $ServerInstans -Query $Query -Variable $StringArray -As DataRows

Invoke-Sqlcmd : Duplicate column names are not permitted in SQL PowerShell

I've spitted Glenn Berry's Performance Query in 14 queries but one of them (number 9) is not working.
This is my PowerShell code:
#Provide SQLServerName
$SQLServer ="localhost"
#Provide Database Name
$DatabaseName ="master"
#Prompt for user credentials
$credential = Get-Credential
$Query1= "-- 09 Index Sizes
-- Note: THIS IS SLOW as it reads index blocks. SAMPLED is not that high, but watch for prod I/O impact if using 'DETAILED'
SELECT DB_NAME() AS DatabaseName,
Object_name(i.object_id) AS TableName
,i.index_id, name AS IndexName
,i.type_desc
,ips.page_count, ips.compressed_page_count
,CAST(ips.avg_fragmentation_in_percent as DECIMAL(5,1)) [fragmentation_pct]
,CAST(ips.avg_page_space_used_in_percent as DECIMAL(5,1)) [page_space_used_pct]
,ips.index_depth, ips.page_count ,ips.forwarded_record_count, ips.record_count
FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL, NULL, 'SAMPLED') AS ips -- or SAMPLED
INNER JOIN sys.indexes AS i ON ips.object_id = i.object_id AND ips.index_id = i.index_id
where ips.page_count > 1
ORDER BY ips.record_count desc;"
Invoke-Sqlcmd -Database $DatabaseName -ServerInstance $Server -Credential $credential -Query $Query1 | Format-Table
The error returned says:
Invoke-Sqlcmd : Duplicate column names are not permitted in SQL PowerShell. To repeat a column, use a column alias for the duplicate
column in the format Column_Name AS New_Name.
At C:\Users\FrancescoM\Desktop\CSV\Test.ps1:23 char:1
+ Invoke-Sqlcmd -Database $DatabaseName -ServerInstance $Server -Crede ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : SyntaxError: (:) [Invoke-Sqlcmd], SqlPowerShellSqlExecutionException
+ FullyQualifiedErrorId : DuplicateColumnNameErrorMessage,Microsoft.SqlServer.Management.PowerShell.GetScriptCommand
Not sure what is the duplicate column because if I run the query called into the PowerShell script on SSMS I don't see any duplicate column:
Arrived at home I formatted the query in a decent way and thanks to Notepad++, after clicking on each column I found out that ips.page_count was called twice.
So there was indeed a column called twice.
Use the following parameter for the Invoke-Sqlcmd command.
-OutputSqlErrors $False
It will suppress the error.
Documentation:
https://learn.microsoft.com/en-us/powershell/module/sqlserver/invoke-sqlcmd?view=sqlserver-ps
You get this error when you have duplicate column names in resultset of the query.
invoke-sqlcmd : Duplicate column names are not permitted in SQL PowerShell
Suppose you used below query as to see result-set in your invoke-sqlcmd query-
select test, * from testtable
Now the test column will be duplicate in the result and invoke sqlcmd execution will fail.
To Resolve this use like this-
select test as testColumn, * from testtable

Using PowerShell to Run a SQL query then insert the results into a table

I have a small PowerShell script that runs a query on about 30+ servers which pulls the server name and which version of SQL Server is installed. I then want to insert that data into a table but can't quite figure out how to do that with the returned data set my query returns. This is my code so far
$SvrNameList = #( invoke-sqlcmd -serverinstance MyServer -Database MyDB -Query "SELECT ServerName FROM ServerNames WHERE [Enabled] = 1" ) | select-object -expand ServerName
foreach ( $i in $SvrNameList )
{
invoke-sqlcmd -ServerInstance $i -Query "SELECT ##ServerName AS ServerName, ##Version AS Version"
}
Any help is appreciated
First, add the instance names and versions into a hash table. After it's populated, you can do inserts into the result table. Like so,
$ht=#{} # Create empty hashtable
foreach ( $i in $SvrNameList ){
$r = invoke-sqlcmd -ServerInstance $i -Query "SELECT s=##ServerName, v=##Version" # Query server names and versions
$ht.Add($r.s, $r.v) # Add name and version into hashtable
}
# Enumerate the hashtable and generate insert commands
$ht.GetEnumerator() | % {
invoke-sqlcmd -ServerInstance foo -query "insert into t(s, v) values ('" + $_.value + "', '"+ $_.name +"');"
}

Error passing variable SQL instance name into Invoke-SQLcmd

I'm working on a project that pulls a list of SQL instances from server A and then loops through the returned list and runs a query (eventually to audit users and insert results into table) but getting an error instance not found. It seems I'm not defining the variable correctly in the loop because if I hardcode the instance name it works.
I appreciate any input on how to fix this.
$Serverlist = invoke-sqlcmd -ServerInstance TESTSERVER1 -Database TESTDB -Query "SELECT instancename from testtable"
foreach ($SQLInst in $Serverlist)
{
$Inst = $SQLInst.INSTANCE
Invoke-Sqlcmd -ServerInstance ${$Inst} -Database Master -Query "select ##servername as servername" | select -ExpandProperty servername
} #end foreach loop
Invoke-Sqlcmd : A network-related or instance-specific error occurred
while establishing a connection to SQL Server. The server was not
found or was not accessible. Verify that the instance name is correct
and that SQL Server is configured to allow remote connections.
(provider: Named Pipes Provider, error: 40 - Could not open a
connection to SQL Server) At line:12 char:1
+ Invoke-Sqlcmd -ServerInstance ${$SQLInst} -Database Master -Query "select ##serv ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Invoke-Sqlcmd], SqlException
+ FullyQualifiedErrorId : SqlExectionError,Microsoft.SqlServer.Management.PowerShell.GetScriptCommand
There's no reason to use curly braces like that ${$Inst} in this instance.
Simply using $Inst will work fine. If you do use curly braces, then you don't use the $ inside: ${Inst}.
Invoke-Sqlcmd -ServerInstance $Inst
# or
Invoke-Sqlcmd -ServerInstance ${Inst}
I would check to make sure that each instance is the correct one:
$Serverlist = invoke-sqlcmd -ServerInstance TESTSERVER1 -Database TESTDB -Query "SELECT instancename from testtable"
foreach ($SQLInst in $Serverlist)
{
$Inst = $SQLInst.INSTANCE
Write-Host $Inst
} #end foreach loop
I noticed some problems with my previous statement. Can you try this?
$Serverlist = invoke-sqlcmd -ServerInstance TESTSERVER1 -Database TESTDB -Query "SELECT instancename from testtable"
foreach ($SQLInst in $Serverlist)
{
$Inst = $SQLInst.instancename
Invoke-Sqlcmd -ServerInstance "$Inst" -Database Master -Query "select ##servername as servername" | select -ExpandProperty servername
} #end foreach loop
When you are referencing a result from a query, you must specify the column name even if there is only one column in the query. Enclosing the query in parentheses and using dot notation with the column name will convert the rows to a list of values. Your original problem was you had the column name incorrect.
Try this:
$Serverlist = ( invoke-sqlcmd -ServerInstance TESTSERVER1 -Database TESTDB -Query "SELECT instancename from testtable").instancename
$Serverlist | ForEach-Object {
( Invoke-Sqlcmd -ServerInstance $SQLInst -Database Master -Query 'select ##servername as servername' ).servername
}

Invoke-Sqlcmd cmdlet throws exception when using -Variable parameter

When I try to use the Invoke-Sqlcmd cmdlet from SQL Server 2008 to execute a query that contains scripting variables, ex. $(MyVar), I receive the following exception:
Invoke-Sqlcmd : Object reference not set to an instance of an object.
Here's the code I'm trying to run (which is copy/paste from the Books Online example with only the connection parameters added).
$MyArray = "MyVar1 = 'String1'", "MyVar2 = 'String2'"
Invoke-Sqlcmd -Query "SELECT `$(MyVar1) AS Var1, `$(MyVar2) AS Var2;" -Variable $MyArray -ServerInstance "localhost" -Database "master" -UserName "who" -Password "me"
If I replace $(MyVar1) and $(MyVar2) in the -Query with 'x' and 'y' then it runs perfectly.
$MyArray = "MyVar1 = 'String1'", "MyVar2 = 'String2'"
Invoke-Sqlcmd -Query "SELECT 'x' AS Var1, 'y' AS Var2;" -Variable $MyArray -ServerInstance "localhost" -Database "master" -UserName "who" -Password "me"
Can anyone tell me why this is not working?
Indeed this is a bug in SQL Server - tracked and fixed here
https://connect.microsoft.com/sqlserver/feedback/details/358291/invoke-sqlcmd-powershell-cmdlet-fails-when-array-passed-via-variable
However, there's a posted workaround. Remove the spaces around the assignment. So instead of
$MyArray = "MyVar1 = 'String1'", "MyVar2 = 'String2'"
use
$MyArray = "MyVar1='String1'", "MyVar2='String2'"
Ok. I posted this same question on the SQL Server forums and, apparently, this is a bug in SQL Server 2008's PowerShell cmdlets... follow the thread here.
Try this alone:
PS>$MyArray = "MyVar1 = 'String1'", "MyVar2 = 'String2'"
Now:
PS>$MyArray
and
PS>MyVar1
Now:
PS>$MyArray|get-member
PowerShell thinks you've assigned 2 string objects to $MyArray, nothing more. This approach does not result in defining the variables $MyVar1 and $MyVar2 to PowerShell.
Sorry, I can't fire up my SQL2008 VM right now to comment on the other parts...

Resources