Export SQL data to Excel - Best possible approach? - sql

I am currently working on an automation which requires to export sql results to excel data. I want to do this via SQL query. Few options i know are as below, but before i start exploring these things. I would like to know the best possible approach .
PS - It would be really great if there is a way to dynamically create excel during query execution and export data in multiple excel sheets.
OPENROWSET
bcp_cmd

You can CREATE VIEW and utilize that view in Excel 2016 under its data connections through PowerQuery. Views are preferred since they are managed independently in the server, and provide realtime data results without requiring a full query to be embedded in the Excel file. The resulting set exists in the workbook as a refresh-able table. Results that need to be recorded should be done via new workbooks or UPDATE's back to the server in a separate script.
In the PowerQuery Editor, Home tab, click Advanced Editor. The database connection string and call to the server is below. You can also dynamically pass parameters from an Excel table to the query utilizing a table in the Name Manager.
Excel tab, table name: tbl_Parameters
A B
1 StartDate 01/01/2020
2 EndDate 02/01/2020
let
Source = Sql.Database("ServerName" , "Database", [Query="
DECLARE #Start_Date AS datetime
DECLARE #End_Date AS datetime
SET #Start_Date = '"&StartDate&"'
SET #End_Date = '"&EndDate&"'
SELECT * FROM uvw_product
WHERE item_sold_date BETWEEN
#Start_Date AND #End_Date
"])
in
Source

A while back I cobbled this Powershell script together.
It querys sql server data, saves as csv, formats it and saves as xls, then mails via smtp.
You can set up a windows scheduled task to automate it.
There's also an import xls module for powershell.
Import-Module Sqlps -DisableNameChecking;
#execute mysql query as excel
$Server = "DB SERVER";
$Database = "DBNAME";
$Query = #"
*SELECT QUERY HERE*
"#
$a = Get-Date
#note: if you run get-location from ide, it will use the ide path instead of the script path
$currentLocation = Split-Path -Parent $PSCommandPath
$FilePath = $currentLocation + "\CSVName.csv"
$SavePath = $currentLocation + "\XLSFileName" +$a.Day+ $a.Month + $a.Year + ".xls"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $Server; Database = $Database; User ID = DBUSERNAME; Password = PASSWORD";
$SqlConnection.Open()
$sqlcmd = $SqlConnection.CreateCommand()
$sqlcmd.Connection = $SqlConnection
$sqlcmd.CommandText = $Query
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $sqlcmd
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$DataSet.Tables[0] | Export-Csv -notypeinformation $FilePath
$SqlConnection.Close()
#Invoke-Sqlcmd -Query $Query -ConnectionString $SqlConnection.ConnectionString | Export-Csv -notypeinformation $FilePath
#release memory function
function Release-Ref($ref){
([System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$ref) -gt 0)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
#format excel to display correct date
$objExcel = new-object -comobject excel.application
$objWorkbook = $objExcel.Workbooks.open($FilePath)
$objWorksheet = $objWorkbook.Worksheets.Item(1)
$objRange = $objWorksheet.UsedRange
[void] $objRange.EntireColumn.Autofit()
$objWorkbook.Saved = $True
$objExcel.DisplayAlerts = $False
$objWorkbook.SaveAs($SavePath,1)
$objExcel.Quit()
#release memory
Release-Ref($objWorksheet)
Release-Ref($objWorkbook)
Release-Ref($objExcel)
#create mail
$smtpServer = "SMTPSERVER"
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$att = new-object Net.Mail.Attachment($SavePath)
$msg = new-object Net.Mail.MailMessage
$msg.Subject = "EMAIL SUBJECT"
$msg.From = "FROM EMAIL"
#$msg.To.Add("TO EMAIL 1")
$msg.To.Add("TO EMAIL 2")
$msg.Body = #"
Hi,
MSG BODY HERE
Best Regards
"#
$msg.Attachments.Add($att)
$smtp.Send($msg)
$att.Dispose()

Download and install the 64-bit or 32-bit version of the driver, based on what you have installed.
https://www.microsoft.com/en-us/download/details.aspx?id=13255
Then, you should be able to run this.
insert into OPENROWSET('Microsoft.Jet.OLEDB.4.0',
'Excel 8.0;Database=D:\testing.xls;',
'SELECT * FROM [SheetName$]') select * from SQLServerTable
Notice: this may not work if you have one 32-bit technology and one 64-bit technology. In this case, you may need to employ some kind of workaround.
See this link for additional ideas of how to integrate SQL Server and Excel.
https://solutioncenter.apexsql.com/how-to-import-and-export-sql-server-data-to-an-excel-file/

Related

Import macros programmatically in Word document

I am trying to automate the process of importing macros in Word documents, but I fail to load macros in any macro-enabled word document. It works just for the Normal.dotm
I want to create a new doc, and load the macro from a .bas file.
$basPath = "C:\Files\macro.bas"
$docPath = "C:\Files\macro_document.doc"
$word = New-Object -ComObject "Word.Application"
$document = $word.documents.Add()
$document.SaveAs($docPath)
$document.VBProject.VBComponents.Import($basPath)
$document.Save()
$document.Close()
$word.Quit()
What am I doing wrong?
Use .SaveAs($docPath,0) where 0 is wdFormatDocument 97-2003
$basPath = "C:\Files\macro.bas"
$docPath = "C:\Files\macro_document.doc"
$word = New-Object -ComObject "Word.Application"
$document = $word.Documents.Add()
$document.VBProject.VBComponents.Import($basPath)
$document.SaveAs($docPath,0) # 0 = wdFormatDocument 97-2003
$document.Close()
$word.Quit()
Or use extension .docm and .SaveAs($docPath,13) where 13 is wdFormatXMLDocumentMacroEnabled:
$basPath = "C:\Files\macro.bas"
$docPath = "C:\Files\macro_document.docm"
$word = New-Object -ComObject "Word.Application"
$document = $word.Documents.Add()
$document.VBProject.VBComponents.Import($basPath)
$document.SaveAs($docPath,13) # 13 = wdFormatXMLDocumentMacroEnabled
$document.Close()
$word.Quit()

create a new sheet using showdetail on pivottabes field in Powershell + VBA

Double Clicking on Pivot tables GrandTotal data creates a separate sheet with entire data source. Would like to get this done through powershell. Below is the code I tried with Powershell 5.1.
$excelFile = "C:\test\Testfile.xlsb"
$Excel = New-Object -ComObject Excel.Application
$wb = $Excel.Workbooks.Open($excelFile)
$s=$wb.worksheets(1).range("C7").select
$s.showdetail
$wb.saveas("C:\test\Testfile_modified.xlsb")
$wb.close()
$Excel.quit()
More direct - no need for select or intermediate variable $s:
$excelFile = "C:\tester\Testfile.xlsb"
$Excel = New-Object -ComObject Excel.Application
$wb = $Excel.Workbooks.Open($excelFile)
$wb.worksheets(1).range("C7").showdetail = $true
$wb.saveas("C:\tester\Testfile_modified.xlsb")
$wb.Close()
$Excel.Quit()
#make sure Excel process is ended (or it may persist)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel)
Remove-Variable Excel
in a script file called from cmd using:
powershell -noexit "& ""C:\Tester\psExcelTest.ps1"""

Powershell script works but not in vb.net code

I need to run a powershell script from vb.net. I wrote it and tried it in powershell ISE and the powershell console and it's working properly.
But when I try to use it in my vb.net code, nothing happens.
The script put into text file citrix session from some user.
here is my powershell script :
$username = 'domain\myusername'
$password = 'mypassword'
$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential $username, $securePassword
Start-Process C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -Credential $credential -NoNewWindow -ArgumentList "add-pssnapin citrix*`nGet-BrokerSession -AdminAddress 'ipadresshere' | Out-File -FilePath C:\inetpub\wwwroot\gestusers\ajax\data\arrays.txt"
and here's my vb.net code :
Dim MyRunSpace As Runspace = RunspaceFactory.CreateRunspace()
' open it
MyRunSpace.Open()
' create a pipeline and feed it the script text
Dim MyPipeline As Pipeline = MyRunSpace.CreatePipeline()
MyPipeline.Commands.AddScript(scriptText)
' add an extra command to transform the script output objects into nicely formatted strings
' remove this line to get the actual objects that the script returns. For example, the script
' "Get-Process" returns a collection of System.Diagnostics.Process instances.
MyPipeline.Commands.Add("Out-String")
' execute the script
Dim results As Collection(Of PSObject) = MyPipeline.Invoke()
' close the runspace
MyRunSpace.Close()
' convert the script result into a single string
Dim MyStringBuilder As New StringBuilder()
For Each obj As PSObject In results
MyStringBuilder.AppendLine(obj.ToString())
Next
' return the results of the script that has
' now been converted to text
Return MyStringBuilder.ToString()
When I try to use a smaller script like :
$test = "blablabla"
$test | Out-File -filepath "C:\inetpub\wwwroot\gestusers\ajax\data\arrays.txt"
it works so I don't really understand why the first script is not working .. if you have some suggestion I would be thankful
I finaly found a solution to my problem.
I am running my web app on a IIS server and when I was launching my script it was the default user "IIS default/application_pool" which was lauching it although I specified a specific user to run it, it was like my script doesn't work.
So I choosed to go in my IIS server params and I just made an other app_pool with my specific user. I put my app under this pool and now it works perfectly.
Here's how to make a specific pool on IIS Server : https://www.developer.com/net/asp/article.php/2245511/IIS-and-ASPNET-The-Application-Pool.htm
cya

"Permission Denied" error while trying to run Powershell script from Word Macros

I need to run a powershell script with a parameter from a word macros. For now even the attempts to simply run a script from VBA are not sucessful. I do the following:
Sub Powershell_Run()
sCmd = "powershell -file ""C:\Users\i351063\Desktop\Scripts\NewAttempt.ps1"""
Set xShell = CreateObject("Wscript.Shell")
Set xShellExec = xShell.Run(sCmd)
rReturn = xShellExec.Run("C:\Users\i351063\Desktop\Scripts\NewAttempt.ps1")
End Sub
The execution of this code returns an error: "Run-time error '70': Permission denied" on the line Set xShellExec = xShell.Run(sCmd). What do I do wrong? How to fix the error?
Thanks a lot in advance!
UPD: Powershell code (I want to pass the filename as a parameter from VBA to PS, for now it's initialized right in the code)
Param([string]$filename)
$filename = "HT.docm"
Add-Type -AssemblyName "Microsoft.Office.Interop.Word"
$word = [Runtime.Interopservices.Marshal]::GetActiveObject('Word.Application')
$wshell = New-Object -ComObject Wscript.Shell
$doc = $word.Documents | Where-Object {$_.Name -eq "$filename"}
If (!$doc)
{
$form.Close()
[void] $wshell.Popup("Failed to find an opened report",0,"Report not found",0x1)
$word.ScreenUpdating = $true
Break
}
Else {...}

Set Environment per Runspace

I am currently working on an application that pulls tasks from a SQL table and exeutes PowerShell scripts. I want to run a SetUP.ps1 in the beginning to set variables like working dir and so on, and a tearDown.ps1 in the end just for clean up.
Currently I am using:
Using myRunSpace = RunspaceFactory.CreateRunspace
myRunSpace.Open()
Using ps = PowerShell.Create
ps.Runspace = myRunSpace
ps.AddScript("Set-ExecutionPolicy -Scope Process RemoteSigned")
ps.Invoke()
End Using
Using ps = PowerShell.Create
ps.Runspace = myRunSpace
ps.AddScript(Application.StartupPath & "\SetUp.ps1")
ps.Invoke()
End Using
Using ps = PowerShell.Create
ps.Runspace = myRunSpace
ps.AddScript(task.Script)
ReturnValue = PSSerializer.Serialize(ps.Invoke())
End Using
Using ps = PowerShell.Create
ps.Runspace = myRunSpace
ps.AddScript(Application.StartupPath & "\TearDown.ps1")
ps.Invoke()
End Using
End Using
In Setup.ps1 I a currently doing:
$env:test = Get-Random
When I now run the code above multithreaded with the snippet as code:
Start-Sleep 10; $env:test
Then all runs give me the same value. the $env:test is the same in each run. Is there a way how I can limit the scope of the $env:test to simply this one runspace?
I think this is what you want to accomplish: Have one thread write the random value to an environment variable, and the other thread reading it?
Remove-Item c:\test.out -force
Remove-Item Env:\test
$testfile = "c:\test.out"
$myRunSpace = [RunspaceFactory]::CreateRunspace()
$myRunSpace.Open()
$ps1 = [PowerShell]::Create()
$ps1.Runspace = $myRunSpace
$ps1.AddScript("`$env:test = Get-Random")
$ps1.Invoke()
$ps2 = [PowerShell]::Create()
$ps2.Runspace = $myRunSpace
$ps2.AddScript("Start-Sleep -Seconds 1")
$ps2.AddScript("`$env:test | Out-File $testfile")
$ps2.Invoke()
start-sleep 2
"variable set to: $env:test"
"file reads: " + (Get-Content $testfile)