I'm looping through a list of sql servers, querying data and creating a dataset which works great. The dataset is piped to a csv file, which has all the rows. I'm then trying to create and array from each row then email the results. The email is being sent but the contents only contain the records from the last server in the list.
ForEach ($instance in Get-Content "D:\servers\sqlservers2.txt")
{
$SQLServer = "$instance" #use Server\Instance for named SQL instances!
$SQLDBName = "msdb"
$SqlQuery = "
Select
##servername as [Server],
j.[name] AS [JobName],
run_status = CASE h.run_status
WHEN 0 THEN 'Failed'
WHEN 1 THEN 'Succeeded'
WHEN 2 THEN 'Retry'
WHEN 3 THEN 'Canceled'
WHEN 4 THEN 'In progress'
END,
h.run_date AS LastRunDate,
h.run_time AS LastRunTime
FROM sysjobhistory h
INNER JOIN sysjobs j ON h.job_id = j.job_id
WHERE j.enabled = 1
and j.name not like 'copytosan'
and j.name not like 'syspolicy%'
and j.name not like '%log%'
AND h.instance_id
IN
(SELECT MAX(h.instance_id)
FROM sysjobhistory h GROUP BY (h.job_id))
"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server =
$SQLServer; Database = $SQLDBName; Integrated Security = True"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $SqlQuery
$SqlCmd.Connection = $SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$SqlConnection.Close()
#File is created with all rows from dataset
$DataSet.Tables[0] | Out-File D:\servers\myfiles.csv -Append
}
foreach ($line in $dataset.tables[0])
{
$body += $line
}
# (Out-String -InputObject $body -Width 100)
Send-MailMessage -To Bob#domain.com -from SQLMAIL#domain.com -Subject Test12345 -body (Out-String -InputObject $body -Width 100) -SmtpServer mail#domain.com
You need to nest your second loop. That way it will loop through the rows of each table instead of only the last table.
ForEach ($instance in Get-Content "D:\servers\sqlservers2.txt")
{
# code
foreach ($line in $dataset.tables[0])
{
$body += $line
}
}
Related
Suppose i have two different kind of servers
Function Query($Query) {
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server=$Server;Initial Catalog=$Database;Integrated Security=SSPI"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.Connection = $SqlConnection
$SqlCmd.CommandText = $Query
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$DataSet = New-Object System.Data.DataSet
$a=$SqlAdapter.Fill($DataSet)
$SqlConnection.Close()
$DataSet.Tables[0] }
$servers_typeA = Query "SELECT DISTINCT [server_typeA] FROM table" | Select -ExpandProperty server_typeA;
$servers_typeB = Query "SELECT DISTINCT [server_typeB] FROM table" | Select -ExpandProperty server_typeB;
I have a forloop that for now loops through servers in typeA and displays connections of databases for each of the servers in $servers_typeA
foreach($server in $servers_typeA)
instead of duplicating the same loop for typeB, so
foreach($server in $servers_typeB)
Is theer a way i can tell the forloop to loop typeB after A?
Essentially, like this?
foreach($server in $servers_typeA then $servers_typeB)
You can simply concatenate the arrays with + in order to loop over their combined items; a simplified example:
$servers_typeA = 'foo', 'bar'
$servers_typeB = 'baz', 'bam'
foreach ($server in $servers_typeA + $servers_typeB) {
"processing server $server..."
}
Note: If there's a chance that the LHS of the concatenation is actually a scalar, not an array, use #(...) around it (not necessary for the RHS): #($servers_typeA) + $servers_typeB
The above yields:
processing server foo...
processing server bar...
processing server baz...
processing server bam...
I have a ps1 script created to run a query on a SQL Server database. After that I run the rows in a for loop however, the dataset is duplicating entries. If I run the query directly on MS SQL without the script, it does not duplicate rows.
$SQLServer = "bla";
$SQLDBName = "bla";
$uid ="bla";
$pwd = "HIdden_Password";
$App="com.android.app";
$SqlQuery = "SELECT value
FROM obj_user u
JOIN obj_user_device d ON d.id_user = u.id_user
JOIN obj_device od ON od.id_device = d.id_device
JOIN def_device_hardware dh ON dh.id_device_hardware = od.id_device_hardware
JOIN def_device_os do ON do.id_device_os = od.id_device_os
JOIN obj_user_device_app_state das ON das.id_user_device = d.id_user_device
LEFT JOIN obj_device_setting ods ON ods.id_device = od.id_device
LEFT JOIN obj_user_setting ous ON ous.id_user = d.id_user
AND das.bundle_id = '$App'";
Write("Number of results from query:")
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $SQLServer; Database = $SQLDBName; Integrated Security = True; User ID = $uid; Password = $pwd;"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $SqlQuery
$SqlCmd.Connection = $SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$rowCount = $SqlAdapter.Fill($DataSet)
$DataSet.Tables[0]
foreach ($row in $Dataset.tables[0].rows)
{
Write ("entry $row.value")
}
You can see that the $rowcCount shows value of 2 but when I print it it shows 4 entries when it should be 2.
Number of results from query: 2
value
-----
**3a5b220714243811**
105986685869347611614
3a5b220714243811
**105986685869347611614**
The forloop also prints 4 entries
Write ("entry $row.value")
entry 3a5b220714243811
**entry 105986685869347611614**
**entry 3a5b220714243811**
entry 105986685869347611614
Same with out-file from $dataset. NOte: the headings for the SQL columns are not duplicated just the results
$DataSet.Tables[0] | out-file "File.txt";
Found the issue. these two are causing the dups removing one works fine
$SqlAdapter.Fill($DataSet)
$rowCount = $SqlAdapter.Fill($DataSet)
Within PowerShell, I'm trying to trigger an email to be sent only if there are any results from a SQL query. Email functionality and DB connection is working fine, but the "if then" statement based on SQL results isn't working - I don't think the $result = [bool] is valid.
End result would be the email only being sent if there are any records returned from the SQL statement, and the email would contain the SQL results.
Here's what I have so far:
$result = [bool]("
SELECT *
FROM table
WHERE condition")
If ($result -eq $true) {
function Invoke-SQL ($dataset) {
$connectionString = "server=servername;uid=valuehere;pwd=passwordhere;database=dbnamehere;"
$sqlCommand = "
SELECT *
FROM table
WHERE condition"
"
$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
$adapter.Fill($dataSet) #| Out-Null
$connection.Close()
}
function Invoke-Email ($dataset) {
foreach ($Row in $dataset.Tables[0].Rows)
{
write-host "value is : $($Row[0])"
}
$From = "email"
$To = "email"
$Subject = "subject here"
$Body = echo 'email body here' $dataset.tables[ 0 ] | Out-String
$SMTPServer = "server here"
Send-MailMessage -From $From -to $To -Subject $Subject -Body $Body -SmtpServer $SMTPServer
#-Port $Port
}
$dataset = New-Object System.Data.DataSet
Invoke-SQL $dataset
$dataset.Tables
Invoke-Email $dataset
$result
There are at least 2 things that you can do to accomplish this based on your current method of executing the query. For a simple SELECT query executed with the 'fill' method of the dataAdapter the return value should be the number of rows returned by the query. You could do this:
$resultCount = $adapter.fill($dataSet)
Then just check if $resultCount is greater than 0.
if($resultCount -gt 0){
#invoke your email function here
}
You could also just check the row count of your data table like this:
$dataSet.Tables[your table name or index].Rows.Count
Again simply checking for greater than 0.
if($dataSet.Tables[your table name or index].Rows.Count -gt 0){
#invoke your email function here
}
I recommend this second approach of counting the number of rows in the data table, because you do not have to worry about differences in the different data adapter objects that are available.
I have the following code which returns what I need but I am struggling to output this to a table from which I can further query.
$instances = invoke-sqlcmd –ServerInstance "myserver" –Database "my db" –query "select instanceconnectname from [dbo].[smytable] WHERE InstanceConnectName LIKE '%CLU%' and connect = 1"
Write-Host $instances.instanceconnectname
foreach ($svr in $instances.instanceconnectname){
$dt = new-object "System.Data.DataTable"
$cn = new-object System.Data.SqlClient.SqlConnection "server=$svr;database=master;Integrated Security=sspi"
$cn.Open()
$sql = $cn.CreateCommand()
$sql.CommandText = "SELECT ##SERVERNAME AS ServerName, SERVERPROPERTY('ComputerNamePhysicalNetBIOS') As ActiveNode"
$rdr = $sql.ExecuteReader()
$dt.Load($rdr)
$cn.Close()
$dt | Format-Table -autosize
}
I have been reading about some custom functions out there is that the only way to do this really? I had thought I could just do some kind of SQL Insert but not figured out how to do it.
Instead of outputting to a DataTable, I would output to a DataSet, which you can then further query. e.g.:
$cn = new-object System.Data.SqlClient.SqlConnection "server=$svr;database=master;Integrated Security=sspi"
#Create the SQL Command from a connection string
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = "SELECT ##SERVERNAME AS ServerName, SERVERPROPERTY('ComputerNamePhysicalNetBIOS') As ActiveNode"
$SqlCmd.Connection = $cn
#Create the SQL DataAdapter
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
#Fill the DataSet
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
Now with it in a DataSet, you can query it, like this:
$DataSet.tables[0].select("ServerName like 'Bob%'")
Hopefully that is enough to get you started...
Can someone tell me why the following code only returns one row from the DataSet (for the master db) instead of one for each database on the server?
$SQLConn = New-Object System.data.SqlClient.SqlConnection
$SQLConn.ConnectionString = "Data Source = $SQLServer; Initial Catalog = master;
Integrated Security = True"
$SQLConn.Open()
$query = "exec sp_msForEachDb 'use [?] SELECT TOP 1 [name] FROM sysusers'"
$SQLCmd = New-Object System.Data.Sqlclient.SqlCommand($query, $SQLConn);
$SQLAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SQLAdapter.SelectCommand = $SQLCmd
$DataSet = New-Object System.Data.Dataset
$SQLAdapter.Fill($DataSet) | out-null
ForEach ($row in $DataSet.Tables[0])
{
$Name = $row["name"]
write-host $Name
}
$SQLConn.Close()
Your code shows your using the index position 0 of $dataset.tables. Change it to $DataSet.Tables
ForEach ($row in $DataSet.Tables[0])
{
$Name = $row["name"]
write-host $Name
}
Change to...
ForEach ($row in $DataSet.Tables)
{
$Name = $row["name"]
write-host $Name
}
You're returning getting multiple datatables in your result set, but only showing the first one Tables[0]. Try this:
$DataSet.Tables | foreach {$_.name}
Here's some completed and tested code to combine result set:
$SQLServer = ".\SQL1"
$query = #"
create table #output
(DB varchar(128),
name varchar(128)
)
exec sp_MSforeachdb #command1='USE [?];
insert #output SELECT TOP 1 ''?'' AS ''DB'', [name]
FROM sysusers';
select * from #output
"#
$SQLConn = New-Object System.data.SqlClient.SqlConnection
$SQLConn.ConnectionString = "Data Source = $SQLServer; Initial Catalog = master;
Integrated Security = True"
$SQLConn.Open()
$SQLCmd = New-Object System.Data.Sqlclient.SqlCommand($query, $SQLConn);
$SQLAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SQLAdapter.SelectCommand = $SQLCmd
$DataSet = New-Object System.Data.Dataset
$SQLAdapter.Fill($DataSet) | out-null
$DataSet.Tables[0]
$SQLConn.Close()
I think I needed to do something like this to get all of the records into one dataset:
$query = "DECLARE #users TABLE ([name] varchar(100)) INSERT #db_roles
exec sp_msForEachDB 'USE [?] SELECT [name] FROM sysusers'
SELECT [name] FROM #db_roles"