SELECT specific columns from EXEC stored procedure - sql

I am having an issue trying to SELECT specific columns from an EXEC statement on a stored procedure. I am trying to find the COUNT(*) that the stored procedure returns which I am successfully doing with :
INSERT INTO #temp
EXEC dbo.my_sp
SET #count = (SELECT COUNT(*) FROM #temp)
DELETE FROM #temp
However, this only works if the columns returned match specifically with the table columns and since I am trying to find the count of many different stored procedures (each of which return different columns), I cannot use this method without creating a new table for each stored procedure.
Is there a way I can SELECT specific columns from the EXEC dbo.my_sp?

Create a loopback linked server to the local instance, making sure that data access is enabled. Let's say you have a local named instance called YourServer\SQL2008:
USE [master];
GO
EXEC sp_addlinkedserver
#server = N'LoopbackLocal',
#srvproduct = N'',
#provider = N'SQLNCLI',
#datasrc = N'.\SQL2008',
#catalog = N'tempdb';
GO
EXEC sp_serveroption
#server = N'LoopbackLocal',
#optname = N'collation compatible',
#optvalue = N'true';
GO
EXEC sp_serveroption
#server = N'LoopbackLocal',
#optname = N'data access',
#optvalue = N'true';
GO
EXEC sp_addlinkedsrvlogin
#rmtsrvname = N'LoopbackLocal',
#locallogin = NULL ,
#useself = N'True';
GO
-- you may need to configure other security here
Then you can use OPENQUERY to run a stored procedure as if it were an ad hoc query.
SELECT COUNT(*)
FROM OPENQUERY
(
LoopbackLocal,
'EXEC dbo.my_sp'
) AS y;
Now, if dbo.my_sp dumps information into a #temp table first, you're going to have issues, since it is no longer a block of code that OPENQUERY can process. For example, if you try to execute sp_who2 this way, at least in SQL Server 2012, you will get an error from sp_describe_first_result_set which OPENQUERY has been altered to use (so maybe this works for you now, I don't have 2008 to test, but it will be an issue someday):
Msg 11526, Level 16, State 1, Procedure sp_describe_first_result_set, Line 1
The metadata could not be determined because ... uses a temp table.
If you're going to be doing this a lot, however, why not make specialized stored procedures (or add options to these ones) such that only a count is returned?

Would ##ROWCOUNT work for you?
if OBJECT_ID('SomeProc') is null
exec ('create procedure dbo.SomeProc as select 1 as SomeValue union all select 2 as SomeValue;')
exec dbo.SomeProc
Select ##ROWCOUNT as RowsAffected
http://technet.microsoft.com/en-us/library/ms187316(v=sql.105).aspx

Related

Dynamically run SQL queries

I want to run SQL queries which retrieve from a database according to user input.
Lets assume there is a table named Queries and the fields of the table are Index, Query, Name.
query - select * from Student
name - GetStudents
Index - 1
When user clicks a button an index will be passed into the server and a query match with that index will be triggered. Assume there are no inputs into queries.
Lets say there are 5 rows in the table and when user pass 3,the third query will be run. When user pass 4 the fourth query will be run.
I think I can simply do this by storing the query as a string on table retrieving the query and run. But I'm not sure whether this workaround is efficient.
Please help me with these points.
Is this approach is okay or is there any better workaround that I can follow.
Is it okay to store query as a string in a table.
Is there any workaround that I can create Stored Procedures pragmatically using asp.net in SQL server management studio.
I'm using ASP.Net and SQL server.
Note that here I can't use Stored Procedures to do this task. Because there is another front-end where user can insert queries into table that I have mentioned above. User has no access to use SQL server management studio.
In theory, yes you certainly could store the query string and then use sp_executesql to run that particular query string.
However, CAUTION. If you have a front end that allows a user to write and submit a query then how are you sanitizing that input? Is there anything to prevent the user submitting 'DROP DATABASE' as the query or event introducing other SQL injection attacks?
A better approach would be to create the procedures (assuming that the activities are all standard tasks) and allowing the user to select which procedure to execute.
You could check the integrity of the dynamic select statement by executing it under the most restrictive security context (a dbuser that has readonly permissions). To take it a bit further, you could also wrap the dynamic select statement into an ever changing dynamic container/string (ever changing part + dynamic query + ever changing part) and suppress any errors that happen during the validation/integrity check.
You cannot rely solely on sanitization because you'll end up in a never-ending catching-up struggle.
use mydbxyz;
go
--create a readonly dbuser
create user readonlydbuser without login;
alter role db_datareader add member readonlydbuser;
alter role db_denydatawriter add member readonlydbuser;
go
--procedure to execute dynamic select (no cte, no variables, just selects)
create or alter procedure execute_simpleselect #sqlinput nvarchar(max) = null
with execute as owner
as
begin
set nocount on;
if nullif(#sqlinput, '') is null
begin
--nothing to execute
return;
end
--check if sql input is a valid/simple select query
declare #foocontrol tinyint;
declare #tblalias sysname = quotename(newid());
declare #sqlcheck nvarchar(max) = N'
select #var = 1;
begin transaction;
begin try
select top (0) #var = '+ #tblalias + '.mycol
from
(
select 1 as mycol
where exists
(
'
+ #sqlinput +
'
)
) as '+ #tblalias + N'
end try
begin catch
select #var = 2;
end catch
rollback transaction;
';
/*
create user readonlydbuser without login;
alter role db_datareader add member readonlydbuser;
alter role db_denydatawriter add member readonlydbuser;
*/
--catch errors
begin try
--change context to a readonlyuser
execute as user='readonlydbuser'; --if this dbuser does not exist, nothing executes
exec sys.sp_executesql #stmt = #sqlcheck, #params = N'#var tinyint output', #var = #foocontrol output;
end try
begin catch
--do nothing, suppress errors
end catch
revert;
--if #foocontrol is not 1, the query cannnot be executed (syntactically incorrect, violation under minimal permissions etc)
if isnull(#foocontrol, 2) = 2
begin
raiserror('what are you trying to do?', 16, 1);
return;
end
--change to the callers security context
exec as caller;
exec sys.sp_executesql #stmt = #sqlinput;
end
--test
exec execute_simpleselect #sqlinput = 'select * from sys.objects';
exec execute_simpleselect #sqlinput = 'create table dbo.testtbl(id int)';
exec execute_simpleselect #sqlinput = 'drop table dbo.tablexzy';
exec execute_simpleselect #sqlinput = 'select user_name()';

How to know the last database before a procedure (in another database) was called

The caller of the procedure who is in database AAA:-
use AAA
exec BBB.dbo.ap_MyProc
The procedure being called which is in database BBB:-
use BBB
create procedure ap_MyProc as
print 'We want a way to return the database name AAA'
Any advice would be appreciated.
Well i can now answer my own question. We can exploit syslockinfo...
declare #db_name varchar(255)
select
#db_name = db_name(rsc_dbid)
from
master.dbo.syslockinfo
where
req_spid = ##SPID
and req_ownertype = 4 --"exSession"
and rsc_dbid <> db_id()
select #db_name = isnull(#db_name, db_name())
print #db_name
Works in SQL 2008 as well. :)
Can your stored procedure be edited? If yes, I think you can edit the stored procedures and add one more parameter for the database name.
And during calling the stored procedure, you should pass the db_name() into the new parameter, so that your stored procedure could know which database is calling it
DECLARE #ServerName varchar(50) = db_name()
EXEC [dbo].[SP] #newParams = #ServerName

Return Table Stored Procedure

Currently work with multiple servers and need to return the results in a table, how can I do?
I intend to get data from each bank and return all through a single procedure.
Example, caught available space of the server and return in table format.
You must be in .NET
First create the links to other server
Example:
USE [master]
GO
EXEC master.dbo.sp_addlinkedserver
#server = N'SecondServer',
#srvproduct=N'SQL Server' ;
GO
EXEC master.dbo.sp_addlinkedsrvlogin
#rmtsrvname = N'SecondServer',
#locallogin = NULL ,
#useself = N'True' ;
GO
Then you can create procedure that can query multiple server.
Example:
CREATE PROCEDURE procedureName
(
#dateFrom datetime,
#dateTo datetime
)
AS
BEGIN
SELECT * FROM CurrentServerName.DatabaseName.TableName
UNION
SELECT * FROM SecondServerName.DatabaseName.TableName;
END

Execute sybase stored procedure as linked server procedure sql server 2008

EDIT
The final goal is to call a stored procedure hosted in sybase with input and output parameters from SQL Server 2008 via Linked Server
I think title is pretty clear.
My goal is to execute a stored procedure hosted in Sybase SQL Anywhere 8 in SQL Server 2008 through the linked server I already created.
Any SQL query made through the linked server is working.
In addition I was able to execute a function but I don't now how to get the return value like that
EXEC ('CALL "dbname"."procedurename"(''param1'', ''param2'', ''param3'')') AT LinkedServerAlias;
Thanks 4 all your help!
Mauro
can you use four part naming convention?
like
exec LinkedServerName.dbname.dbo.procedurename #param1, #param2, #param3
I was finally able to do it by calling
SELECT * FROM OPENQUERY([LinkedServer], 'SELECT "dbname"."spname"(#p1,#p2, #p3)')
I'll add comments and example as soon as I experiment it.
4 part object names are valid only for SQL Server linked servers.
You have to have your EXEC inside an OPENQUERY
SELECT * FROM OPENQUERY([LinkedServer], 'EXEC MyDB.MyScheme.MyProc.spname #p1, #p2, #p3')
Now, you can't parametrise OPENQUERY calls so you have use dynamic SQL
DECLARE #sql nvarchar(4000), #linkedsql nvarchar(4000)
SET #sql = 'EXEC MyDB.MyScheme.MyProc.spname ' + CAST(#p1value as int) + ...
SET #linkedsql = 'SELECT * FROM OPENQUERY(LinkedServer, ''' + #sql + ''')'
EXEC (#linkedsql)

Execute stored proc with OPENQUERY

I have SQL Server 2008 with a linked Sybase server and I am trying to execute a stored procedure on the Sybase server using OPENQUERY. If I have a stored proc that doesn't take parameters it succeeds fine. If I have a stored proc with parameters it fails. I even tried a very basic stored proc that only took an int an that still failed. Below is the syntax I am using:
select * from
OPENQUERY([LINKSERVER],'exec database.user.my_stored_proc ''AT'',''XXXX%'',''1111'',1')
Msg 7357, Level 16, State 2, Line 3
Cannot process the object "exec database.user.my_stored_proc 'AT','XXXX%','1111',1". The OLE DB provider "ASEOLEDB" for linked server "LINKSERVER" indicates that either the object has no columns or the current user does not have permissions on that object.
As the proc will execute just fine without parameters, I don't think it is a permission issue.
This worked for me,
SELECT * FROM OPENQUERY(LOCALSERVER, 'SET FMTONLY OFF EXEC snr.dbo.GetAllSignals #controlRunId = 25, #experimentRunId = 26')
I was creating temporary tables, and that's why i got access denied
Here is more info http://www.sommarskog.se/share_data.html#OPENQUERY
I create a sp that doesn't return any value and it doesn't work.
Your SP in mysql have to return a value!
for example I do this in "mysql":
CREATE DEFINER=`root`#`localhost` PROCEDURE `MyPro`(IN `Name` VARCHAR(50), IN `Id` INT, OUT `Result` INT)
MODIFIES SQL DATA
BEGIN
DECLARE Result INT;
SET Result = 0;
INSERT into MyTable (Id,Name) VALUES(Id,Name);
SELECT Result;
END
That "Id" and "Name" is input parameter and "Result" is output parameter
and create linked server in SQL SERVER and call it like this:
select * from openquery
(
Test,'call mydb.MyPro(''Name'',''16'', #P0);'
)
It works for me :D
Linked Servers and OPENQUERY, Gems to MS SQL Server...that are wolves in sheep clothing. I've found the following solutions to work when dealing with parameters
If the SP is basically just SELECT statements, the move the same to a VIEW and just pass SQL statements via OPENQUERY.
Build the OPENQUERY as a string and then use execute_sql.
You could also see if it works to precede exec with SET FMTONLY ON:
OPENQUERY([LINKSERVER],'SET FMTONLY ON; exec database.user.my_stored_proc ''AT'',''XXXX%'',''1111'',1')
If you try this and it works, you should probably Google FMTONLY+OPENQUERY to get an idea of what it means.
Try this,
SELECT * FROM OPENQUERY(linked_server_name, 'SELECT postgres_procedure_name (parameters)');
I experienced a very similar issue, but my SP wasn't taking any parameters.
I tried experimenting with altering the query sent through the openquery to include 'SET NOCOUNT ON' and 'SET FMTONLY OFF' but this had no difference.
The only solution that worked for my stored procedure was dropping the existing version, and altering the code to specifically 'SET NOCOUNT ON'
After doing this I was able to successfully run my stored proc through my linked server connection.
First of all you have to add hard code text fields then you have to
replace it by your parameters value like FromDate,TillDate,EmpID,CompCode,0,DeptID,DesgId,LocationID,AtnType
DECLARE #startdate varchar(255) = '2019-12-17'
DECLARE #enddate varchar(255) = '2019-12-17'
Set #SQL = 'SELECT * FROM OPENQUERY(' + quotename(#LinkedServer) + ',' + '''' +
'SET FMTONLY OFF; exec [TAP].[dbo].[GetAttendanceList] ' + 'FromDate,TillDate,EmpID,CompCode,0,DeptID,DesgId,LocationID,AtnType,1'')'
You have to replace your parameters values shown below
set #SQL=REPLACE(#SQL,'FromDate',+''''+''''+#startdate+''''+'''')
set #SQL=REPLACE(#SQL,'TillDate',+''''+''''+#enddate+''''+'''')
set #SQL=REPLACE(#SQL,'CompCode',+''''+''''+#CompCode+''''+'''')
set #SQL=REPLACE(#SQL,'AtnType',+''''+''''+''''+'''')
if #EmpID is Null
begin
set #SQL=REPLACE(#SQL,'EmpID','null')
end
if #DeptID is Null
begin
set #SQL=REPLACE(#SQL,'DeptID','null')
end
if #DesgId is Null
begin
set #SQL=REPLACE(#SQL,'DesgId','null')
end
if #LocationID is Null
begin
set #SQL=REPLACE(#SQL,'LocationID','null')
end
print #SQL
exec ( #SQL)