I was wondering if anyone could help with the following code shown. I'm basically trying to get this code re-hashed if possible to allow me to run it against a set of server names supplied in a text file named "servers.txt".
The DBCC should be run by the PS script and run against all DB for that servername. I'm not up to speed enough with PS to understand how to do this for this script.
How to change it allow to plug in values instead of being hardcoded for each servername?
I've read a bit around this and looked at the Invoke-Sql command which I believe is a SQL 2008 extension to PS.
Unfortunately the PS environment is from a SQL 2005 box and I dont have the power to get this moved so dont think ill be able to use invoke
Please see the original code and then my experiment to try and get it to run using invoke.
$ScriptName = $myInvocation.MyCommand.Name
[void][reflection.assembly]::LoadWithPartialName("System.Data.SqlClient")
$ConnString = "Server=DB-OCC05;Integrated Security=SSPI;Database=master;Application Name=$ScriptName"
$MasterConn = new-object ('System.Data.SqlClient.SqlConnection') $ConnString
$MasterCmd = new-object System.Data.SqlClient.SqlCommand
$MasterCmd.Connection = $MasterConn
$SqlDBCC = "DBCC CHECKDB(master) WITH TABLERESULTS"
$MasterCmd.CommandText = $SqlDBCC
$MasterConn.Open()
$Rset = $MasterCmd.ExecuteReader()
If ($Rset.HasRows -eq $true) {
While ($Rset.Read()) {
$line = $Rset["MessageText"]
If ($Rset["Level"] -gt 10) {
Write-Host $line -backgroundcolor Yellow -foregroundcolor Red
} else {
Write-Host $line
}
}
$Rset.Close()
}
$MasterConn.Close()
And then my test running from SQL 2005 environment:
Invoke-Sqlcmd -Query "SELECT GETDATE() AS TimeOfQuery;" -ServerInstance "MyComputer\MyInstance"
And also tried this test:
gc "C:\Powershell\Servers.txt" | foreach-object {Invoke-Sqlcmd "DBCC checkdb;" -ServerInstance "$_\MyInstance"}
But the above test runs didnt work cause of the:
The term 'Invoke-Sqlcmd' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path.
A few modifications to your script. Everything is basically the same except for the connection string and the few lines at the bottom for loading your servers.txt file (a text file with one line per instance) and enumerating its content:
function Execute-DBCC
{
param (
[parameter(Mandatory = $true)][string]$serverInstance
)
$connString = "Server=$serverInstance;Integrated Security=SSPI;Database=master;Application Name=$ScriptName"
$masterConn = new-object ('System.Data.SqlClient.SqlConnection') $connString
$masterCmd = new-object System.Data.SqlClient.SqlCommand
$masterCmd.Connection = $masterConn
$masterCmd.CommandText = "EXECUTE master.sys.sp_MSforeachdb 'DBCC CHECKDB([?]) WITH TABLERESULTS'"
$masterConn.Open()
$reader = $masterCmd.ExecuteReader()
if ($reader.HasRows -eq $true)
{
while ($reader.Read())
{
$messageText = $reader["MessageText"]
if ($reader["Level"] -gt 10)
{ Write-Host $messageText -backgroundcolor Yellow -foregroundcolor Red }
else
{ Write-Host $messageText }
}
$reader.Close()
}
$masterConn.Close()
}
[void][reflection.assembly]::LoadWithPartialName("System.Data.SqlClient")
$servers = #(Get-Content ".\servers.txt")
$servers | %{
Execute-DBCC $_
}
Related
At the moment I have the following code which grabs the return table and outputs it into a CSV file.
Push-Location; Import-Module SQLPS -DisableNameChecking; Pop-Location
$SQLServer = "localhost"
$today = (get-date).ToString("dd-MM-yyyy")
$DBName = "ZoomBI"
$ExportFile = "\\Shared_Documents\FC Folder\Despatch\Brexit Files\DHL\DHL "+$today+".csv"
$Counter = 0
$Storedprocedure = "EXEC [dbo].[DHLDeliveries]"
while ( $true )
{
# Remove the export file
if (Test-Path -Path $ExportFile -PathType Leaf) {
Remove-Item $ExportFile -Force
}
# Clear the buffer cache to make sure each test is done the same
$ClearCacheSQL = "DBCC DROPCLEANBUFFERS"
Invoke-Sqlcmd -ServerInstance $SQLServer -Query $ClearCacheSQL
# Export the table through the pipeline and capture the run time. Only the export is included in the run time.
$sw = [Diagnostics.Stopwatch]::StartNew()
Invoke-Sqlcmd -ServerInstance $SQLServer -Database $DBName -Query $Storedprocedure | Export-CSV -Path $ExportFile -NoTypeInformation
$sw.Stop()
$sw.Elapsed
$Milliseconds = $sw.ElapsedMilliseconds
$Counter++
Exit
}
However, instead of that I need to be able to output the results to an Excel document with two sheets
and put the results into each sheet.
# Create a Excel Workspace
$excel = New-Object -ComObject Excel.Application
# make excel visible
$excel.visible = $true
# add a new blank worksheet
$workbook = $excel.Workbooks.add()
# Adding Sheets
foreach($input in (gc c:\temp\input.txt)){
$s4 = $workbook.Sheets.add()
$s4.name = $input
}
# The default workbook has three sheets, remove them
($s1 = $workbook.sheets | where {$_.name -eq "Sheet1"}).delete()
#Saving File
"`n"
write-Host -for Yellow "Saving file in $env:userprofile\desktop"
$workbook.SaveAs("$env:userprofile\desktop\ExcelSheet_$Today.xlsx")
Can anyone help ?
I would take a look at the ImportExcel module. It took me 2 lines of code to create an excel document with two sheets.
https://www.powershellgallery.com/packages/ImportExcel/5.4.2
https://www.youtube.com/watch?v=fvKKdIzJCws&list=PL5uoqS92stXioZw-u-ze_NtvSo0k0K0kq
I have put together a nice PowerShell script to script out the objects (tables, functions, sprocs etc) from a database, limiting it to the ones in a list.
But I am stuck trying to find a way to script the database itself. Each time I do that, it seems to try to script out the whole database (it is way to large for that to go well).
Assuming I have a $db variable that is a reference to my database, how can I use SMO to script out that database, creating it with the same Properties and DatabaseScopedConfigurations, but none of the actual objects in it?
Update:
For reference here is my current script. It takes a server and database name and will script out all the objects found in a file called DbObjectsList.txt (assuming they are in the database). But this does not actually make the database. The database I am running this on is a legacy one, and it has a bunch of odd options set. I would like to preserve those.
$serverName = "MyServerName"
$databaseName = "MyDbName"
$date_ = (date -f yyyyMMdd)
$path = ".\"+"$date_"
# Load the Sql Server Management Objects (SMO) and output to null so we don't show the dll details.
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') > $null
# Setup the scripting options
$scriptOptions = new-object ('Microsoft.SqlServer.Management.Smo.ScriptingOptions')
$scriptOptions.ExtendedProperties = $true
$scriptOptions.AnsiPadding = $true
$scriptOptions.ClusteredIndexes = $true
# Dri = Declarative Referential Integrity
$scriptOptions.DriAll = $true
$scriptOptions.Triggers = $true
$scriptOptions.NoCollation = $false
$scriptOptions.SchemaQualify = $true
$scriptOptions.ScriptSchema = $true
$scriptOptions.EnforceScriptingOptions = $true
$scriptOptions.SchemaQualifyForeignKeysReferences = $true
$scriptOptions.NonClusteredIndexes = $true
$scriptOptions.Statistics = $true
$scriptOptions.Permissions = $true
$scriptOptions.OptimizerData = $true
# get a reference to the database we are going to be scripting from
$serverInstance = New-Object ('Microsoft.SqlServer.Management.Smo.Server') $serverName
$db=$serverInstance.Databases | Where-Object {$_.Name -eq $databaseName}
$dbname = "$db".replace("[","").replace("]","")
$dbpath = "$path"+ "\"+"$dbname" + "\"
if ( !(Test-Path $dbpath))
{
$null=new-item -type directory -name "$dbname"-path "$path"
}
# Load the list of db objects we want to script.
$listPath = ".\DbObjectList.txt"
if ((Test-Path $listPath))
{
$dbListItems = Get-Content -Path $listPath
}
else
{
throw "Could not find DbObjectst.txt file (it should have a list of what to script)."
}
# Setup the output file, removing any existing one
$outFile = "$dbpath" + "FullScript.sql"
if ((Test-Path $outFile)){Remove-Item $outFile }
$typeDelimiter = "=========="
foreach ($dbListItem in $dbListItems)
{
# Let the caller know which one we are working on.
echo $dbListItem
if ($dbListItem.StartsWith($typeDelimiter))
{
# Pull the type out of the header
$startIndex = $typeDelimiter.Length;
$stopIndex = $dbListItem.LastIndexOf($typeDelimiter)
$type = $dbListItem.Substring($startIndex, $stopIndex - $startIndex).Trim()
continue;
}
if ($type -eq $null)
{
throw "Types not included DbObjectsList.txt. Add types before groups of objects, surrounded by " + $typeDelimiter
}
foreach ($dbObjectToScript in $db.$type)
{
$objName = "$dbObjectToScript".replace("[","").replace("]","")
$compareDbListItem = "$dbListItem".replace("[","").replace("]","")
if ($compareDbListItem -eq $objName)
{
"-- " + $dbListItem | out-File -Append $outFile
$dbObjectToScript.Script($scriptOptions)+"GO" | out-File -Append $outFile
}
}
}
I am trying to gather data from eventlogs of logons, disconnect, logoff etc... this data will be stored in a csv format.
This is the script i am working which got from Microsoft Technet and i have modified to meet my requirement. Script is working as it should be but there is looping going on which i can't figure out how it should be stopped.
$ServersToQuery = Get-Content "C:\Users\metho.HOME\Desktop\computernames.txt"
$cred = "home\Administrator"
$StartTime = "September 19, 2018"
#$Yesterday = (Get-Date) - (New-TimeSpan -Days 1)
foreach ($Server in $ServersToQuery) {
$LogFilter = #{
LogName = 'Microsoft-Windows-TerminalServices-LocalSessionManager/Operational'
ID = 21, 23, 24, 25
StartTime = (Get-Date).AddDays(-1)
}
$AllEntries = Get-WinEvent -FilterHashtable $LogFilter -ComputerName $Server -Credential $cred
$AllEntries | Foreach {
$entry = [xml]$_.ToXml()
$Output += New-Object PSObject -Property #{
TimeCreated = $_.TimeCreated
User = $entry.Event.UserData.EventXML.User
IPAddress = $entry.Event.UserData.EventXML.Address
EventID = $entry.Event.System.EventID
ServerName = $Server
}
}
}
$FilteredOutput += $Output | Select TimeCreated, User, ServerName, IPAddress, #{Name='Action';Expression={
if ($_.EventID -eq '21'){"logon"}
if ($_.EventID -eq '22'){"Shell start"}
if ($_.EventID -eq '23'){"logoff"}
if ($_.EventID -eq '24'){"disconnected"}
if ($_.EventID -eq '25'){"reconnection"}
}
}
$Date = (Get-Date -Format s) -replace ":", "-"
$FilePath = "$env:USERPROFILE\Desktop\$Date`_RDP_Report.csv"
$FilteredOutput | Sort TimeCreated | Export-Csv $FilePath -NoTypeInformation
Write-host "Writing File: $FilePath" -ForegroundColor Cyan
Write-host "Done!" -ForegroundColor Cyan
#End
First time when i run the script, it runs fine and i get the csv output as it should be. When i run the script again than a new CSV is created (as it should be) but the same event log enteries are created twice and run it again than three enteries are created for the same event. This is very strange as a new csv is created each time and i dont not have -append switch for export-csv configured.
$FilteredOutput = #()
$Output = #()
I did try adding these two lines in above script as i read somewhere that it is needed if i am mixing multiple variables into a array (i do not understand this so applogies if i get this wrong).
Can someone please help me this, more importantly, I need to understand this as it is good to know for my future projects.
Thanks agian.
mEtho
It sounds like the$Output and $FilteredOutput variables aren't getting cleared when you run the script subsequent times (nothing in the current script looks to do that), so the results are just getting appended to these variables each time.
As you've already said, you could add these to the top of your script:
$FilteredOutput = #()
$Output = #()
This will initialise them as empty arrays at the beginning, which will ensure they start empty as well as make it possible for them to be appended to (which happens at the script via +=). Without doing this on the first run the script likely failed, so I assume you must have done this in your current session at some point for it to be working at all.
i am running a powershell program to insert the data from Excel to database:
param([int]$StepNumber=1)
$invocation = (Get-Variable MyInvocation).Value
$directorypath = Split-Path $invocation.MyCommand.Path
$datestr=$(Get-Date).ToString("yyyy-MM-dd")
$url="http://www.dtcc.com/~/media/Files/Downloads/client-center/NSCC/NSCC-MPID-Directory.xls"
$destination = $directorypath +"/NSCC-MPID-Directory"+"_"+$datestr+".xls"
## if destination does not exsist, creat a new one.
if (!(Test-Path $destination) ) {
New-Item $destination -type file -force
}
$client = new-object System.Net.WebClient
$client.DownloadFile( $url, $destination)
$filepath = $destination
$excelConnection = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=$filepath;Extended Properties='Excel 12.0 Xml;HDR=YES;IMEX=1'"
$sqlConnection = 'Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=SIAC;Data Source=database1;'
$excelQuery = 'select * from [OTC$]'
$tablename = 'NSCC_MPID_OTC'
Try {
$conn = New-Object System.Data.OleDb.OleDbConnection($excelConnection)
$conn.open()
$cmd = $conn.CreateCommand()
$cmd.CommandText = $excelQuery
$rdr = $cmd.ExecuteReader()
$sqlbc = New-Object System.Data.SqlClient.SqlBulkCopy($sqlConnection, [System.Data.SqlClient.SqlBulkCopyOptions]::TableLock)
$sqlbc.DestinationTableName = $tableName
#$sqlbc.Batchsize = 1000
#$sqlbc.BulkCopyTimeout = 60
#$sqlbc.EnableStreaming=$true
# add all columns - you can add as few as you like.
for ($i = 0; $i -lt $rdr.FieldCount; $i++) {
$fname = $rdr.GetName($i)
Write-Host $fname -ForegroundColor green
[void]$sqlbc.ColumnMappings.Add($fname, $fname)
}
# write all of the data to the table
Try {
$sqlbc.WriteToServer($rdr)
} Catch {
Write-Host "$_" -Fore red -back white
} Finally {
$rdr.Close()
}
} Catch {
Write-Host "$_" -ForegroundColor red
}
When I run the program which needs to use the OLE DB Provider, it gives the error: "The Microsoft.ACE.OLEDB.12.0 Provider is not registered on the local machine. Can someone tell me why it can not find the provider when it is already there.
I tried dowloading Microsoft Database Access Engine 2017 and Microsoft Database Access Engine 2010 Redistributible and still have the same error. I also restart my computer and try run as administrator.... No thing worked for me.
Could anyone help me with this? Thanks.
The OLEDB provider are 32-bit and 64-bit aware.
If only 32-bit provider is installed (when installed Office is 32-bit),
The script must be run using powershell (x86).
To do that, run this command:
& "$Env:WINDIR\SysWOW64\WindowsPowerShell\v1.0\powershell.exe" -File <script_file_path>
To list installed OLEDB, see List all Ole DB Providers in Powershell.
The output may be different when run in Powershell (x64) and Powershell (x86).
function Get-OledbRegistered
{
[CmdletBinding()]
[OutputType([System.Collections.Generic.List[PSObject]])]
param ()
Process
{
$list = New-Object ([System.Collections.Generic.List[PSObject]])
foreach ($provider in [System.Data.OleDb.OleDbEnumerator]::GetRootEnumerator())
{
$v = New-Object PSObject
for ($i = 0; $i -lt $provider.FieldCount; $i++)
{
Add-Member -in $v NoteProperty $provider.GetName($i) $provider.GetValue($i)
}
$list.Add($v)
}
return $list
}
}
Get-OledbRegistered
I am using the Scripter class to give me a script for the data out of an existing database. I want to script a dataset that can be inserted into a production database. We are doing this to test if an installation of our Software is correct.
Unfortunately the dataset has to be removed later without any entries left behind so that it does not interfere with the data of our customers. So what I need are INSERT and DELTE statements. These are maintained manually at the moment which is too much of a burden.
Very well so I just went and executed the Scripter twice (once for INSERT, once for DELETE)
Problem is that when setting ScriptDrops to true then the output is in the form
DELETE FROM [dbo].[TableName]
What I would like is something of the form:
DELETE FROM [dbo].[TableName] WHERE ID = 'GUID'
Technically this would be possible since there are Primary Keys on all the tables.
The Scripter class must also in some form know of that things since it also gets the order of the DELETE-statements (dependencies) correct via foreign keys.
Any help on this would be appreciated.
Following are the 2 PowerShell-scripts I am using to export the data:
ScriptRepositoryData.ps1
$scriptPath = $MyInvocation.MyCommand.Path
$scriptDirectory = Split-Path $scriptPath -Parent
. $scriptDirectory\DatabaseScripting.ps1
$filepath='c:\data.sql'
$database='ECMS_Repository'
$tablesToExclude = #(
"SomeUnwantedTable"
)
$tablesListFromDatabase = GetTableList $database
$tablesArray = #()
$tablesListFromDatabase |% {
if (-not $tablesToExclude.Contains($_.Name.ToString()))
{
$tablesArray += $_.Name
}
}
ScriptInsert $database $tablesArray $filepath
DatabaseScripting.ps1
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | out-null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMOExtended") | out-null
Function GetTableList ($database)
{
Invoke-SqlCmd -Database $database -query "SELECT * FROM sys.tables"
}
Function ScriptInsert ($database, $tables, $destination)
{
try {
$serverMO = new-object ("Microsoft.SqlServer.Management.Smo.Server") "localhost"
if ($serverMO.Version -eq $null) {Throw "Can't find the instance localhost"}
$urnsToScript = New-Object Microsoft.SqlServer.Management.Smo.UrnCollection
$databaseMO = $serverMO.Databases.Item("ECMS_Repository")
if ($databaseMO.Name -ne $database) {Throw "Can't find the database $database"}
$tables |% {
$tableListMO = $databaseMO.Tables.Item($_, "dbo")
$tableListMO |% {
$urnsToScript.Add($_.Urn)
}
}
$scripter = new-object ('Microsoft.SqlServer.Management.Smo.Scripter') $serverMO
$scripter.Options.ScriptSchema = $False;
$scripter.Options.ScriptData = $true;
$scripter.Options.ScriptDrops = $true;
$scripter.Options.ScriptAlter = $true;
$scripter.Options.NoCommandTerminator = $true;
$scripter.Options.Filename = $destination;
$scripter.Options.ToFileOnly = $true
$scripter.Options.Encoding = [System.Text.Encoding]::UTF8
$scripter.EnumScript($urnsToScript)
Write-Host -ForegroundColor Green "Done"
}
catch {
Write-Host
Write-Host -ForegroundColor Red "Error occured"
Write-Host
Write-Host $_.Exception.ToString()
Write-Host
}
}
Unfortunately I did not find a way to do this using the Sql Management Objects.
Anyhow I now use the output of the Scripter and select the IDs of each table. I then use the IDs to change every line that looks like
DELETE FROM [dbo].[tableName]
to this:
DELETE FROM [dbo].[tableName] WHERE ID IN ('guid1', 'guid2')
Here is how I did it:
$content = Get-Content $destination
Clear-Content $destination
$content |% {
$line = $_
$table = $line.Replace("DELETE FROM [dbo].[","").Replace("]","")
$query = "SELECT ID, ClassID FROM" + $_
$idsAsQueryResult = Invoke-SqlCmd -Database $database -query $query
$ids = $idsAsQueryResult | Select-Object -Expand ID
if ($ids -ne $null) {
$joinedIDs = [string]::Join("','",$ids)
$newLine = $line + " WHERE ID IN ('" + $joinedIDs + "')"
Add-Content $destination $newLine
}
}
Where $destination is the script that has been generated with the Scripter class and $database is a string containing the database name.
I had to select a second column (ClassID which is there on all tables due to our OR mapper re-store) because of some weird error in Select-Object which I do not fully understand.
This of course only works because all tables have primary keys and all primary keys are named ID and are not combined primary keys or something.
You could of course achieve the same thing for other more complicated database schemas by extracting primary key information via SQL management objects.