I have, what I think, is a real head-scratcher.
I am accessing a database to get a list of accounts. Each account has an account_id and account_parent_id property (among several other properties). If the account is the child of another account, the account_parent_id has the account ID of the parent and if the account is a parent (or has no children), the account_parent_id is blank. There are only two levels, so if an account has one or more children, it will not have a parent.
I need the output to be the account number (if the account has no children) and the account number of the parent and all children (comma separated) if there are children. Here is the code I have:
$SQLServer = "<database fqdn>"
$SQLDBName = "<databaes name>"
$uid ="<username>"
$pwd = "<password>"
$SqlQuery = "SELECT * from <account table>"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $SQLServer; Database = $SQLDBName; Persist Security Info = True; User ID = $uid; Password = $pwd;"
$SqlConnection.Open()
$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)
$data = $DataSet[0].Tables
$SqlConnection.Close()
Foreach ($row in $data) {
Foreach ($account in $row) {
If ($account.parent_account_id -eq $row.account_id) {
$accts += $account.account_id
}
ElseIf ($account.parent_account_id -eq $account.account_id) {
$accts = $account.account_id
}
Return $accts
}
}
The problem is that I'm not getting anything at all into $accts. What am I missing here?
Thanks.
The best way to achieve your desired result is to do the mapping in SQL:
$SqlQuery = #'
SELECT t1.account_id AS parent, t2.account_id AS child
FROM <account table> t1 LEFT OUTER JOIN <account table> t2
ON t1.account_id = t2.parent_account_id
'#
Then you can extract the information you want via Group-Object and calculated properties:
$data | Group-Object parent |
Select-Object #{n='Parent';e={$_.Name}},
#{n='Children';e={$_.Group.child -join ','}}
If you're stuck with PowerShell v2 or earlier you need to replace $_.Group.child -join ',' with something like ($_.Group | Select-Object -Expand child) -join ','.
So, we did end up updating the SQL query. The data type I was getting back was a DataTable. I took out a row and used a loop like the following, to get he data I needed:
foreach ($item in $tableRow) {
$($item.account_ids)
}
Related
I'm getting some data from a SQL table, which I then store in a System.Data.DataSet object. I want to pass this data in this DataSet, as an Argument/Parameter, to a workflow, such that I can display all the data in this DataSet in a foreach -parallel style. But I'm at a loss for the correct syntax of passing data from a System.Data.DataSet object to a workflow. Currently I get an Error near the line "param([System.Data.DataSet]$pServiceDataSet)" as shown below.
Function GetSQLData
{
param ($TargetDBServer, $TargetDB, $SQLQuery)
# SQL Connection Object
$sqlConn = New-Object System.Data.SqlClient.SqlConnection
$sqlConn.ConnectionString = "Server=$TargetDBServer;Database=$TargetDB;User Id=SomeUser;Password=SomePassword;"
$sqlConn.Open()
# SQL Command
$sqlcmd = New-Object System.Data.SqlClient.SqlCommand
$sqlcmd.Connection = $sqlConn
$sqlcmd.CommandText = $SQLQuery
# SQL Adapter
$sqlAdp = New-Object System.Data.SqlClient.SqlDataAdapter ($sqlcmd)
# SQL DataSet
$ResultDataSet = New-Object System.Data.DataSet
$sqlAdp.Fill($ResultDataSet) | Out-Null
$sqlConn.Close()
return $ResultDataSet.Tables[0]
}
$CurrentComputerName = $env:COMPUTERNAME
# Export the Windows Services & it's config parameters from the "DatabaseABC..WindowsServicesConfig" Table.
$SQLQueryForService = "
SELECT [ServiceName], [StartUpParameter], [DBServerName], [DBName]
FROM [dbo].[WindowsServicesConfig] WITH (NOLOCK)
WHERE [HostServerName] = '$CurrentComputerName'
AND [ServiceName] LIKE '%MyService%' "
$ServicesDataSet = GetSQLData -TargetDBServer "ServerABC" -TargetDB "DatabaseABC" -SQLQuery $SQLQueryForService
$ServicesDataSet.GetType()
$ServicesDataSet | Format-Table
workflow DisplayAllServices
{
param([System.Data.DataSet]$pServiceDataSet) # <- I get an Error here
foreach -parallel ($Service in $pServiceDataSet)
{
$Service.ServiceName
$Service.StartUpParameter
$Service.DBServerName
$Service.DBName
}
}
DisplayAllServices -pServiceDataSet $ServicesDataSet
My final objective is to use the data in this DataSet to create Windows Services. But this is my most frustrating hurdle. I cannot get past the Error.
Figured out the Solution to my Problem. Replaced the "param([System.Data.DataSet]$pServiceDataSet)" with "param([PSObject]$pServiceDataSet)".
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)
I have created a test database in SQL Server 2016 Express, it holds 1 table labeled drivers.
I use PowerShell to perform ciminstance query of installed drivers, then insert those values into the test database driver table. (the insert works as expected)
The issue I have is attempting to update the driver table, only the last object is inserted into the database 40 times(that is how many drivers are returned from the ciminstance query). I have created 2 PowerShell scripts
Insert values
Update values
Stumped!
$database = 'test'
$server = 'groga\sqlExpress'
$table = 'dbo.Driver'
$SQLServer = "groga\sqlExpress"
$SQLDBName = "test"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $SQLServer; Database =
$SQLDBName; Integrated Security = True"
$SqlConnection.Open()
$today = Get-Date
$drivers = gcim win32_pnpsigneddriver -Property *
$model = gcim win32_computersystem -Property *
foreach($driver in $drivers)
{
if(!($driver.Description -match "Generic") -and $driver.Manufacturer -
notmatch 'Microsoft|Standard|Generic' -and $driver.DriverDate -ne $null)
{
$count = New-Object psobject -Property #{
'Date' = $driver.DriverDate
'Manufacturer' = $driver.Manufacturer
'Version' = $driver.DriverVersion
'PackageID' = "0"
'SKU' = $model.SystemSKUNumber
'Model' = $model.Model
'Today' = $today}
$col1 = $count.Date
$col2 = $count.Manufacturer
$col3 = $count.Version
$col4 = $count.PackageID
$col5 = $count.SKU
$col6 = $count.Model
$col7 = $count.Today
$update = #"
UPDATE $table
SET [Date]='$col1',
[Manufacturer]='$col2',
[Version]='$col3',
[PackageID]='$col4',
[SKU]='$col5',
[Model]='$col6',
[Today]='$col7'
"#
$dbwrite = $SqlConnection.CreateCommand()
$dbwrite.CommandText = $update
$dbwrite.ExecuteNonQuery()
}
}
$Sqlconnection.Close()
The UPDATE statement will apply to all rows that are matched by the query. So what your script is doing is setting ALL rows in the table to info for a driver then doing the same for the whole list.
You will need to determine the fields which uniquely identify each driver and then filter your query down to that. Looking at sample driver info, this could be Date, Manufacturer, Device Name (something you would need to add to your schema), DriverVersion.
Example with just Date, Manufacturer, DriverVersion:
$update = #"
UPDATE $table
SET [PackageID] = '$col4'
[SKU]='$col5',
[Model]='$col6',
[Today]='$col7'
WHERE [Date] = '$col1' AND [Manufacturer]='$col2' AND [Version]='$col3'
"#
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"