I have a stored procedure that returns rows:
CREATE PROCEDURE MyProc
AS
BEGIN
SELECT * FROM MyTable
END
My actual procedure is a little more complicated, which is why a stored procedure is necessary.
Is it possible to select the output by calling this procedure?
Something like:
SELECT * FROM (EXEC MyProc) AS TEMP
I need to use SELECT TOP X, ROW_NUMBER, and an additional WHERE clause to page my data, and I don't really want to pass these values as parameters.
You can
create a table variable to hold the
result set from the stored proc and
then
insert the output of the
stored proc into the table variable,
and then
use the table variable
exactly as you would any other
table...
... sql ....
Declare #T Table ([column definitions here])
Insert #T Exec storedProcname params
Select * from #T Where ...
You can use a User-defined function or a view instead of a procedure.
A procedure can return multiple result sets, each with its own schema. It's not suitable for using in a SELECT statement.
You either want a Table-Valued function or insert your EXEC into a temporary table:
INSERT INTO #tab EXEC MyProc
You need to declare a table type which contains the same number of columns your store procedure is returning. Data types of the columns in the table type and the columns returned by the procedures should be same
declare #MyTableType as table
(
FIRSTCOLUMN int
,.....
)
Then you need to insert the result of your stored procedure in your table type you just defined
Insert into #MyTableType
EXEC [dbo].[MyStoredProcedure]
In the end just select from your table type
Select * from #MyTableType
You must read about OPENROWSET and OPENQUERY
SELECT *
INTO #tmp FROM
OPENQUERY(YOURSERVERNAME, 'EXEC MyProc #parameters')
It is not necessary use a temporary table.
This is my solution
SELECT * FROM
OPENQUERY(YOURSERVERNAME, 'EXEC MyProc #parameters')
WHERE somefield = anyvalue
You can copy output from sp to temporaty table.
CREATE TABLE #GetVersionValues
(
[Index] int,
[Name] sysname,
Internal_value int,
Character_Value sysname
)
INSERT #GetVersionValues EXEC master.dbo.xp_msver 'WindowsVersion'
SELECT * FROM #GetVersionValues
drop TABLE #GetVersionValues
Try converting your procedure in to an Inline Function which returns a table as follows:
CREATE FUNCTION MyProc()
RETURNS TABLE AS
RETURN (SELECT * FROM MyTable)
And then you can call it as
SELECT * FROM MyProc()
You also have the option of passing parameters to the function as follows:
CREATE FUNCTION FuncName (#para1 para1_type, #para2 para2_type , ... )
And call it
SELECT * FROM FuncName ( #para1 , #para2 )
You can cheat a little with OPENROWSET :
SELECT ...fieldlist...
FROM OPENROWSET('SQLNCLI', 'connection string', 'name of sp')
WHERE ...
This would still run the entire SP every time, of course.
If 'DATA ACCESS' false,
EXEC sp_serveroption 'SQLSERVERNAME', 'DATA ACCESS', TRUE
after,
SELECT * FROM OPENQUERY(SQLSERVERNAME, 'EXEC DBNAME..MyProc #parameters')
it works.
Use OPENQUERY, and before execute set SET FMTONLY OFF; SET NOCOUNT ON;
Try this sample code:
SELECT top(1)*
FROM
OPENQUERY( [Server], 'SET FMTONLY OFF; SET NOCOUNT ON; EXECUTE [database].[dbo].[storedprocedure] value,value ')
If you get the error 'Server is not configured for DATA ACCESS',
use this:
EXEC sp_serveroption 'YourServer', 'DATA ACCESS', TRUE
For the sake of simplicity and to make it re-runnable, I have used a system StoredProcedure "sp_readerrorlog" to get data:
-----USING Table Variable
DECLARE #tblVar TABLE (
LogDate DATETIME,
ProcessInfo NVARCHAR(MAX),
[Text] NVARCHAR(MAX)
)
INSERT INTO #tblVar Exec sp_readerrorlog
SELECT LogDate as DateOccured, ProcessInfo as pInfo, [Text] as Message FROM #tblVar
-----(OR): Using Temp Table
IF OBJECT_ID('tempdb..#temp') IS NOT NULL DROP TABLE #temp;
CREATE TABLE #temp (
LogDate DATETIME,
ProcessInfo NVARCHAR(55),
Text NVARCHAR(MAX)
)
INSERT INTO #temp EXEC sp_readerrorlog
SELECT * FROM #temp
It sounds like you might just need to use a view. A view allows a query to be represented as a table so it, the view, can be queried.
If your server is called SERVERX for example, this is how I did it...
EXEC sp_serveroption 'SERVERX', 'DATA ACCESS', TRUE;
DECLARE #CMD VARCHAR(1000);
DECLARE #StudentID CHAR(10);
SET #StudentID = 'STUDENT01';
SET #CMD = 'SELECT * FROM OPENQUERY([SERVERX], ''SET FMTONLY OFF; SET NOCOUNT ON; EXECUTE MYDATABASE.dbo.MYSTOREDPROC ' + #StudentID + ''') WHERE SOMEFIELD = SOMEVALUE';
EXEC (#CMD);
To check this worked, I commented out the EXEC() command line and replaced it with SELECT #CMD to review the command before trying to execute it! That was to make sure all the correct number of single-quotes were in the right place. :-)
I hope that helps someone.
Related
I have a user-defined table type tyAnalysisNumbers. I need to populate my user defined data type within a stored procedure with a SELECT statement and I am struggling to get that working within my stored procedure.
The following ways I have tried do not work
DECLARE #MyTable tyAnalysisNumbers;
INSERT INTO #MyTable
EXEC ('SELECT * FROM ' + #someTable);
I get this error:
An INSERT EXEC statement cannot be nested
I am unsure how to insert into my custom table via a select statement.
Can anyone help me accomplish this?
An INSERT EXEC statement cannot be nested
Above error is self explanatory. Please look at below scenario:
For example, we have one procedure which inserts data in table type and return result.
CREATE PROCEDURE uspInsertData1
AS
BEGIN
DECLARE #MyTable tyAnalysisNumbers;
INSERT INTO #MyTable
EXEC ('SELECT * FROM someTable');
select * from #MyTable
END
Now, let's say we have another procedure which will call above procedure and again insert data in another table.
CREATE PROCEDURE uspInsertData2
AS
BEGIN
DECLARE #MyTable tyAnalysisNumbers;
INSERT INTO sometable
EXEC uspInsertData1
END
Now, if you execute 1st procedure it will work fine but if you execute second procedure you will get this error.
An INSERT EXEC statement cannot be nested.
Because now you have nested EXEC statements.
I suggest to finish your work in single stored procedure if possible.
Try it like this:
DECLARE #MyTable tyAnalysisNumbers;
SELECT * INTO #Temp FROM #MyTable;
DECLARE #tblName AS SYSNAME = (SELECT name FROM sys.tables WHERE name = #someTable);
EXEC ('INSERT INTO #Temp SELECT * FROM ' + #tblName);
This also addresses the SQL Injection problem.
I have a simple stored procedure with one parameter #Name which I want to replace with another variable.
I am actually looking for SQL injection character and if name contains -- then it should replace it with blank. The stored procedure shown below, it is executing without an error, but not replacing the string for example let is say user searches for EXEC John'''select * FROM TEST2 -- which has SQL injection statement in it
CREATE PROCEDURE GetStudentDetails
#Name nvarchar(300)
AS
BEGIN
SET NOCOUNT ON;
SELECT #Name = REPLACE(#Name ,'--','');
SET #Name = REPLACE(#Name ,'--','');
SELECT *
FROM TABLENAME
WHERE Name LIKE N'%'+ #Name +'%'
END
Updated stored procedure:
CREATE PROCEDURE GetStudentDetails
#Name nvarchar(300)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SafeSearchItem nvarchar(30);
SELECT #SafeSearchItem = REPLACE(#Name ,N'--',N'')
SET #SafeSearchItem = REPLACE(#Name ,N'--',N'')
SELECT *
FROM TABLENAME
WHERE Name LIKE N'%'+ #SafeSearchItem +'%'
END
EXEC
EXEC John'''select * FROM TEST2 --
In the second stored procedure, I am always able to inject SQL - not sure it is my system?
As it stands, we can't answer the question, as, well there isn't a question applicable for information we're been provided. There is no risk of injection in the SP we have, thus, there is not answer on how to avoid it.
Anyway, instead, what i'm going to do is show firstly why that SP isn't subject to injection and then change it so it would be, and how the limited "fix" in it could easily be avoided.
Firstly, let's start with a simple table and data (I strongly suggest running any following scripts in a Sandbox environment!):
USE Sandbox;
GO
CREATE TABLE InjectionReady (ID int IDENTITY(1,1), SomeText varchar(500));
INSERT INTO InjectionReady
VALUES ('Here is some text'),
('Life is like a box a chocolates'),
('Milk Chocolate is my favourite'),
('Cheese is dairy product'),
('Chocolate is a dairy product'),
('Cows say "moo"!'),
('English Cat says "Meow"'),
('Japanese Cat says "Nyaa"');
GO
OK, and now let's create your SP (amended for our object). and then do some tests:
CREATE PROCEDURE NonInjectionSearch #Wildcard nvarchar(100) AS
SELECT #Wildcard = REPLACE(#Wildcard ,N'--',N'');
SET #Wildcard = REPLACE(#Wildcard ,N'--',N'');
SELECT *
FROM InjectionReady
WHERE SomeText LIKE N'%'+ #Wildcard +N'%';
GO
EXEC NonInjectionSearch 'Chocolate';
EXEC NonInjectionSearch '''; DROP TABLE InjectionReady;--';
EXEC NonInjectionSearch '''; DROP TABLE InjectionReady; SELECT ''';
No injection. Great! Ok, now for an SP that could suffer injection:
CREATE PROCEDURE InjectionSearch #Wildcard nvarchar(100) AS
SELECT #Wildcard = REPLACE(#Wildcard ,N'--',N'');
SET #Wildcard = REPLACE(#Wildcard ,N'--',N'');
DECLARE #SQL nvarchar(MAX);
SET #SQL = N'
SELECT *
FROM InjectionReady
WHERE SomeText LIKE N''%'+ #Wildcard + N'%'';'; --Yes, intentional non parametrisation
PRINT #SQL;
EXEC (#SQL);
GO
EXEC InjectionSearch 'Chocolate';
GO
EXEC InjectionSearch '''; CREATE TABLE Injection1(ID int);--'; --This'll fail
GO
EXEC InjectionSearch '''; CREATE TABLE Injection2(ID int); SELECT '''; --Oh! This worked!
GO
So, how could you avoid this? Well, Parametrise your dynamic SQL:
CREATE PROCEDURE ParamSearch #Wildcard nvarchar(100) AS
DECLARE #SQL nvarchar(MAX);
SET #SQL = N'
SELECT *
FROM InjectionReady
WHERE SomeText LIKE N''%'' + #pWildCard +''%'';'; --Yes, intentional non parametrisation
PRINT #SQL;
EXEC sp_executesql #SQL, N'#pWildcard nvarchar(500)', #pWildCard = #Wildcard;
GO
EXEC ParamSearch 'Chocolate';
GO
EXEC ParamSearch '''; CREATE TABLE Injection1(ID int);--'; --Won't inject
GO
EXEC ParamSearch '''; CREATE TABLE Injection2(ID int); SELECT '''; --Oh! this didn't inject either
Dynamic objects bring another level to this, however, I'll only cover this if required; as it stands (like I said at the start) the question asked can't happen for the scenario we have.
Clean up:
DROP TABLE Injection2;
DROP PROC ParamSearch;
DROP PROC InjectionSearch;
DROP PROC NonInjectionSearch;
DROP TABLE InjectionReady;
Is there any good way to do this, or am I just heading in the wrong direction? I would like to create a stored procedure inside an SQL script. I would like to have variables declared at the beginning of the script so that I can create the SPROCs to use in different contexts/servers.Here is what I would like to do (I know this obviously doesn't work, but I'm looking for any ideas of an alternative)..
DECLARE #golbalValue = 'SomeValue'
GO
CREATE PROCEDURE [dbo].[MyStoredProcedure](
AS
BEGIN
SELECT * FROM Mytable WHERE MyCol = #globalValue
END
GO
What you could do is use a scalar function for the variable
create function f ()
returns varchar(20)
as
begin
return 'some value'
end
go
then use it in your procedure
create proc p ()
as
begin
select *
from my_table
where col = f()
end
go
another possibility which is perhaps more appropriate is to use sqlcmd here's an example.
From what I understand, you need to create stored procedures with set value from your parameters. You don't want input parameters in the stored Procedures though. Second, you want to switch database contexts. So I think you'll need a tempTable for your parameters and some dynamic SQL. Try this out:
IF OBJECT_ID('tempdb..#globalParam') IS NOT NULL
DROP TABLE #globalParam;
IF OBJECT_ID('AdventureWorks2012.dbo.myTable') IS NOT NULL
DROP TABLE AdventureWorks2012.dbo.myTable
IF OBJECT_ID('Master..myTable') IS NOT NULL
DROP TABLE Master..mytable
--Create your data tables
SELECT 'SomeValue' AS col1 INTO AdventureWorks2012.dbo.myTable;
SELECT 1000 AS col1 INTO master.dbo.myTable;
CREATE TABLE #globalParam(
ParamName VARCHAR(100),
val SQL_VARIANT --SQL_Variant is designed to hold all data types.
);
--Here are your globalParams
DECLARE #globalParam1 VARCHAR(100) = 'SomeValue';
DECLARE #globalParam2 INT = 1000;
--Load your parameters into a table. Might have to cast some of your parameters to SQL_Variant
INSERT INTO #globalParam
VALUES ('globalParam1',#globalParam1),
('globalParam2',CAST(#globalParam2 AS sql_variant));
GO
--Switch database context
USE AdventureWorks2012
GO
--Variable to hold CREATE PROC
DECLARE #sql VARCHAR(MAX);
--Set #SQL with parameter value from #globalParam
SELECT #sql =
'CREATE PROCEDURE dbo.myStoredProc AS
BEGIN
SELECT * FROM myTable WHERE col1 = ''' + CAST(val AS VARCHAR(100)) + '''
END'
FROM #globalParam
WHERE ParamName = 'globalParam1'
--Execute to create the stored procedure
EXEC(#sql)
--Execute it to see if it works
EXEC dbo.myStoredProc
--Switch context. Repeat same steps
USE master
GO
DECLARE #sql VARCHAR(MAX);
SELECT #sql =
'CREATE PROCEDURE dbo.myStoredProc AS
BEGIN
SELECT * FROM myTable WHERE col1 = ''' + CAST(val AS VARCHAR(100)) + '''
END'
FROM #globalParam
WHERE ParamName = 'globalParam2'
EXEC(#sql)
EXEC dbo.myStoredProc
--Cleanup
DROP PROCEDURE dbo.myStoredProc;
USE AdventureWorks2012
GO
DROP PROCEDURE dbo.myStoredProc;
You cannot do what you want. T-SQL doesn't have the concept of global variables. One method is to store values in a "global" table and then reference them as needed. Something like:
create table GlobalParams (
name varchar(255) not null primary key,
value varchar(255) not null
);
create procedure . . .
begin
. . .
declare #value varchar(255);
select #value = value from Globalparams where name = 'name';
select *
from Mytable
where MyCol = #value;
. . .
end;
Note: this is a simplistic example that only allows variables whose type is a string.
You can also wrap the logic in a user-defined function, so the call looks like:
select *
from Mytable
where MyCol = udf_GlobalLookup('name');
It is rather rare to need global parameters that are shared among different stored procedures. Such a global context can be useful, at times, for complex systems. It is unlikely that you need all this machinery for a simple application. An alternative method, such as just passing the parameters in as arguments, is probably sufficient.
I know the preferred method for returning scalar values from stored procs is either using RETURN or an OUTPUT parameter. But lets say that I have a stored proc that returns the value using a select statement:
CREATE PROC spReturnNumber AS
SELECT 1
Is it possible to get this value from within another stored proc?
CREATE PROC spCheckNumber AS
EXEC spReturnNumber -- <-- get the return value here?
Clarification: I need a solution that doesn't require using an OUTPUT parameter, or using RETURN to return the value.
Thanks in advance.
You could use insert-exec to store the result of a stored procedure in a table:
declare #t table (col1 int)
insert #t exec spReturnNumber
return (select col1 from #t)
The definition of the table has to match the result set of the stored procedure.
Use an OUTPUT parameter instead of (or in addition to, if this procedure is used by other applications) the SELECT.
ALTER PROCEDURE dbo.spReturnNumber
#Number INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SET #Number = 1;
SELECT #Number;
END
GO
CREATE PROCEDURE dbo.spCheckNumber
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Number INT;
EXEC dbo.spReturnNumber #Number = #Number;
SELECT #Number;
END
GO
If you can't change the original procedure, but you know its output will remain static, you could use a #temp table.
CREATE PROCEDURE dbo.spCheckNumber
AS
BEGIN
SET NOCOUNT ON;
CREATE TABLE #n(i INT);
INSERT #n(i) EXEC dbo.spReturnNumber;
DECLARE #Number INT;
SELECT #Number = i FROM #n;
END
GO
You can't get the SELECT value from "parent" procedure but you can get the return value like this:
CREATE PROC A AS
BEGIN
DECLARE #ret int
EXEC #ret = spReturnNumber
RETURN #ret
END
If you are unable to change the proc being called .. place the result set in a temp table [or table variable]:
CREATE TABLE #results (val INT)
DECLARE #someval int
INSERT #results
EXEC dbo.spCheckNumber
SELECT #someval =val from #results
Let say I have a simple Stored Procedure:
ALTER PROCEDURE [dbo].[myProc]
AS
BEGIN
SELECT * FROM myTable
END
How can I do a WHERE statement in Microsoft SQL Server Management Studio to the stored procedure? Something like that:
SELECT * FROM myProc WHERE x = 'a'; -- But that doesn't work...
It sounds like you're trying to make a "dynamic" stored procedure.
Something you might want to do is:
1) Insert the contents of your stored procedure into a temporary table
2) Use dynamic sql to apply a where condition to that temporary table.
Something like:
declare #as_condition varchar(500); --Your condition
create table #a
(
id bigint
)
insert into #a
execute sproc
declare #ls_sql varchar(max);
set #ls_sql = "select * from #a where " + #as_condition;
execute (#ls_sql);
SQL Server allows you to use INSERT INTO to grab a stored procedure's output. For example, to grab all processes with SPID < 10, use:
create table #sp_who (
spid smallint,
ecid smallint,
status nchar(30),
loginame nchar(128),
hostname nchar(128),
blk char(5),
dbname nchar(128),
cmd nchar(16),
request int)
insert into #sp_who execute sp_who
select * from #sp_who where spid < 10
You can't add a WHERE clause to a stored procedure like this.
You should put the clause in the sproc, like this:
ALTER PROCEDURE [dbo].[myProc]
#X VARCHAR(10)
AS
BEGIN
SELECT * FROM myTable WHERE x=#X
END
GO
The syntax for calling a stored procedure is through the use of EXECUTE not SELECT(e.g.):
EXECUTE dbo.myProc 'a'
I think you can't do that.
The command to execute a stored procedure is EXECUTE.
See some more examples of the EXECUTE usage.
I think its better to use a view or a table valued function rather than the suggested approach. Both allow you to pass parameters to the function
If you want the WHERE clause to be something you can "turn off" you can do this, passing in a predetermined value (e.g. -1) if the WHERE limitation is to be bypassed:
ALTER PROCEDURE [dbo].[myProc]
#X VARCHAR(10)
AS
BEGIN
SELECT * FROM myTable WHERE x=#X or #X = -1
END
GO
You must declare a variable in the store procedure which will be necessary to pass to run the stored procedure. Here is an example. Keep this in mind: Before AS you can simply declare any variable by using the # character, but after the AS you must write Declare to declare any variable, e.g., Declare #name nvarchar (50).
ALTER PROCEDURE [dbo].[myProc]
#name varchar (50)
AS
BEGIN
SELECT * FROM myTable
where name= #name
END