I run the query below (from inside powershell), from different servers and all is fine:
Invoke-Sqlcmd -Query "
SELECT [ServerName]=##servername,m.Match_id, m.HostfamilyId, hf.NIEFlag, ra.DS2019Sent
FROM APIA_Repl_pub.dbo.repl_HostFamily hf
INNER JOIN APIA_Repl_pub.dbo.repl_Match m ON m.HostfamilyId = hf.HostFamilyID
INNER JOIN APIA_Repl_Pub.dbo.repl_Aupair ra on ra.AuPairID = m.AupairId
WHERE ra.JunoCore_applicationID = 459630
" -ServerInstance "CTSTGDB"
I even sometimes run the same query into several servers to compare them, see the result below as an example:
I just want to run the same query from inside SSMS, but see what I do and what I get:
(I have even simplified the query but still I get the error below)
GO
declare #sql varchar(8000)
SET #SQL=N'powershell.exe -command Invoke-Sqlcmd -Query "SELECT [ServerName]=##servername" -ServerInstance "CTSTGDB"'
IF OBJECT_ID('tempdb..#Radhe','U') IS NOT NULL
DROP TABLE #RADHE
CREATE TABLE #RADHE(I INT NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED, OUTPUT NVARCHAR(4000))
INSERT INTO #RADHE
EXEC xp_cmdshell #sql
SELECT * FROM #RADHE
GO
OUTPUT
Invoke-Sqlcmd : A positional parameter cannot be found that accepts argument
'[ServerName]=##servername'.
At line:1 char:1
+ Invoke-Sqlcmd -Query SELECT [ServerName]=##servername -ServerInstance ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Invoke-Sqlcmd], ParameterB
indingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.SqlServer.
Management.PowerShell.GetScriptCommand
NULL
I found out an interesting article:
6 methods to write PowerShell output to a SQL Server table
It is related or could be an alternative to the way I am working.
what exactly is the problem of Invoke-Sqlcmd from inside SSMS?
As I understand, you struggle with executing a query against a different server than the one the query is connected to. Well, I can think of at least 2 ways of doing this in SSMS:
If your current server has a linked server for the one you are after, you can use the AT clause of the EXEC statement, like this:
declare #Sql nvarchar(max) = N'SELECT [ServerName]=##servername,m.Match_id,
m.HostfamilyId, hf.NIEFlag, ra.DS2019Sent
FROM APIA_Repl_pub.dbo.repl_HostFamily hf
INNER JOIN APIA_Repl_pub.dbo.repl_Match m ON m.HostfamilyId = hf.HostFamilyID
INNER JOIN APIA_Repl_Pub.dbo.repl_Aupair ra on ra.AuPairID = m.AupairId
WHERE ra.JunoCore_applicationID = 459630';
-- This example assumes that the linked server name is the same as the remote server itself
exec (#Sql) at [CTSTGDB];
If linked server is not available, you may utilise the SQLCMD mode for the query:
:connect CTSTGDB
go
SELECT [ServerName]=##servername,m.Match_id, m.HostfamilyId, hf.NIEFlag, ra.DS2019Sent
FROM APIA_Repl_pub.dbo.repl_HostFamily hf
INNER JOIN APIA_Repl_pub.dbo.repl_Match m ON m.HostfamilyId = hf.HostFamilyID
INNER JOIN APIA_Repl_Pub.dbo.repl_Aupair ra on ra.AuPairID = m.AupairId
WHERE ra.JunoCore_applicationID = 459630;
go
:exit
go
This mode can be toggled by the Query -> SQLCMD Mode menu option.
Related
I am exploring powershell to run the following SQL script stored in my machine with name "TestSQLScript.sql"
USE TestDB
GO
SELECT TOP (1000) [DocId]
,[DocumentInfo]
FROM [TestDB].[dbo].[Docs]
INSERT INTO WorkDocs
( [DocId]
,[DocumentInfo])
SELECT [DocId]
,[DocumentInfo]
FROM [TestDB].[dbo].[Docs]
SELECT TOP (1000) [DocId]
,[DocumentInfo]
FROM [TestDB].[dbo].[WorkDocs]
When I use the following command it executed well.
invoke-sqlcmd -inputfile "D:\TestSQLScript.sql" -serverinstance "MYPC\MSSQLSERVER2019" -Username "sa" -Password "***" -database "master"
When I am running the following it through error while accessing table WorkDocs.
invoke-DbaQuery -SQLInstance "MYPC\MSSQLSERVER2019" -sqlcredential $Cred -File "D:\TestSQLScript.sql" -database "master"
In the initial finding it is noticed that tables referenced as [TestDB].[dbo].[WorkDocs] are accessible, and table referenced without [TestDB].[dbo] is not accessible at Insert statement, while I have already used the database reference on top in the SQL file "TestSQLScript.sql"
USE TestDB
GO
I can not reference all the tables with [TestDB].[dbo] as I have hundreds of script and there are multiple database references in each SQL script. Can you suggest how can I resolve this?
In addition to this How can I get the result count for each SQL query in the file. I have Multiple Select, Insert, Update and truncate SQL command in one SQL file and I want to log each execution result. Please refer to below for more.
For Select Statements - 1000 Records fetched
For Insert Statements - 100 rows inserted into 'Table Name'
For Update Statements - 50 rows affected in 'Table Name'
For Truncate Statements - 'Table Name' has been truncated.
Platform: SQL Server 2016
I've written a SQL statement that outputs a series of SQL commands and I want to execute the output of this query in the same script. This is the query that builds the commands I want to execute.
select
'ALTER SCHEMA dbo TRANSFER SYSNET.' + name + ';'
from
sys.tables
where
schema_name(schema_id) = 'sysnet'
order by
1;
I know I need to capture the output in a variable and then execute it. I'm sure it's simple but everything I've tried didn't work and Google has failed me.
==================================================
Thanks for the answers! scsimon technically gave the best answer since it provided the means of executing the output of any dynamic SQL and that's what I asked for. With that said, Ross Bush provided the simplest way for me to accomplish this specific task of transferring schema ownerships. In the end I used this...
EXEC sp_MSforeachtable #command1='ALTER SCHEMA dbo TRANSFER ?;'
,#whereand='AND schema_name(schema_id) = ''sysnet'''
Just loop through those seems to be what you want.
select row_number() over (order by (select null)) as RN, 'ALTER SCHEMA dbo TRANSFER SYSNET.'+ name + ';' as CMD
into #mytemp
from sys.tables where
schema_name(schema_id) = 'sysnet'
declare #sql varchar(max)
declare #i int = 1
while #i <= (select max(RN) from #mytemp)
begin
select #sql = CMD from #mytemp where RN = #i
print #sql
--exec(#sql)
set #i = #i + 1
end
drop table #mytemp
This is not the best advice as the function below is an undocumented SQL Server function and may not be around in the future. That being said, I would use the sp_MSforeachtable table to issue a command for each table in the target database.
EXEC sp_MSforeachtable 'SELECT COUNT(*) FROM ?'
It is possible in SSMS to run the same query on some databases?
I was thinking something like an array with database names and cycling through it, maybe with SQLCMD mode.
some pseudocode:
:setvar arr ["db1", "db2", "db3"]
foreach $db in $arr
:setvar database $db
use $(database)
go
select * from table
Thanks
-- To Achive your Desire OutpUt You Have to Use Dynamic Query
-- you can Achieve this in TSQL
-- TO Know The Database ID Run Below Query
/*
SELECT * FROM Sys.databases WHERE database_id >4
*/
USE MASTER
GO
BEGIN TRAN
DECLARE #strt INT,#End INT,#Database NVARCHAR(255)
SELECT * INTO #T FROM Sys.databases WHERE database_id IN (4,5,6)-- Here you Have to Defined the Database ID
ORDER BY 1
SELECT ROW_NUMBER ()OVER (ORDER BY database_Id)Db_Id,* INTO #TT FROM #T
SET #strt=1
SELECT #End=Max(Db_ID)FROM #tt
WHILE #strt<=#END
BEGIN
DECLARE #string NVARCHAR(MAX)
SELECT #Database=NAME FROM #TT WHERE Db_ID=#strt
Set #string=' Select * from '+#Database+'..Table_Name'
SET #strt=#strt+1
PRINT #string
EXEC(#string)
END
ROLLBACK TRAN
Here's an example using two of my databases (Staging and Warehouse) to hit the sys.columns table in each. Just change out the IN filter with the names of whichever databases you want, and the ".sys.columns" with the schema/table name you need.
DECLARE #query NVARCHAR(MAX) = ''
;
SELECT #query += CONCAT('SELECT * FROM ',[name],'.sys.columns;')
FROM sys.databases
WHERE [name] IN ('Staging','Warehouse')
;
EXEC sp_executesql #query;
You can do this with a Local Server Group as well.
View --> Registered Servers
Right-click Local Server Group and create new
Add new server registrations to the group (any databases you want to query). Be sure to specify the database in the Connection Properties tab
Now you can right-click the Local Server Group folder and execute a new query, which will run on all the databases in that folder.
I have a linked server that I have to fetch data from. I'm joining on a table that I expect very few rows from. The query is below, and seems to be returning all of the rows to the original server to do the sort there.
I'm looking for a way to tell the query to filter on the target machine, with a query hint or something else.
Query
INSERT INTO #DealerHierarchy(DealerId, Level)
SELECT cd.ParentId, cd.Level
FROM [dbo].[AssignedDealer] ad
JOIN [nlsdb].[nls].[dbo].[vw_parentDealers] cd ON cd.RootId = ad.DealerId
WHERE ad.UserId = #userId
AND ad.IsActive = 1
AND (#DealerId IS NULL OR ad.DealerId = #DealerId)
When I add the following line, it seems to change and only send back the needed rows
and cd.RootId = 72311
I have tried moving out the local query into a separate temp table, and then select from the view WHERE DealerId IN (select from temp table) but it still runs slowly. Adding the REMOTE hint in the JOIN also does nothing.
Query plan:
https://www.brentozar.com/pastetheplan/?id=r1iazaaFZ
Slow code executed on linked server
declare #p1 int
set #p1=7
exec sp_prepexec #p1 output,N'#P1 numeric(10)',N'SELECT "Tbl1007"."ParentId" "Col1010","Tbl1007"."Level" "Col1011" FROM "nls"."dbo"."vw_parentDealers" "Tbl1007" WHERE #P1="Tbl1007"."RootId"',72311
select #p1
Fast code executed on linked server
declare #p1 int
set #p1=10
exec sp_prepexec #p1 output,NULL,N'SELECT "Tbl1007"."ParentId" "Col1010","Tbl1007"."Level" "Col1011" FROM "nls"."dbo"."vw_parentDealers" "Tbl1007" WHERE "Tbl1007"."RootId"=(72311.)'
select #p1
You can force a specific query to be run on the remote database by using OPENQUERY. OPENQUERY doesn't accept a parameter, so you can make it dynamic by further wrapping it in EXEC.
Example
DECLARE #SearchString NVARCHAR = ...
DECLARE #OpenQueryString NVARCHAR = 'SELECT * FROM OPENQUERY(remotedb, ''' + #SearchString + ''')'
EXEC (#OpenQueryString)
Is there any way to reference the table inside a 'sp_MSforeachtable' loop running inside a 'sp_msforeachdb' loop?
For example, in the following query the '?' is always referencing the database:
DECLARE #cmd VARCHAR(8000);
SET #cmd = 'USE ?; EXEC sp_MSforeachtable #command1="select db_name = DB_NAME(), db_foreach = ''?'', tb_foreach = ''?'' "'
EXEC sp_msforeachdb #command1 =#cmd
Resulting in:
db_name db_forearch tb_foreach
ServerMonitor master master
I want to have something like:
db_name db_forearch tb_foreach
ServerMonitor master <TABLE_NAME>
What should I change?
Solved. I used my ow cursor, as suggested by Sean. But the #replacechar solution suggested by Ben Thul is exactly what I was looking for.
DECLARE #cmd VARCHAR(8000);
SET #cmd = 'USE ^; EXEC sp_MSforeachtable #command1="select db_name = DB_NAME(), db_foreach = ''^'', tb_foreach = ''?'' "'
EXEC sp_msforeachdb #command1 =#cmd, #replacechar = '^'
Take a look at the parameters for sp_msforeachtable. One of them is #replacechar which, by default, is a question mark (i.e. ?). Feel free to pass in another equally unlikely character to occur in a query (maybe a ^).
Of course, I'd be remiss if I didn't mention that depending on what you're trying to do (and I would argue that anything that you're trying to do over all tables is doable this way), there are easier to read (and write) solutions in powershell:
import-module sqlps -disablenamechecking;
$s = new-object microsoft.sqlserver.management.smo.server '.';
foreach ($db in $s.databases) {
foreach ($table in $db.Tables) {
$table | select parent, name; --merely list the table and database
}
}
For what you are doing you could do something like this. Although this is still using the for each db procedure which can be problematic. You will want to add a where clause to the final select statement to filter out some databases (model, tempdb, master, etc)
declare #TableNames table
(
DatabaseName sysname
, TableName sysname
)
insert #TableNames
EXEC sp_msforeachdb #command1 = 'use ?;select ''?'', name from sys.tables'
select *, 'exec ' + Databasename + '..sp_spaceused [''' + TableName + ']'';'
from #TableNames