I am running a script which triggers a query to fetch the worst performing SQL query for an interval of 1hr from the SQL server & also formatting the output using "Format-Table -AutoSize". But the query is not printing full query rather it is printing partial.
Code Snippet:
function PerformerQuery {
$i=1
$number=2
do{
$serverInstanceP = "SQLExpress2014"
$perfQuery="
USE [master]
SELECT TOP 20
total_worker_time/execution_count AS Avg_CPU_Time
,Execution_count
,total_elapsed_time/execution_count as AVG_Run_Time
,total_elapsed_time
,(SELECT SUBSTRING(text,statement_start_offset/2+1,statement_end_offset) FROM sys.dm_exec_sql_text(sql_handle)) AS Query_Text
FROM sys.dm_exec_query_stats
ORDER BY Avg_CPU_Time DESC
"
Invoke-Sqlcmd -ServerInstance $serverInstanceP -Database master -Query $perfQuery -QueryTimeout 9400| Format-Table -AutoSize
Start-Sleep -s 3
}
while ($i -le $number)
}
PerformerQuery
Tried with "Out-String" & "Out-Gridview", but the same output. Cannot use "ft" as it is printing the discrete data.
Please refer the screenshot as the output (added both command line output and database server output).
Database Server output:
Commandline output:
Did you try to add -Wrap to the Format-Table and/or pipe it to Out-String -Width 500 so it does not use the terminal width as a limit.
Related
Am trying to use below code but am getting a syntax error on line 4 chr 5. For the love of me I am not able to find out what's wrong with my code. Can someone please help, Must be something really really stupid.
PS, this is not my code, someone helped me with it.
$csv = Import-Csv 'D:\Chayan\POmiss\test.csv'
$SQLSERVER = "WSQL009D"
Foreach ($row in $csv) {
Invoke-Sqlcmd -ServerInstance $SQLSERVER -Database BuyerWorksheetCentralRepository -Query "INSERT
into BuyerWorksheetCentralRepository.[po].[PurchaseOrderMessage]
values (
$($row.PurchaseOrderID),
4,
'Timeout waiting for mainframe to send response message',
getdate(),
'deb00535'
)"
}
#Steven answer is good about using Here Strings make things simpler and easier to debug. In this case, it is not a PowerShell error that is happening. When you see "Msg 102, Level 15, State 1" that is a SQL error. You will have to check the schema of your database to ensure that you are passing in the right value. If you POID is a varchar type, then you may be simply missing a set of quotes around the VALUES that you are passing. e.g. :
...
values (
'$($row.PurchaseOrderID)',
4,
'Timeout waiting for mainframe to send response message',
getdate(),
'deb00535'
)
...
Remember, you are replacing $($row.PurchaseOrderID) with the value in the CSV. You still need the single quotes in the query for SQL to not complain ;-). So your code becomes:
$csv = Import-Csv 'D:\Chayan\POmiss\test.csv'
$SQLSERVER = "WSQL009D"
Foreach ($row in $csv) {
$query =
#"
INSERT
into BuyerWorksheetCentralRepository.[po].[PurchaseOrderMessage]
values (
'$($row.PurchaseOrderID)',
4,
'Timeout waiting for mainframe to send response message',
getdate(),
'deb00535'
)
"#
Invoke-Sqlcmd -ServerInstance $SQLSERVER -Database BuyerWorksheetCentralRepository -Query $query
}
If you need debugging, you can always do:
Write-Host $query
And see if that query will actually work in SQL.
The last thing that might be happening is to check your CSV file in Notepad. You may have an extra row of data that is blank. The error message eerily seems like you have one entry that has a blank value that is trying to be inserted.
EDIT
#ChayanChakraborty Provided the sample file:
PurchaseOrderID PONumMaster DivisionNum HoursElapsedSinceLastStatusUpdate
--------------- ----------- ----------- ---------------------------------
16601566 536958 8 2070
16601613 536998 8 1471
16601626 537011 8 700
Unfortunately #ChayanChakraborty, that is not a CSV file. Adding .CSV to the file name does not make it a Comma-Separated Values file. If you are exporting to file from Microsoft SQL Server Management Studio, you have to change the output options (see Here).
In Microsoft SQL Server Management Studio, go to Tools -> Options... menu. Then go to the Query Results -> SQL Server -> Results to Text section. Change the Output Format to Comma delimited, click Ok, and re-run your query. Your CSV file should only look like:
PurchaseOrderID,PONumMaster,DivisionNum,HoursElapsedSinceLastStatusUpdate
16601566,536958,8,2070
16601613,536998,8,1471
16601626,537011,8,700
You should verify that the end of the file also doesn't have the results as that will mess it up:
(3 rows affected)
Completion time: 2021-05-14T11:29:21.9260884-06:00
My guess is Invoke-SQLcmd isn't able to interpret the multi-line argument you gave -Query. This is typically done using a here string, like below:
$csv = Import-Csv 'D:\Chayan\POmiss\test.csv'
$SQLSERVER = "WSQL009D"
$QueryTemplate =
#"
INSERT INTO BuyerWorksheetCentralRepository.[po].[PurchaseOrderMessage]
VALUES (
%PurchaseOrderID%,
4,
'Timeout waiting for mainframe to send response message',
GETDATE(),
'deb00535'
)
"#
Foreach ($row in $csv) {
$Query = $QueryTemplate -replace '%PurchaseOrderID%', $row.PurchaseOrderID
Invoke-Sqlcmd -ServerInstance $SQLSERVER -Database BuyerWorksheetCentralRepository -Query $Query
}
I changed it a little bit. Rather than recreate the entire query for every loop iteration I created a template with a known string to replace with data you're getting from the CSV, namely $row.PurchaseOrderID.
How to force Invoke-SqlCmd in Powershell to flush its output? Basically the same question asked at https://github.com/PowerShell/PowerShell/issues/3060, where Powershell people say it's SQL's problem whereas "SQL folks say this is an issue with the default powershell output, not their code".
Here is a simple demo:
Write-Host "Run 1"
Invoke-Sqlcmd -Query "SELECT GETDATE() AS CurrentTime;"
#Read-Host -Prompt "press a key..."
Write-Host "Run 2"
Invoke-Sqlcmd -Query "SELECT GETDATE() AS CurrentTime;"
the output would look like this:
Run 1
Run 2
CurrentTime
-----------
8/23/2019 9:24:44 AM
8/23/2019 9:24:44 AM
whereas I am expecting that the first output is after "Run 1" and before "Run 2". If commenting out the Read-Host, the Invoke-Sqlcmd get separated but still not what I'm expecting:
Run 1
press a key...:
CurrentTime
-----------
8/23/2019 9:22:28 AM
Run 2
8/23/2019 9:22:32 AM
So, how to force Invoke-SqlCmd in Powershell to flush its output?
Please see your script after modification below:
$run1 = Invoke-Sqlcmd -Query "SELECT GETDATE() AS CurrentTime;" | Out-String;
Write-Host "Run 1 is $run1"
#Read-Host -Prompt "press a key..."
Start-Sleep 2 #To simulate different timing
$run2 = Invoke-Sqlcmd -Query "SELECT GETDATE() AS CurrentTime;" | Out-String
Write-Host "Run 2 is $run2"
And the output will be,
Run 1 is
CurrentTime
-----------
8/23/2019 11:51:47 AM
Run 2 is
CurrentTime
-----------
8/23/2019 11:51:49 AM
I'm trying to read values from a CSV file, embed them into a INSERT T-SQL statement and run that statement using Invoke-Sqlcmd.
Here's my code:
Push-Location; Import-Module SQLPS -DisableNameChecking; Pop-Location
$InsertQry = "insert into $ImportTable VALUES ('`$(Col1)','`$(Col2)','`$(Col3)','`$(Col4)') "
Import-CSV $ImportFile | ForEach-Object { `
$RowData = "Col1=$($_.{My Ref})","Col2=$($_.{General satisfaction})","Col3=$($_.Helpfulness)","Col4=$($_.Effort)"
Invoke-Sqlcmd `
-Database $DBName -ServerInstance $SQLServer `
-Query $InsertQry `
-Variable $RowData
}
The script works fine for rows in the CSV file that contain values for each column. Unfortunately for me, some of the rows in the CSV file contain empty values (so perhaps only the first two columns contain values). These rows fail to be inserted into the table, and generate the following error:
Invoke-Sqlcmd : The format used to define the new variable for
Invoke-Sqlcmd cmdlet is invalid. Please use the 'var=value' format for
defining a new variable.
The potentially empty columns are all columns that are either empty or contain a single digit number 1 to 5.
I've tried various ways to escape the value, cast it to a different data type, add zero or an empty string to it, null coalesce it, but I cannot get a solution that works.
I have control over the destination table, so I'd be happy to pass zero, empty string, null or any other value as a placeholder for the empty values.
As per the documentation you are to pass variables in a string array where each element has a "key=value" format. You are building that correctly. Invoke-SQLCMD seems to take offence to null values being passed. The nulls of course are coming from blank entries in your CSV. Assuming you allow nulls in those columns then perhaps you could just adjust the query as each loop pass instead.
Push-Location; Import-Module SQLPS -DisableNameChecking; Pop-Location
$InsertQry = "insert into $ImportTable VALUES ('{0}','{1}','{2}','{3}')"
$propertiesToSplat = #{
Database = $DBName
ServerInstance = $SQLServer
}
Import-CSV $ImportFile | ForEach-Object {
$propertiesToSplat.Query = $InsertQry -f $_."My Ref", $_."General satisfaction", $_.Helpfulness, $_.Effort
Invoke-Sqlcmd #propertiesToSplat
}
So at each loop pass we use the format operator to insert the column values into your insert statement. Using curly braces in property names is useful when your properties contain special characters. Since you just have to deal with a space; quotes work just as well.
I also wanted to show you splatting which is a method to pass properties as a hashtable to a cmdlet. This lets you edit props on the fly and keep your lines shorter without having to worry about backticks everywhere.
Edit - completely new answer
I suck at ForEach-Object. This a foreach loop that checks the value of "General staisfaction" for each line in the CSV, and replaces it with a placeholder string before completing the $RowData variable. Unfortunately I cannot test it here; please let me know how you get on.
Push-Location; Import-Module SQLPS -DisableNameChecking; Pop-Location
$InsertQry = "insert into $ImportTable VALUES ('`$(Col1)','`$(Col2)','`$(Col3)','`$(Col4)') "
$myCSVFile = Import-CSV $ImportFile
foreach($line in $myCSVFile){
if($line."General staisfaction" -eq $null -or $line."General staisfaction" -eq ""){
$line."General staisfaction" = "placeholder"
}
$RowData = "Col1=$($line.{My Ref})","Col2=$($line.{General satisfaction})","Col3=$($line.Helpfulness)","Col4=$($line.Effort)"
Invoke-Sqlcmd -Database $DBName -ServerInstance $SQLServer -Query $InsertQry -Variable $RowData
}
I'm having issues making calls to the local database using the method outlined below.
Error Message
invoke-sqlcmd : Value cannot be null.
Parameter name: ServerInstance
At C:\filelocation\HealthCheckCombined.ps1:86 char:3
1. invoke-sqlcmd -query $agentquery -serverinstance $servername ...
2. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Invoke-Sqlcmd], ArgumentNullException
+ FullyQualifiedErrorId : CannotGetServerInstance,Microsoft.SqlServer.Management.PowerShell.GetScriptCommand
Environment(s)
Server 2016 Candidate 3
Server 2012
SQL Server 2014
SQL Server 2012
PowerShell 3.0, 4.0 & 5.0
Goal
I'm trying to run a query via PowerShell against whatever SQL instance is listed in the servers.txt (config file).
Two components-
External Configuration File (servers.txt)
PowerShell script containing functions, loop to create an array from servers.txt and execute the function.
So the contents of servers.txt looks like=
server=test2k16\powershell
server=test2k16\healthcheck
Here's the section where I import the text file and create the function=
#===============================================================================
#Configurable variables
#===============================================================================
$configfile = 'C:\filelocation\servers.txt'
Import-Module "sqlps"
#===============================================================================
#===============================================================================
#SQL Agent Jobs
#===============================================================================
function SQLAgent{
$agentquery= #"
declare #count int
select #count = count(1) from msdb.dbo.sysjobs as sj
join msdb.dbo.sysjobhistory as sjh on sj.job_id = sjh.job_id
where sj.enabled != 0
and sjh.sql_message_id > 0
and sjh.run_date > CONVERT(char(8), (select dateadd (day,(-30), getdate())), 112)
and sjh.Step_id <= 1
if (#count >= 1)
begin
select distinct sj.name as SQLJobName
from msdb.dbo.sysjobs as sj
join msdb.dbo.sysjobhistory as sjh on sj.job_id = sjh.job_id
where sj.enabled != 0
and sjh.sql_message_id > 0
and sjh.run_date > CONVERT(char(8), (select dateadd (day,(-30), getdate())), 112)
and sjh.Step_id <= 1
order by name
end
else
begin
Select 'No Job Failed in Last Month' as SQLJobName
end
"#
invoke-sqlcmd -query $agentquery -serverinstance $servername -username "user" -password "password" | Format-Table -AutoSize -Wrap
}
#===============================================================================
Now I make the magic happen by formatting the imported variables and looping through them while running the function=
#===============================================================================
#Run Health Check for each server
#===============================================================================
$import = $(foreach ($line in get-content $configfile) {$line.tolower().split(" ")}) | sort | get-unique
ForEach ($_ in $import){
$servername = $import.trimstart("server=")
}
ForEach ($_ in $servername){
SQLAgent
}
#===============================================================================
Findings thus far
Extracting the code within in the function and importing the text file works perfectly fine. No error.
The $servername variable in the loop displays the correct values (test2k16\powershell & test2k16\healthcheck) if I change the script to only display those variables in the loop
I'm obviously missing something... I've been searching the stack and Google for a day now and finding nothing. Hopefully it's something small I overlooked or don't understand about PowerShell yet.
Thanks in advance for any help!
You are referencing $ServerName for the -ServerInstance parameter. It expects a string, and you are presenting it with an array of strings. Also, you are using the ForEach loop incorrectly the last two times. It should be a variable name, and not the automatic variable of $_. Example:
ForEach($Server in $ServerName){
SQLAgent
}
Then change your -ServerInstance in your function to reference $Server. Better yet, set parameters for your function and feed it the info it needs within your loop.
Function SQLAgent($Server){
<Code goes here!>
invoke-sqlcmd -query $agentquery -serverinstance $server -username "user" -password "password" | Format-Table -AutoSize -Wrap
}
$ServerList = Get-Content $configfile | ForEach{$_.Split('=')[1]}
ForEach($Item in $ServerList){
SQLAgent -Server $Item
}
Why does this display nothing?
$result = foreach($db in sqlps ls SQLSERVER:\\SQL\\MYSERVER\\Databases) {
foreach($group in $db.FileGroups) {
write #{Database=$group}
}
}
$result | % { $_.Database }
But this one works fine?
$result = foreach($db in sqlps ls SQLSERVER:\\SQL\\MYSERVER\\Databases) {
write #{Database=$db}
}
$result | % { $_.Database}
How do I fix it?
Edit:
It works ,but how display the result like 'NameMyBase' = 'Status'
Blockquote
Get-ChildItem SQLSERVER:\SQL\BACKUP1\ARCSERVE_DB\Databases | select FileGroups
Blockquote
I have just the column empty
Nevertheless "select -Expand FileGroups" doesn't work : Property "FileGroups-Expand" cannot be found.
The default output format of sqlps is text, so your command sqlps ls SQLSERVER:\\SQL\\MYSERVER\\Databases is generating a bunch of strings (which don't have a FileGroups property) rather than the objects you seem to expect.
Also, I think your path is missing the instance. \SQL\HOSTNAME\Databases should raise an error (or at least it did for me). I had to use \SQL\HOSTNAME\INSTANCE\Databases.
Try something like this:
Add-PSSnapin SqlServerProviderSnapin100
Get-ChildItem SQLSERVER:\SQL\MYSERVER\INSTANCE\Databases |
select -Expand FileGroups
The snap-in adds the SQLSERVER: provider, so you can use Get-ChildItem for accessing the child objects.
Edit: With SQL Server 2012 there should be a loadable sqlps module:
Import-Module 'sqlps' -DisableNameChecking
I don't have access to an SQL Server 2012, though, so I can't verify that.
Thanks very much it 's OK for acceess object even if i hva sql 2012
i much use command like '
Blockquote
sqlps ls SQLSERVER:\SQL\BACKUP1\ARCSERVE_DB\Databases; foreach ($Item in Get-ChildItem) { write #{$Item.Name=$Item.Status}}
Blockquote
This comman list what contains the root and not what contains the Databases
I would like to do a Set-Location but the cursor don't move it display always the same thing, the root
Blockquote
sqlps Set-Location SQLSERVER:\SQL\BACKUP1\ARCSERVE_DB\Databases; foreach ($Item in Get-ChildItem) { write #{$Item.Name=$Item.Status}}
Blockquote
The Set-Location works fine if i use the command not in my program c++ but with shell prompt. How can i use Set- Location in *ONE LIN*E with sqlps?