Powershell SQL Server query is creating duplicate entries - powershell

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)

Related

Unable to execute a SQL Query which starts with "WITH" instead of "SELECT"

When I am trying to execute the SQL Query starts with "WITH" I am getting the following error
Query:
WITH BMC_Dependency_IP_Endpoint AS (
SELECT ER.SOURCE_RECONCILIATIONIDENTITY, ER.CLASSID
,re.DNSHOSTNAME,re.NETWORKLOCATION,re.IPSUBNET,re.NETWORKZONE,re.NETWORKENVIRONMENT,re.SUBNETMASK,re.NAME
FROM BMC_CORE_BMC_DEPENDENCY er
INNER JOIN (SELECT * FROM BMC_CORE_BMC_IPENDPOINT WHERE DATASETID = 'BMC.ASSET' AND COALESCE(MARKASDELETED,0) <> 1) re
ON (er.DESTINATION_RECONCILIATIONIDEN = RE.RECONCILIATIONIDENTITY)
WHERE er.DESTINATION_CLASSID = 'BMC_IPENDPOINT' AND er.SOURCE_CLASSID = 'BMC_COMPUTERSYSTEM' AND er.DATASETID = 'BMC.ASSET' AND COALESCE(er.MARKASDELETED,0) <> 1 AND er.classid = 'BMC.CORE:BMC_HOSTEDACCESSPOINT'
),
APP_PORTFOLIO_APPID AS
(
select DISTINCT APPID, APPLICATION_OWNER_NAME, APP_SUP_CONTACT_NAME, AppSupDelegateName, PRODUCT_CATEGORIZATION_TIER_1, PRODUCT_CATEGORIZATION_TIER_2, PRODUCT_CATEGORIZATION_TIER_3
, IT_SERVICE
FROM PCT_PRODUCT_CATALOG
WHERE APPID IS NOT NULL AND STATUS = 1
)
SELECT c.HOSTNAME, c.SERIALNUMBER, dbo.FN_ADJUSTED_DATE(c.LastScanDate) as LASTSCANDATE,c.Environment, c.AWS_State, c.CME_Live_State, c.PrimaryIPAddress, c.Location, c.PRIMARYAPPID, c.Category, c.RFB, c.OSName, c.OSType, OSversion, OSPatchLevel, c.PatchSlot, c.Nextgen, c.CME_Managed, c.CME_Chef
,COALESCE(ip.name,c.PrimaryIPAddress) AS IP
,ap.PRODUCT_CATEGORIZATION_TIER_3 as Application
FROM BMC_CORE_BMC_ComputerSystem c
LEFT OUTER JOIN BMC_Dependency_IP_Endpoint ip
ON ip.SOURCE_RECONCILIATIONIDENTITY = c.ReconciliationIdentity
LEFT OUTER JOIN APP_PORTFOLIO_APPID ap
ON ap.APPID = c.PRIMARYAPPID
WHERE c.DATASETID = 'BMC.ASSET' AND COALESCE(c.MARKASDELETED,0) <> 1
Error:
Exception calling "Fill" with "1" argument(s): "Execution Timeout
Expired. The timeout period elapsed prior to completion of the
operation or the server is not responding."
Tried to execute individual blocks in the query which works ok
$SQLServer = "XXXX,YYY"
$SQLDBName = "AAAAA"
$uid ="readonly"
$pwd = "read"
$SQLquery = [System.IO.File]::ReadAllText("C:\PC\sql.txt")
write-host "SQL Query : $SqlQuery"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $SQLServer; Database = $SQLDBName;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)
$DataSet.Tables[0] | Export-Csv "C:\PC\new.csv"
Expected Results:
A csv file with all the values
Actual results:
Exception calling "Fill" with "1" argument(s): "Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding."
(Assuming that the query will execute normally), This usually is the case that the query takes longer than 30 seconds to execute. You can set the CommandTimeout to longer than the default 30 seconds:
...
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $SqlQuery
$SqlCmd.Connection = $SqlConnection
$SqlCmd.CommandTimeout = 1000
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
...

Powershell SQL Query to variables

Trying to use SQL results as variables. This will return the results to the host, but I would like to use each value as a variable.
$job = "22940"
$SQLServer = "my-sql" #use Server\Instance for named SQL instances!
$SQLDBName = "MyDatabase"
$SqlQuery = "DECLARE #Job VARCHAR(5);
SET #Job = '$job'
SELECT dbo.Job.Job, dbo.Job.Customer,
dbo.Job.Note_Text,
dbo.User_Values.Date1 AS 'Setup Date',
dbo.User_Values.Date2 AS 'Due Date'
FROM dbo.Job INNER JOIN
dbo.User_Values ON dbo.Job.User_Values = dbo.User_Values.User_Values
WHERE (dbo.Job.Job = #Job) AND (dbo.Job.User_Values = dbo.User_Values.User_Values)"
$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()
clear
$DataSet.Tables[0]
How can I put each column into a variable that I can use for something else?
I work with data tables directly. You should also look into handling parameters correctly. It helps with handling special characters in the variable value. And, it protects from SQL Injection Attacks:
$Sql = 'select top 100 first_name, Last_name from person where last_name=#lastname'
$Database = 'XXX'
$Server = 'XXX'
$LastName = 'Jones'
$Connection = New-Object System.Data.SqlClient.SqlConnection
$Connection.ConnectionString = "Server=$Server;Database=$Database;Integrated Security=True"
$Connection.Open()
$cmd = new-object system.Data.SqlClient.SqlCommand($Sql, $Connection)
$null = $cmd.Parameters.AddWithValue('#lastname', $LastName)
$DataTable = New-Object System.Data.DataTable
$SqlDataReader = $cmd.ExecuteReader()
$DataTable.Load($SqlDataReader)
$Connection.Close()
$Connection = $null
$DataTable.Rows.Count
foreach ($r in $DataTable.Rows)
{
$fn = $r.first_name
$ln = $r.last_name
"$fn $ln"
}
Not sure if this is really the correct way but it does give me the results I am looking for. Looked through some of my notes on working with CSV files and I added the foreach at the end. The above comments from #notjustme and #jcoehoorn got me looking in the right direction. Thanks
$job = "22940"
$SQLServer = "my-sql" #use Server\Instance for named SQL instances!
$SQLDBName = "MyDatabase"
$SqlQuery = "DECLARE #Job VARCHAR(5);
SET #Job = '$job'
SELECT dbo.Job.Job, dbo.Job.Customer,
dbo.Job.Note_Text,
dbo.User_Values.Date1 AS 'Setup Date',
dbo.User_Values.Date2 AS 'Due Date'
FROM dbo.Job INNER JOIN
dbo.User_Values ON dbo.Job.User_Values = dbo.User_Values.User_Values
WHERE (dbo.Job.Job = #Job) AND (dbo.Job.User_Values = dbo.User_Values.User_Values)"
$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()
clear
foreach($Job in $DataSet.Tables[0])
{
$MyJobNumber = $Job.Job
$MyCustomer = $Job.Customer
$MyNotes = $Job.Note_Text
$MySetupDate = $Job.Setup_Date
$MyDueDate = $Job.Due_Date
}

Working with System.ComponentModel.MarshalByValueComponent

I'm pulling data from SQL using System.Data.SqlClient and eventaully end up with a dataset stored into a variable:
$SqlQuery = "select * from my_Table"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $SQLServer; Database = $SQLDBName; Integrated Security = True"
$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
$amManagedFolders = New-Object System.Data.DataSet
$SqlAdapter.Fill($dataset)
I then access the table via $dataset.Tables[0] which is of type: System.ComponentModel.MarshalByValueComponent
I need to be able to use ForEach in order to loop through that table and store the results into a CustomPSObject for later use, however, I am not sure of a method to use in order to achieve this.
Using this page I found the following code helpful. I'm taking a SQL query and producing an Excel workbook with the results.
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$SqlConnection.Close()
$DataSetTable = $DataSet.Tables["Table"]
## - Build the Excel column heading:
[Array] $getColumnNames = $DataSetTable.Columns | Select ColumnName
## - Build column header:
[Int] $RowHeader = 1
foreach ($ColH in $getColumnNames)
....
foreach ($rec in $DataSetTable.Rows)
{
foreach ($Coln in $getColumnNames)
{
## - Next line convert cell to be text only:
$xlsSh.Cells.NumberFormat = "#"
## - Populating columns:
$xlsSh.Cells.Item($rowData, $colData) = $rec.$($Coln.ColumnName).ToString()
$ColData++
}
$rowData++; $ColData = 1
}
-Edit, saw your comment. You might be able to adapt the answer to this question

Output results to a SQL table

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...

sp_msforeachdb in dataset

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"