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

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

Related

Passing Powershell variable into a SQL INSERT

I'm quite new when it comes to Powershell and I'm not sure if my method is the best.
How can I pass Powershell variables into SQL using Invoke SQL or the .Net method? All I'm trying to do is to Insert data from the Get-AzureADGroup Display Name and Object Id into a SQL table.
$Groups = Get-AzureADGroup -All $true
foreach($Group in $Groups){
$DN = $Group.DisplayName
$ID = $Group.ObjectId
$insertquery="
INSERT INTO [dbo].[Table]([DsiplayName],[ObjectId])
VALUES(''$DN'',''$ID'')
"
Invoke-Sqlcmd -ServerInstance "Server" -Database "Database" -Query $insertquery
}
However, it comes up with the error 'Incorrect syntax near 'GroupDisplayName'. I've tried using " and ' interchangeably in the query with no luck. Am I missing something?

PowerShell Code failing to retrieve the entire XML column data

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

Powershell sql insert error data will be truncated removed data and still get error

I have a powershell script that I am trying to use to write data from a source .txt to an SQL table
Import-Csv $_.fullname -Header Z_AP_EXNUM,Z_AP_ID | ForEach-Object {Invoke-Sqlcmd `
-Database $database -ServerInstance $server -Username $uid -Password $pwd `
-Query "insert into $table VALUES ('$_.Z_AP_EXNUM','$_.Z_AP_ID')"
My actual code has about 293 headers/columns to insert. The error I receiving is that String or binary data will be truncated. The statement has been terminated. I went into one of my test files and removed all the data but a few standard fields and still receive these errors. I am not sure what could be causing the insert to fail.
Error:
Invoke-Sqlcmd : String or binary data would be truncated.
The statement has been terminated.
At C:\Users***\Desktop\script\scripy.ps1:79 char:111
+ Z_AP_STR_CSEM5_9,Z_AP_STR_NAME5_10,Z_AP_STR_SCOR5_10,Z_AP_STR_CSEM5_10 | ForEach-Object {Invoke-Sqlcmd <<<< `
+ CategoryInfo : InvalidOperation: (:) [Invoke-Sqlcmd], SqlPowerShellSqlExecutionException
+ FullyQualifiedErrorId : SqlError,Microsoft.SqlServer.Management.PowerShell.GetScriptCommand
Check the table schema, this error usually shows due to the difference of column definition type in the table and the powershell data type you want to store in it.

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
}

Load text into SQL Server via Powershell

I am loading character data read from a file row by row into a SQL table. The table is:
CREATE TABLE [dbo].[PSFileOrder](
[PSFOrder_Id] [int] IDENTITY(0,1) NOT NULL,
[PSFile] [varchar](255) NOT NULL,
CONSTRAINT [PK_PSFileOrder] PRIMARY KEY CLUSTERED
(
[PSFOrder_Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
The powershell code I am using is
#LoadPSFile
$PSFile = "d:\ps\xx.ps1"
cls
$xy = Get-content $PSFile
$xy
foreach($xy in $xy) {invoke-sqlcmd -serverInstance localhost\sqlexpress -query "Insert AA.dbo.PSFileOrder(PSFile) Values ('$xy')"}
If the file I am loading is:
#Filename
#The first line will always be the file name
cls
$filter = "*.*"
$folder = "\\localhost\d$\Master\men and their toys"
$filecount = (Get-ChildItem -literalpath $folder -filter $filter).Countem -literalpath $folder -filter $filter).Countem -literalpath $folder -filter $filter).Countem -literalpath $folder -filter $filter).Count
If ($filecount -lt 1) {$filecount = 0}
"There are {0} files in the {1} directory" -f $filecount, $folder
the file loads without error. If there is a single quote in the text
"There are {0} files in the {1} d'irectory" -f $filecount, $folder
then I will recieve this error
Invoke-Sqlcmd : Incorrect syntax near 'irectory'.
Unclosed quotation mark after the character string ' -f $filecount, $folder')
;'.
At C:\Users\RC\AppData\Local\Temp\2ed13f21-5b46-4df4-a5ee-2488c3dd5ee4.ps1:6 char:35
+ foreach($xy in $xy) {invoke-sqlcmd <<<< -serverInstance localhost\sqlexpress -query "Insert AA.dbo.PSFileOrder(PSFile) Values ('$xy')"}
+ CategoryInfo : InvalidOperation: (:) [Invoke-Sqlcmd], SqlPowerShellSqlExecutionException
+ FullyQualifiedErrorId : SqlError,Microsoft.SqlServer.Management.PowerShell.GetScriptCommand
I do believe the error is caused by the way I have written the "-query" statement using single quotes arount the $xy variable.
My questions are:
Have I crafted the Invoke-sqlcmd correctly?
Is there a substitue for the single quote around $xy?
Is there a better way to load the text?
I want to maintain the one to one relationship of the text file and row in the db.
Thanks
RC
Two answers put me on the correct path of doubling the single quote prior to loading to SQL. The revised code is
#LoadPSFile
cls
Get-content "d:\ps\xx.ps1" |
Foreach-Object {$_ -replace "'", "''"} |
ForEach-Object{invoke-sqlcmd -serverInstance localhost\sqlexpress -query "Insert AA.dbo.PSFileOrder(PSFile) Values ('$_')"}
You get malformed SQL syntax. I believe the quick fix is to replace any single quote with two single quotes in $xy:
$escaped = $xy.Replace("'", "''")
invoke-sqlcmd -serverInstance localhost\sqlexpress -query "Insert AA.dbo.PSFileOrder(PSFile) Values ('$escaped')"
# or (the same but without the intermediate variable):
invoke-sqlcmd -serverInstance localhost\sqlexpress -query #"
Insert AA.dbo.PSFileOrder(PSFile) Values ('$($xy.Replace("'", "''"))')
"#
But this is perhaps not the best way. Unfortunately I am not familiar with invoke-sqlcmd capabilities. If it supports parameterized queries I would go that way and supply $xy value as it is as a parameter value (no preparation of $xy would be needed in such a case).
You must double the apostrophes within the string
foreach($xy in $xy)
{
$xy = $xy -replace "'", "''"
invoke-sqlcmd -serverInstance localhost\sqlexpress -query "Insert AA.dbo.PSFileOrder(PSFile) Values ('$xy')"
}
when performance is an issue, you choose some bulkcopy solultion. In the SQLPSX project
there is a module adolib.psm1 which contains a function invoke-bulkcopy
link text
A better solution would be to use ADO.NET paramaterized queries via the SqlCommand.Parameters property. This will solve your single apostrophe error as well as scrub your input for anything else that could create a sql error.
Unfortunately Invoke-SqlCmd does not allow you to do this. However, I modified Chad Miller's Invoke-SqlCmd2 to allow you to do this. Include Invoke-SqlCmd2.ps1 in your profile and change your script like so:
foreach($xy in $xy) {invoke-sqlcmd -serverInstance localhost\sqlexpress -query "Insert AA.dbo.PSFileOrder(PSFile) Values (#xy)" -SqlParamaters #{ '#xy' = $xy}}

Resources