Create a table or view from EXEC statement SQL Server - tsql

EXEC('Select
trim(field1) field1
CVDATE(date1) date1
from tabel1
where trim(field1) <> 'ABC' ') at AE --linked server
I need to dump the result generated from EXECUTION statement into a view. Is this possible? I am guessing the query is written this way is due to performance reasons

You can use the openquery to run the SELECT on remote server and create a view from the result set.
Here is a code sample. The sample creates local1 as the linked server and executes the query on it. If you already have your linked server registered, you don't need to run the step 1 and 4.
-- Step 1. register a linked server
exec sp_addlinkedserver
#server = 'local1',
#srvproduct = '',
#provider='SQLNCLI',
#datasrc = 'NameOfTheRemoteServer' -- if server has multiple instances use 'NameOfTheServer\NameOfTheInstance'
go
-- Step 2. run the query on remote server and create a view out of the results
create view ViewTest
as
select * from openquery(
local1,
'select
top 10
lower(AddressLine1) as Street,
ModifiedDate as dt
from AdventureWorks2014.Person.Address'
)
go
-- Step 3. check the view
select * from ViewTest
go
-- Step 4. finally drop the linked server
sp_dropserver 'local1'
go

Related

unexpected behavior when using SET NOEXEC ON in a script called using sqlcmd

Below is an example of a simplified set of scripts that accurately reproduces an issue the exists in a more complex scripts being written for Prod.
When simulation.bat is run before the sandbox database exists, it works fine - the database is created along with the one populated table and one view. Here is the terminal output from that -
However, after the initial execution of the batch file, subsequent executions of it cause a database error message to surface even though set NOEXEC on; was used in the if block. It appears to choke on the view creation because the table doesn't exist. While it makes sense that the table doesn't exist, why is it trying to create the view at all when set NOEXEC on has been set? How can the logic be modified to ensure that it does not try to create the view if the database already exists?
4 files -
simulation.bat
#echo off
sqlcmd -S TheServerName -E -d master -i .\Simulation.sql -v db_name=sandbox
pause
Simulation.sql
print 'database name $(db_name)';
--if database already exists then print a message and exit script, otherwise create database
if exists (select 1 from sys.databases where [name] = '$(db_name)')
begin
print '--- Database $(db_name) already exists - script execution aborted ---';
set NOEXEC on; --prevent execution of statements in this batch and batches that follow it
end;
if not exists (select 1 from sys.databases where [name] = '$(db_name)')
begin
create database $(db_name);
print '--- $(db_name) database created ---';
end;
go
use $(db_name);
go
:r .\Tally.sql
go
print 'Table creation is complete.';
go
:r .\vw_TallyRows.sql
go
print 'View creation is complete.';
go
set NOEXEC off; --allow execution of statements that follow
go
print 'Reached end of script.';
go
Tally.sql
create table Tally (n int not null primary key);
insert into Tally values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10);
vw_TallyRows.sql
create view vw_TallyRows as
select rows = count(1) from dbo.Tally;
At some point, it occurred to me that if the database existed (as indicated by the line printed to the terminal in the 2nd screenshot) then the dbo.Tally table should also exist. Based on that it didn't make sense that an invalid object name error message was appearing. The root cause: in Simulation.sql use $(db_name); came after the block of code that checks whether or not the database exists.
Therefore, the solution was to rearrange the order of the statements at the beginning of the Simulation.sql script and add another existence check -
print 'database name $(db_name)';
if not exists (select 1 from sys.databases where [name] = '$(db_name)')
begin
create database $(db_name);
print '--- $(db_name) database created ---';
end;
go
use $(db_name);
go
if exists (select 1 from sys.databases where [name] = '$(db_name)') and exists (select distinct 1 from sys.all_objects where is_ms_shipped = 0)
begin
print '--- Database $(db_name) already exists - script execution aborted ---';
set NOEXEC on; --prevent execution of statements in this batch and batches that follow it
end;
go
--no change to remaining logic in script
Terminal output when sandbox database does not exist yet:
Terminal output when sandbox database does exist:

Get all database names from multiple servers

We have multiple SQL Servers and most of them are standalone. I am in need of creating a stored procedure / view that would insert all database names into a table from all servers.
Is there a way to do this via a stored procedure or a view? I do not have any powershell or .Net experience.
Here's what I have so far. I just can't figure out how to 'jump' from server to server and add all my results into a real table.
CREATE TABLE ##temp
(
DATABASE_NAME VARCHAR(100),
DATABASE_SIZE INT,
REMARKS VARCHAR(500)
)
INSERT into ##temp
EXEC [sp_databases]
--doing this to also get ServerName along with the db name.
--When I insert into a real table, I'll seperate it into two columns plus remove "#_!_#"
update ##temp
set DATABASE_NAME = (select ##SERVERNAME ) + '#_!_# ' + DATABASE_NAME
where DATABASE_NAME not like '%#_!_#%'
select DATABASE_NAME from ##temp
SQL Server Management Studio allows you to execute a query against multiple servers using the Registered Servers feature. This was added in SQL Server 2008 as this tutorial shows so you shouldn't worry about compatibility.
Running multi-server queries is easy:
From the View menu, select `Registered Servers. This will open a new window similar to the Object Explorer that displays the objects of a single server.
Add connections for all your servers connection details in the Local Server Groups folder
Right-click on the Local Server Groups folder and select New Query. The query you enter here will run an all registered servers.
To find all databases run select * from sys.databases or just sp_databases
SSMS will collect the results from all servers and display them in a grid. If you want the results to go to a single server's table though, you'll have to add the target server as a linked server to all others and use a four-part name to target the target table, eg INSERT INTO myManagementServer.MyDb.dbo.ThatTable...
SQL Server has even more powerful features for managing multiple servers. You can administer multiple servers through a Central Management Server and apply settings to multiple servers through policies. That feature was also added in 2008.
In SQL Server 2008 R2 the SQL Server Utility was added which goes even farther and collects diagnostics, metrics, performance data from multiple servers and stores it in a management warehouse for reporting. Imagine being able to see eg storage and query performance for multiple servers, or free space trends for the last X months.
The drawbacks are that historical data needs space. Collecting it also requires adding some stored procedures to all monitored servers, although this is done automatically.
For this kind of thing it's good to have at least one server that has a linked connection to all the servers you need information for. If you do then you can use this little script I just wrote:
-- (1) Create global temp table used to store results
IF OBJECT_ID('tempdb..##databases') IS NOT NULL DROP TABLE ##databases;
CREATE TABLE ##databases
(
serverDBID int identity,
serverName varchar(100),
databaseName varchar(100),
databaseSize decimal(20,6)
);
-- (2) Create and populate table variable used to collect server names
DECLARE #servers TABLE(id int identity, serverName varchar(100));
INSERT #servers(serverName)
SELECT name FROM sys.servers;
-- (3) loop through each DB and collect database names into ##databases
DECLARE #i int = 1, #serverName varchar(100), #db varchar(100), #sql varchar(8000);
WHILE #i <= (SELECT COUNT(*) FROM #servers)
BEGIN
SELECT #serverName = serverName FROM #servers WHERE id = #i;
SET #sql = 'INSERT ##databases(serverName, databaseName) SELECT '''+#serverName+
''', name FROM master.sys.databases';
EXEC (#sql);
SET #i += 1;
END;
-- (4) Collect database sizes
SET #i = 1; -- reset/re-use #i;
WHILE #i <= (SELECT COUNT(*) FROM ##databases)
BEGIN
SELECT #serverName = serverName, #db = databaseName
FROM ##databases
WHERE serverDBID = #i;
SET #sql =
'UPDATE ##databases
SET databaseSize =
(SELECT sum(size)/128. FROM ['+#serverName+'].['+#db+'].sys.database_files)
WHERE serverDBID = '+CAST(#i AS varchar(4))+';'
BEGIN TRY
EXEC (#sql);
END TRY
BEGIN CATCH
PRINT 'There was an error getting dbsize info for '+#serverName+' > '+#db;
END CATCH;
SET #i += 1;
END;
-- Final Output
SELECT * FROM ##databases;

Transaction context in use by another session

I have a table called MyTable on which I have defined a trigger, like so:
CREATE TRIGGER dbo.trg_Ins_MyTable
ON dbo.MyTable
FOR INSERT
AS
BEGIN
SET NOCOUNT ON;
insert SomeLinkedSrv.Catalog.dbo.OtherTable
(MyTableId, IsProcessing, ModifiedOn)
values (-1, 0, GETUTCDATE())
END
GO
Whenever I try to insert a row in MyTable, I get this error message:
Msg 3910, Level 16, State 2, Line 1
Transaction context in use by another session.
I have SomeLinkedSrv properly defined as a linked server (for example, select * from SomeLinkedSrv.Catalog.dbo.OtherTable works just fine).
How can I avoid the error and successfully insert record+execute the trigger?
Loopback linked servers can't be used in a distributed transaction if MARS is enabled.
Loopback linked servers cannot be used in a distributed transaction.
Trying a distributed query against a loopback linked server from
within a distributed transaction causes an error, such as error 3910:
"[Microsoft][ODBC SQL Server Driver][SQL Server]Transaction context in
use by another session." This restriction does not apply when an
INSERT...EXECUTE statement, issued by a connection that does not have
multiple active result sets (MARS) enabled, executes against a
loopback linked server. Note that the restriction still applies when
MARS is enabled on a connection.
http://msdn.microsoft.com/en-us/library/ms188716(SQL.105).aspx
I solve It.
I was using the same linked server to call the second procedure and then into the procedure I was using the same linked server.
It's very Easy, only we have to know the restricctions of linked servers.
I have resolved it by removing linked server used in the stored procedure and then called stored procedure by the same linked server. It wasnt working in DEV environement.
One of causes of this situation is a trigger that works for linked-sever database table. An also SQL version of SQL-Server which processes database matters. To avoid this ERROR during sql query execution we should temporarily disable and after execution enable triggers for tables updated. All with database name check. Here is an example:
Select * From People where PersonId In (#PersonId, #PersonIdRight)
IF 'DOUBLE' = DB_NAME()
ALTER TABLE [dbo].[PeopleSites] DISABLE TRIGGER [PeopleSites_ENTDB_UPDATE]
Update PeopleSites Set PersonId = #PersonIdRight Where PersonId = #PersonId
IF 'DOUBLE' = DB_NAME()
ALTER TABLE [dbo].[PeopleSites] ENABLE TRIGGER [PeopleSites_ENTDB_UPDATE]
Select * From PeopleSites where PersonId In (#PersonId, #PersonIdRight)
I also got the same error in our DEV environemnt, moving the linked databases to another sql instance resolved the issue. In our production environment these databases are already on separate instances
In my case I was using SQL 2005 and got "transaction context in use by another session" when running Insert....exec over a linked server. The fix for me was to patch from SP2 build 3161 to SP3. SP2 cumulative 5 is supposed to fix though.
https://support.microsoft.com/en-us/kb/947486
When remote database sits on the same server,configure the linked server without specifying the database server ip / hostname and port. Just the database name should be sufficient.
I was getting the same "transaction context in use by another session error" when trying to run an UPDATE query:
BEGIN TRAN
--ROLLBACK TRAN
--COMMIT TRAN
UPDATE did
SET did.IsProcessed = 0,
did.ProcessingLockID = NULL
FROM [proddb\production].DLP.dbo.tbl_DLPID did (NOLOCK)
WHERE did.dlpid IN ('bunch of GUIDs')
--WHERE did.DLPID IN (SELECT DLPID FROM #TableWithData)
However I didn't realize I was already trying to run this on the DLP database on the ProdDb\Production server. Once I removed that "[proddb\production].DLP.dbo." prefix from the query, it worked fine.

Creating a T-SQL temp table on another server machine

I'm using SQL Query Analyzer to build a report from the database on one machine (A), and I'd like to create a temp table on a database server on another machine(B) and load it with the data from machine A.
To be more specific, I have a report that runs on machine A (machine.a.com), pulling from schema tst. Using SQL Query Analyzer, I log into the server at machine.a.com and then have access to the tst schema:
USE tst;
SELECT *
FROM prospect;
I would like to create a temp table from this query window, only I'd like it built on another machine (call it machine.b.com). What syntax would I use for this? My guess is something like:
CREATE TABLE machine.b.com.#temp_prospect_list(name varchar(45) Not Null, id decimal(10) Not Null);
And then I'd like to load this new table with something like:
INSERT INTO machine.b.com.#temp_prospect_list VALUES (
USE tst;
SELECT *
FROM prospect; );
The syntax to access a remote server in T-SQL is to fully qualify any table name with the following (brackets included when necessary):
[LinkedServer].[RemoteDatabase].[User].[Table]
So, for example, to run a SELECT statement on one server that accesses a table on another server:
SELECT * FROM [machine.b.com].tst.dbo.table7;

detach database/take offline fails

I'm currently in the process of detaching a development database on the production server. Since this is a production server I don't want to restart the sql service. That is the worst case scenario.
Obviously I tried detaching it through SSMS. Told me there was an active connection and I disconnected it. When detaching the second time it told me that was impossible since it was in use.
I tried EXEC sp_detach_db 'DB' with no luck.
I tried getting the database offline. That ran for about 15 minutes when I got bored and turned it off.
Anyway, I tried everything ... I made sure all connections were killed using the connections indicator in detach database using SSMS.
The following returned 0 results:
USE master
SELECT * FROM sys.sysprocesses WHERE dbid = DB_ID('DB')
And the following is running for 18 minutes now:
ALTER DATABASE DB SET OFFLINE WITH ROLLBACK IMMEDIATE
I did restart SMSS regularly during all this to make sure SSMS wasn't the culprit by locking something invisibly.
Isn't there a way to brute force it? The database schema is something I'm pretty fond of but the data is expendable.
Hopefully there is some sort of a quick fix? :)
The DBA will try to reset the process tonight but I'd like to know the fix for this just in case.
Thx!
ps: I'm using DTC ... so perhaps this might explain why my database got locked up all of a sudden?
edit:
I'm now doing the following which results in an infinite execution of the final part. The first query even returns 0, so I suppose the killing of the users won't even matter.
USE [master]
GO
SELECT * FROM sys.sysprocesses WHERE dbid = DB_ID('Database')
GO
DECLARE #return_value int
EXEC #return_value = [dbo].[usp_KillUsers]
#p_DBName = 'Database'
SELECT 'Return Value' = #return_value
GO
ALTER DATABASE Database SET OFFLINE WITH ROLLBACK IMMEDIATE
GO
How are you connecting to SQL Server? Is it possible that you're trying to detach the database while you yourself are connected to it? This can block a Detach, depending on the version of SQL Server involved.
You can try using the DAC for stuff like this.
Try killing all connections before detaching the database, IE:
USE [master]
GO
/****** Object: StoredProcedure [dbo].[usp_KillUsers] Script Date: 08/18/2009 10:42:48 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[usp_KillUsers]
#p_DBName SYSNAME = NULL
AS
/* Check Paramaters */
/* Check for a DB name */
IF (#p_DBName IS NULL)
BEGIN
PRINT 'You must supply a DB Name'
RETURN
END -- DB is NULL
IF (#p_DBName = 'master')
BEGIN
PRINT 'You cannot run this process against the master database!'
RETURN
END -- Master supplied
IF (#p_DBName = DB_NAME())
BEGIN
PRINT 'You cannot run this process against your connections database!'
RETURN
END -- your database supplied
SET NOCOUNT ON
/* Declare Variables */
DECLARE #v_spid INT,
#v_SQL NVARCHAR(255)
/* Declare the Table Cursor (Identity) */
DECLARE c_Users CURSOR
FAST_FORWARD FOR
SELECT spid
FROM master..sysprocesses (NOLOCK)
WHERE db_name(dbid) LIKE #p_DBName
OPEN c_Users
FETCH NEXT FROM c_Users INTO #v_spid
WHILE (##FETCH_STATUS <> -1)
BEGIN
IF (##FETCH_STATUS <> -2)
BEGIN
SELECT #v_SQL = 'KILL ' + CONVERT(NVARCHAR, #v_spid)
-- PRINT #v_SQL
EXEC (#v_SQL)
END -- -2
FETCH NEXT FROM c_Users INTO #v_spid
END -- While
CLOSE c_Users
DEALLOCATE c_Users
This is a script to kill all user connections to a database, just pass the database name, and it will close them. Then you can try to detach the database. This script is one I found a while back and I cannot claim it as my own. I do not mean this as any sort of plagarism, I just don't have the source.
SELECT DISTINCT req_transactionUOW FROM syslockinfo
KILL 'number_returned' (the one(s) with process_id -2)
The cause was DTC being a little bit annoying and locking up the database completely with a failed transaction. Now I would like to know the reason why this happened. But at least it gives me the ability to reset the broken transactions when the problem re-occurs.
I'm posting it here since I'm sure it'll help some people who are experiencing the same issues.