How to get the compatibility level of a database from analysis server? - sql-server

I searched around and found there is a way to get compatibility level of a database in powershell through something like:
Import-Module SqlServer
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server=$Server;Initial Catalog=$DB;Integrated Security=SSPI"
$conn = new-object Microsoft.SqlServer.Management.Common.ServerConnection($sqlConnection)
$srv = new-object Microsoft.SqlServer.Management.Smo.Server($conn)
$db = New-Object Microsoft.SqlServer.Management.Smo.Database
$db = $srv.Databases.Item("$DB")
$comp_lvl = New-Object Microsoft.SqlServer.Management.Smo.CompatibilityLevel
$comp_lvl = $db.CompatiblityLevel
Write-Host "Compatibility level =" $db.CompatibilityLevel
However, I am getting errors trying to get compatibility level of a database that is on an analysis server
Exception getting "Item": "Failed to connect to server ..."
I realized it is probably working for a regular database engine, but something else may be used for an analysis server. I looked around on MS Docs and didnt really find anything helpful.
SO is this even possible?
UPDATE:
I was able to find something on this page: https://technet.microsoft.com/en-us/hh213141(v=sql.100)
Import-Module SqlServer
$as = New-Object Microsoft.AnalysisServices.Server
$as.connect("$Server")
$as.databases
Write-Host "Compatibility level ="$as.DefaultCompatibilityLevel
but this returns ALL databases back...
I want to specify just one database to get the compatibility level of...
I tried this,
$as.databases["$Database"]
but it seems not to return the proper level, because the DB i am passing has a lvl of 1103, not 1200...

I figured it out!
Import-Module SqlServer
$as = New-Object Microsoft.AnalysisServices.Server
$as.connect("$Server")
$c = $as.Databases | Where-Object { $_.ID -eq $Database }
Write-Host "Compatibility level =" $c.CompatibilityLevel

Related

How to create a copy of existing SQL DB on same server

I to perform an operation a part of which has me looking for a way to create a copy of SQL DB on same server. I tried the suggestion given at Copy SQL Server database with PowerShell script . However the resulting copy is about a quarter size of the actual DB.
Ideas anyone?
Thanks
If your PowerShell solution is working except you are noticing a file-size discrepancy with the newly copied database compared to the source database, it may not be an actual problem.
SQL Server database and log sizes are variable and are typically not an exact indication of the amount of data they contain. The copied database may be "optimized" in terms of its disk file usage in a way that the source database is currently not.
There are three things you can do to convince yourself you have a working solution.
Run the Shrink command on both of the databases to free space and see if the resulting disk files are more similar in terms of size https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-shrinkdatabase-transact-sql?view=sql-server-ver15
Write a benchmark script that compares record counts on all the tables. If you see a discrepancy in the record count, you know you have a problem.
Example benchmark script:
declare #sourceCount int;
declare #copyCount int;
set #sourceCount = (select count(*) from SourceDb.dbo.SomeTable);
set #copyCount = (select count(*) from CopyDb.dbo.SomeTable);
if #sourceCount <> #copyCount
begin
select 'PROBLEM!'
end
-- Now repeat for all the other tables
Use a SQL data comparison tool to automate the method in step 2. above. There are many such tools including the one built-in to Visual Studio 2019 (Tools menu) or otherwise: https://learn.microsoft.com/en-us/sql/ssdt/how-to-compare-and-synchronize-the-data-of-two-databases?view=sql-server-ver15
However, all these methods will only work reliably if you can ensure that either database isn't receiving updates during the copy/benchmarking process. If someone is accessing one or the other of the databases independently while you are measuring, and they alter the data independently while you are measuring, your results would be invalidated, possibly without your knowing.
EDIT
I managed to make it work as soon as I have started using the SqlServer module instead of the SQLPS module, because the latter had long been deprecated. I edited the answer I am referring to in my initial post, below.
I was having a similar error implementing this. Tried literally everything, it just wouldn't work. What did work for me, was generating a script through the ScriptTransfer method, create the new database and then apply the script to the new database through Invoke-SqlCmd. I have posted a detailed explanation and code in this answer.
Okay. I managed to implement this. And anybody who needs to do this in future, please try this:
Import-Module SQLPS -DisableNameChecking
$SQLInstanceName = "$env:COMPUTERNAME\sqlexpress"
$SourceDBName = "xxx"
$CopyDBName = "${SourceDBName}_copy"
$Server = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' -ArgumentList $SQLInstanceName
$SourceDB = $Server.Databases[$SourceDBName]
$CopyDB = New-Object -TypeName 'Microsoft.SqlServer.Management.SMO.Database' -ArgumentList $Server , $CopyDBName
# Delete any existing copy
Try
{
Invoke-Sqlcmd -ServerInstance "$SQLInstanceName" -Query "Drop database $CopyDBName;" -Username "***" -Password "****" -Verbose
}
Catch
{
Write-Output 'Failed to delete database'
}
$CopyDB.create()
$ObjTransfer = New-Object -TypeName Microsoft.SqlServer.Management.SMO.Transfer -ArgumentList $SourceDB
$ObjTransfer.DestinationDatabase = $CopyDBName
$ObjTransfer.DestinationServer = $Server.Name
$ObjTransfer.DestinationLoginSecure = $true
$ObjTransfer.CopyData = $true
$ObjTransfer.CopyAllObjects = $false
$ObjTransfer.CopyAllDatabaseTriggers = $true
$ObjTransfer.CopyAllDefaults = $true
$ObjTransfer.CopyAllRoles = $true
$ObjTransfer.CopyAllRules = $true
$ObjTransfer.CopyAllSchemas = $true
$ObjTransfer.CopyAllSequences = $true
$ObjTransfer.CopyAllSqlAssemblies = $true
$ObjTransfer.CopyAllSynonyms = $true
$ObjTransfer.CopyAllTables = $true
$ObjTransfer.CopyAllViews = $true
$ObjTransfer.CopyAllStoredProcedures = $true
$ObjTransfer.CopyAllUserDefinedAggregates = $true
$ObjTransfer.CopyAllUserDefinedDataTypes = $true
$ObjTransfer.CopyAllUserDefinedTableTypes = $true
$ObjTransfer.CopyAllUserDefinedTypes = $true
$ObjTransfer.CopyAllUserDefinedFunctions = $true
$ObjTransfer.CopyAllUsers = $true
$ObjTransfer.PreserveDbo = $true
$ObjTransfer.Options.AllowSystemObjects = $false
$ObjTransfer.Options.ContinueScriptingOnError = $true
$ObjTransfer.Options.IncludeDatabaseRoleMemberships = $true
$ObjTransfer.Options.Indexes = $true
$ObjTransfer.Options.Permissions = $true
$ObjTransfer.Options.WithDependencies = $true
$ObjTransfer.TransferData()

Get-ClusterGroup and foreach using imported SQL table

I am looking at getting information from a number of servers i.e. clustername, nodename, and state.
I can get the correct ouput when I hardcode the names into a foreach loop like this:
$clusters = "Cluster1", "Cluster2"
foreach ($cluster in $clusters) {
Get-ClusterGroup -Cluster $cluster
}
but when I run this version that pulls the information from a SQL table its coming up with the below error.
$clusters = $SQLServer = "DatawarehouseServer" #use Server\Instance for named SQL instances!
$SQLDBName = "Datawarehouse"
$SqlQuery = "SELECT clusters FROM dbo.clusters"
$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]
foreach ($cluster in $clusters) {
Get-ClusterGroup -Cluster $cluster
}
WARNING: If you are running Windows PowerShell remotely, note that some failover
clustering cmdlets do not work remotely. When possible, run the cmdlet locally
and specify a remote computer as the target. To run the cmdlet remotely, try
using the Credential Security Service Provider (CredSSP). All additional errors
or warnings from this cmdlet might be caused by running it remotely.
Get-ClusterGroup : The cluster service is not running. Make sure that the
service is running on all nodes in the cluster. There are no more endpoints
available from the endpoint mapper
At line:26 char:2
+ {Get-ClusterGroup -Cluster $cluster}
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ConnectionError: (:) [Get-ClusterGroup], ClusterCmdletException
+ FullyQualifiedErrorId : ClusterEndpointNotRegistered,Microsoft.FailoverClusters.PowerShell.GetClusterGroupCommand.
I have now been trying this I seem to be getting step closer or a step back depedant on how you look at it. It pulls the correct information out now but for only one cluster:
Thanks to the comments I have managed to get a step further or a step backwards with this:
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server=Datawarehouseserver;Database=Datawarehouse;Integrated Security=True"
$SqlConnection.Open()
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = "SELECT clusters FROM dbo.clusters"
$SqlCmd.Connection = $SqlConnection
$clustername = $SqlCmd.ExecuteScalar()
$SqlConnection.Close()
#Write-Output "Cluster is " $dbname
clear
foreach ($cluster in $clustername) {
Get-ClusterGroup -Cluster $cluster
}
You need to iterate over the cluster names returned by your SQL query, but your variable $cluster is never assigned that list of names. Use this instead:
foreach ($cluster in $DataSet.Tables[0].clusters) {
...
}

How to use PowerShell to batch call Update-Database

We use an Azure Elastic Pool resulting in multiple client databases and one master database with references to the client database.
We already have multiple databases and are working on a new version of the code. We use EF6 Code-First.
When we make a change to our model (add a property) we create the migration file and need to call Update-Database for all existing client databases.
This is monkey work we want to skip.
I already have a Powershell script to connect to the master database and execute a query on a table. This returns the names of the child databases.
With it I can change the Web.config and replace the Template database name with the proper name of the child database.
Now I need to call Update-Database to execute the migration scripts. With this last part I'm struggling because I'm running the ps1-script outside Visual Studio and thus the command Update-database is unknown. I tried using migrate.exe but then I get lots of errors.
I think the easiest solution is to run my script within the Package manager console but I can't figure out how to do that.
I managed to get it working. After I placed the ps1-file in the root of my code folder I could run it in the Package Manager Console using .\UpdateDatabases.ps1.
For completeness here's the script I created. I'm new to PowerShell so some optimizations might be possible.
cls
$currentPath = (Get-Item -Path ".\" -Verbose).FullName
#Read Web.config
$webConfig = $currentPath + "\<your project>\Web.config"
$doc = (Get-Content $webConfig) -as [Xml]
$DatabaseNamePrefix = $doc.configuration.appSettings.add | where {$_.Key -eq 'DatabaseNamePrefix'}
#Get Master connectionstring
$root = $doc.get_DocumentElement();
foreach($connString in $root.connectionStrings.add | where {$_.Name -eq "Master"})
{
$masterConn = $connString.connectionString
}
#Connect to master database
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = $masterConn
#Query Client table for the child database names
$SqlQuery = "select Code from Clients"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $SqlQuery
$SqlCmd.Connection = $SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
#Put query result in dataset
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$SqlConnection.Close()
foreach ($row in $DataSet.Tables[0].Rows)
{
$clientDbName = $row[0].ToString().Trim()
#Change Web.Config
foreach($connString in $root.connectionStrings.add | where {$_.Name -eq "DevelopmentDb"})
{
$newDatabaseName = "Database=" + $DatabaseNamePrefix.value + $clientDbName + ";";
$newConn = $connString.connectionString -replace "(Database=.*?;)",$newDatabaseName
$connString.connectionString = $newConn;
}
$doc.Save($webConfig)
#Update database
Update-Database -ConfigurationTypeName Application
}
"Finished"
You may want to take a look at Azure Elastic Database Jobs. Which is designed to work with the elastic database pools.
The Elastic Database Jobs SDK includes also PowerShell components.

How to query SQL Server using PowerShell?

I found some code online but so far I can't get it to connect to my SQL Server database. I have followed this website to the letter: https://blogs.msdn.microsoft.com/walzenbach/2010/04/14/how-to-enable-remote-connections-in-sql-server-2008/
I have allowed remote connections, added port 1433 to my firewall etc. I then run this code from PowerShell ISE:
$dataSource = “\\SCCM12-01\MSSQLSERVER”
$user = “MyID\OurDomain.org”
$pwd = “MyPassword”
$database = “CM1”
$connectionString = “Server=$dataSource;uid=$user; pwd=$pwd;Database=$database;Integrated Security=False;”
$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
$connection.Open()
when I run this I get the following error.
Exception calling "Open" with "0" argument(s): "A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was
not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 25 - Connection
string is not valid)"
If you have simple query to do I recommend Select-SQLView powershell module. It allows to quickly select rows from table or view. It stores your database and server name so you do not have to provide this values every time.
As usual You can push results to table or to GridView.
If more complex queries are needed use SQLCommands module.
Not sure why you are getting this error. You can refer to this link https://github.com/Tervis-Tumbler/InvokeSQL
You can try this one-
function Invoke-SQL {
param(
[string] $dataSource = ".\SQLEXPRESS",
[string] $database = "MasterData",
[string] $sqlCommand = $(throw "Please specify a query.")
)
$connectionString = "Data Source=$dataSource; " +
"Integrated Security=SSPI; " +
"Initial Catalog=$database"
$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
$dataset = New-Object System.Data.DataSet
$adapter.Fill($dataSet) | Out-Null
$connection.Close()
$dataSet.Tables
}
The error message actually explains what's wrong:
"SQL Network Interfaces, error: 25 - Connection string is not valid".
There is something amiss on the connection string. What exactly is hard to say as you have masked most of the details. Maybe the smart quotes wreck things? Maybe you got a quote character in the password? Anyway, it looks like you have invalid parameter for the user id:
$connectionString = “Server=$dataSource;uid=$user; pwd=$pwd;Database=$database;Integrated Security=False;”
Try User Id instead of uid like so,
Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;
You properly just need to change 'Integrated Security' to 'true' when not using a db login
**Integrated Security=True**

Unable to Get Information using PowerShell to Query Oracle

I use PowerShell to query SQL databases, and I am quite familiar with that process. However, I am now tasked with building an automated task that queries Oracle for information.
It seems straight forward: Install proper Oracle DLL's, import them into PS, execute the query much like SQL. However, this is not the case. All I get when I request information is a list called FieldCount. This seems to imply that I am able to see the information, it's just not displaying correctly. I'd like the actual values, and nothing seems to get this for me.
Thanks to anyone who knows anything about this, as my hands are tied and this is the only way I can think of to get this information from Oracle on a scheduled basis. I am not the Oracle admin, I only have read access to this view.
function Get-OracleData($cmdText){
Add-Type -Path 'C:\app\client\username\product\12.1.0\client_1\odp.net\managed\common\Oracle.ManagedDataAccess.dll'
$username = 'username'
$password = 'password'
$con = New-Object Oracle.ManagedDataAccess.Client.OracleConnection('User Id=$username;Password=$password;Data Source=OracleServerName')
$con.Open()
$cmd = New-Object Oracle.ManagedDataAccess.Client.OracleCommand
$cmd.Connection = $con
$cmd.CommandText = $cmdText
$rdr = $cmd.ExecuteReader()
if($rdr.Read()){
return $rdr
}else{return 0}
}
Get-OracleData -cmdText '
SELECT em.employee_number,
em.last_name,
em.first_name,
em.middle_names,
em.email_address,
em.start_date,
em.term_date,
em.location_addr_line_1,
em.location_city,
em.location_work_state,
FROM CustomView em
'
Found the answer in the link below. I was able to get what I needed by inserting the below code at the line where $cmd.CommandText = $cmdText is located in my original post, and getting rid of what's below it.
$ds = New-Object system.Data.DataSet
$da = New-Object Oracle.ManagedDataAccess.Client.OracleDataAdapter($cmd)
[void]$da.fill($ds)
return $ds.Tables[0] | Select *
This returns to a variable, and I can get the first entry using $results[0], and $results[0].EMPLOYEE_NUMBER, etc.
Reference: http://poshcode.org/3965 #line55

Resources