I have a table that have a list of "Reports" or "Correspondence Letters" (html files). This list will appear in a dropdown in a website. Some of these items will need a "check" to see if they are allowed to be added to the dropdown.
An example of how this table (I removed columns that was not necessary to display):
In this table there is a column "Cor_PolNeedCheck_ToShow". This value links to a different table where the query for that file is stored.
From here I need to create a query that will build the actual list that will be displayed. But in order to do that I need to run the query that is in the second table to perform the check. I've created the following query to do this and it brings back the expected results:
DECLARE #retvalue INT
DECLARE #Paramater NVARCHAR(20) = '241215'
DECLARE #Cor_GroupCde NVARCHAR(10) = 248
DECLARE #Statement NVARCHAR(500);
SELECT #Statement = (SELECT Lookup_Query + #Paramater FROM [dbo].[Ref_Lookup]
WHERE Lookup_ID = (Select Cor_PolNeedCheck_ToShow FROM dbo.Ref_Correspondence WHERE Cor_Group_Cde = #Cor_GroupCde));
EXEC #retvalue = sp_executesql #Statement;
PRINT #retvalue
This value needs to be passed into a query again to verify each record to show or not to show. This is the query that will run to show the items (I have added the #retvalue of the above in the query just to show what it needs to do). This query is in a stored proc where the #Paramater will be passed (above) from the application and then to be used as needed for the below (as above query needs to fit into this one).
SELECT Cor_Group_Cde, Cor_Desc
FROM Ref_Correspondence
WHERE Cor_Show = 'Y' AND Cor_Prod_List Like '%#' + #ProdID + '#%'
AND (Cor_PolNeedCheck_ToShow IS NULL OR --#retValue > 0)
The problem I'm facing is that I need to get the #retValue into the where clause.
I have though of a stored proc, but a stored proc cannot be called in a where clause. I then though of a user defined function, but the problem there is that you cannot call a stored proc ("sp_executesql") in a function.
Due to company standards I cannot do this within the application either. Is there a way around this or is there a way to do this that I maybe missed with the above?
-------------------- EDIT ---------------------
The function I created looks like this:
USE [DBName]
GO
/****** Object: UserDefinedFunction [dbo].[ufn_CorrespondenceCheckResult] Script Date: 4/11/2019 5:35:04 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[ufn_CorrespondenceCheckResult]
(
#Paramater nvarchar(20),
#Cor_GroupCde nvarchar(10)
)
RETURNS int
AS
BEGIN
DECLARE #Statement nvarchar(MAX);
DECLARE #Return int;
set #Return = 0;
SELECT #Statement = (SELECT Lookup_Query + #Paramater FROM [dbo].[Ref_Lookup]
WHERE Lookup_ID = (Select Cor_PolNeedCheck_ToShow FROM dbo.Ref_Correspondence WHERE Cor_Group_Cde = #Cor_GroupCde));
EXEC #Return = sp_executesql #Statement;
return #Return;
END
Running the above function in my script gives me an error of "Only functions and some extended stored procedures can be executed from within a function.":
SELECT Cor_Group_Cde, Cor_Desc
FROM Ref_Correspondence
WHERE Cor_Show = 'Y' AND Cor_Prod_List Like '%#' + #ProdID + '#%'
AND (Cor_PolNeedCheck_ToShow IS NULL OR ((SELECT [dbo].[ufn_CorrespondenceCheckResult] (#Paramater,#Cor_GroupCde)) > 0))
It would be better to convert your Stored Procedure into a Function, if possible. Then you can use function in WHERE clause.
If you can't convert it into the function then better to execute the SP and store the full result in a Variable. Now you can use This table in WHERE clause.
You should store the result of the Stored Procedure in a variable in then use that in your next statement's condition. You can use an OUTPUT parameter to return the value of from the Stored Procedure.
DECLARE #count INT
EXEC FindProductByModel --CREATE a stored procedure FindProductByModel instead of a function
#Paramater = '241215',
#Cor_GroupCde = '248',
#retvalue= #count OUTPUT --#retval is an output paramater of your stored procedure
SELECT Cor_Group_Cde, Cor_Desc
FROM Ref_Correspondence
WHERE Cor_Show = 'Y' AND Cor_Prod_List Like '%#' + #ProdID + '#%'
AND (Cor_PolNeedCheck_ToShow IS NULL OR #count>0)
Related
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.
Not sure how to implement this, but I need a way to get the current list of parameters for a stored procedure as well as their passed in values (this code will be executed in the stored procedure itself).
I know I can use sys.parameters to get the parameter names, but how to get the actual values?
What I need to do with this is to make a char string of the form
#param_name1=#value1,#param_name2=#value2,...,#param_namen=#valuen
I have tried to use dynamic sql, but not having much joy with that.
Any ideas??
Edit:
Currently I am just going through all the parameters one-by-one to build the string. However I want a "better" way to do it, since there are quite a few parameters. And incase parameters are added later on (but the code to generate the string is not updated).
I tried using dynamic sql but gave up, since the sp_executesql sp requires parameters be passed into it...
You state '(this code will be executed in the stored procedure itself).' so assuming you are in the procedure you will already know the parameter names as you have to declare them when creating your procedure. Just do a select and put the names inside text fields
ALTER PROCEDURE procname
(
#param1 NVARCHAR(255)
,#param2 INT
...
)
SELECT [Parameters] = '#param1=' + #param1
+ ',#param2=' + CONVERT(NVARCHAR(MAX),#param2)...
The CONVERT is there as an example for non-char datatypes.
update
You will need to create a linked server that points to itself to use the OPENQUERY function.
USE [master]
GO
/****** Object: LinkedServer [.] Script Date: 04/03/2013 16:22:13 ******/
EXEC master.dbo.sp_addlinkedserver #server = N'.', #srvproduct=N'', #provider=N'SQLNCLI', #datasrc=N'.', #provstr=N'Integrated Security=SSPI'
/* For security reasons the linked server remote logins password is changed with ######## */
EXEC master.dbo.sp_addlinkedsrvlogin #rmtsrvname=N'.',#useself=N'True',#locallogin=NULL,#rmtuser=NULL,#rmtpassword=NULL
GO
Now you can do something like this cursor to get each parameter name and then use dynamic sql in OPENQUERY to get the value:
DECLARE curParms CURSOR FOR
SELECT
name
FROM sys.parameters
WHERE OBJECT_ID = OBJECT_ID('schema.procedurename')
ORDER BY parameter_id
OPEN curParms
FETCH curParms INTO #parmName
WHILE ##FETCH_STATUS <> -1
BEGIN
SELECT #parmName + '=' + (SELECT * FROM OPENQUERY('linkedservername','SELECT ' + #parmName))
FETCH curParms INTO #parmName
END
CLOSE curParms
DEALLOCATE curParms
Since SQL Server 2014 we have sys.dm_exec_input_buffer, it is a table valued function with an output column event_info that gives the full execution statement (including parameters).
We can parse the param values from sys.dm_exec_input_buffer and get the param names from sys.parameters and join them together to get the string you want.
For example:
create procedure [dbo].[get_proc_params_demo]
(
#number1 int,
#string1 varchar(50),
#calendar datetime,
#number2 int,
#string2 nvarchar(max)
)
as
begin
-- get the full execution statement
declare #statement nvarchar(max)
select #statement = event_info
from sys.dm_exec_input_buffer(##spid, current_request_id())
-- parse param values from the statement
declare #proc_name varchar(128) = object_name(##procid)
declare #param_idx int = charindex(#proc_name, #statement) + len(#proc_name)
declare #param_len int = len(#statement) - #param_idx
declare #params nvarchar(max) = right(#statement, #param_len)
-- create param values table
select value, row_number() over (order by current_timestamp) seq
into #params
from string_split(#params, ',')
-- get final string
declare #final nvarchar(max)
select #final = isnull(#final + ',','') + p1.name + '=' + ltrim(p2.value)
from sys.parameters p1
left join #params p2 on p2.seq = parameter_id
where object_id = ##procid
select #final params
end
To test it:
exec get_proc_params_demo 42, 'is the answer', '2019-06-19', 123456789, 'another string'
Returns the string you want:
#number1=42,#string1='is the answer',#calendar='2019-06-19',#number2=123456789,#string2='another string'
I have something similar wrapped as a UDF. I use it for error logging in catch blocks.
I'm getting a strange error in SSRS, in a report (which gets put into a sproc) with many drop-down parameters:
Query execution failed for dataset 'DataSet1'.
Must pass parameter number 3 and subsequent parameters as '#name = value'. After the form '#name = value' has been used, all subsequent parameters must be passed in the form '#name = value'
I'm lost on what's going on here - what is meant by #name = value .
I searched online, someone mentioned that you should alter the stored-procedure?
Here is how the top half of my stored-proc looks:
USE [FederatedSample]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[prc_RPT_Select_BI_Completes_Data_View_2]
#FromDate DATETIME,
#ToDate DATETIME,
#AccountIDs VARCHAR(max) = null,
#ClientIDs VARCHAR(max) = null,
#SupplierIDs VARCHAR(max) = null,
#CompleteType INT = NULL,
/*
* 0 - Routed
* 1 - Targeted
* 2 - Offerwall
*/
#SourceType BIT = NULL,
/*
* Works if #AccountID is not null
* (should only be used if #AccountID has a single value)
*
* 0 - Owned by #AccountID
* 1 - External (not owned by #AccountID)
*/
#SurveyStatus INT = NULL,
/*
* NULL - All Surveys
* 0 - Completes Approved Surveys
* 1 - Invoiced Surveys
*/
#IsSupplierUser BIT = 0
/*
* used to decide whether to display FEDSurveyName or SupplierSurveyName
*/
AS
BEGIN
SET NOCOUNT ON
DECLARE #SQL NVARCHAR(MAX) = N'',
#Params NVARCHAR(MAX)
IF #AccountIDs is not null
BEGIN
SET #SQL += N'DECLARE #AccountIDs VARCHAR(MAX) = #pAccountIDs; '
END
IF #ClientIDs is not null
BEGIN
SET #SQL += N'DECLARE #ClientIDs VARCHAR(MAX) = #pClientIDs; '
END
IF #SupplierIDs is not null
BEGIN
SET #SQL += N'DECLARE #SupplierIDs VARCHAR(MAX) = #pSupplierIDs; '
END
SET #SQL += N'
SELECT bi.SupplierID as ''Supplier ID''
,bi.SupplierName as ''Supplier Name''
,bi.PID as ''PID''
,bi.RespondentID as ''Respondent ID''
,lk_slt.Name as ''Entry Link Type''
,ts.SurveyNumber as ''Initial Survey ID'''
And later in the stored proc. it does stuff like this to split strings:
IF #AccountIDs is not null
BEGIN
SET #SQL += CHAR(13) + CHAR(9)
SET #SQL += N' and bi.AccountID in (SELECT CAST(val as INT) FROM dbo.Split(#AccountIDs, '','
When invoking a stored procedure, you either can pass the parameters by position (not a good idea) or by Name (a better approach IMHO).
EXEC dbo.MyStoredProcedure '12/31/2012', 1; -- Not a great way to pass parameters
EXEC dbo.MyStoredProcedure #AsOfDate = '12/31/2012', #AccountID = 1; -- A better way
From the error message you are receiving, I suspect that SSRS is using the second approach and is running into an issue with the third parameter being provided to the stored procedure.
Without more information to go off of it is difficult to provide you with an exact explanation for the error (the stored procedure would perhaps help), an educated guess is that the way the parameters are being provided for Account IDs, Client IDs and Supplier IDs isn't quite correct. Specifically, I think the problem might be that you are providing multiple identifiers delimited by a comma.
You might try passing a single Account ID, Client ID and Supplier ID to see if you still receive the error. I would also try to look at the stored procedure (or talk to the DBA \ Developer who wrote it) to ascertain the intended usage of the stored procedure.
I got a similar message when passing a comma instead of a full stop for a decimal value in one of the parameters of a stored procedure.
Here is a simplified example of what happened.
The following command string was erroneously generated with the value of #param2 with a comma instead of a full stop, causing a misunderstanding of the number of parameters.
EXEC myStoredProc #param1 = 1, #param2 = 0,5 ,#param3 = 'something'
There are bunch of databases to the SQL server I am connected.
How should I query the sysobjects in order to spot in what database a stored procedure with name 'myStoredProcedure' is located ?
The query should return the database name.
Thanks
I know you are not asking for this, but I'd really download RedGate's Sql Search add-in for SSMS and use that. It allows you to find any object (proc, table, view, column, etc) on any database easily.
And it's free!
I'd give this a try:
CREATE TABLE ##DatabaseList
(
DatabaseName varchar(50)
)
EXECUTE SP_MSForEachDB 'USE [?]; INSERT INTO ##DatabaseList SELECT DB_NAME() FROM [sys].[objects] WHERE name = "MyStoredProcedure" AND type_desc = "SQL_STORED_PROCEDURE"'
SELECT * FROM ##DatabaseList
DROP TABLE ##DatabaseList
That's using the undocumented/ unsupported system stored procedure SP_MSForEachDb and writing any hits to a global temp table, then outputting the contents to the Results window before dropping the table. If you just need to know which database (or databases - there may of course be more than one) has an appropriately named SP, this should do it. If you want to use the output elsewhere as a parameter, it may take a little more work.
By the way, I'm only learning this stuff myself over the last few months so if anyone can critique the above and suggest a better way to go at it I'm happy to receive feedback. Equally, I can answer any further questions posted here to the best of my ability.
Cheers
So out of curiosity I decided to try write this myself, especially since ADG mentioned his solution was using an unsupported, undocumented procedure. This could also be expanded to take a 2nd parameter so where it checks the type = P (stored Proc) you could probably change it to look for other things like views / tables etc.
My solution is a bit long but here goes:
CREATE PROCEDURE spFindProceduresInDatabases
(
#ProcedureName NVARCHAR(99)
)
AS
BEGIN
-- Get all the database names and put them into a table
DECLARE #Db TABLE (DatabaseName Varchar(99))
INSERT INTO #Db SELECT name FROM Sys.databases
-- Declare a table to hold our results
DECLARE #results TABLE (DatabaseName VARCHAR(99))
-- Make a Loop
-- Declare a variable to be incremented
DECLARE #count INT
SET #count = 0
-- Declare the end condition
DECLARE #endCount INT
SELECT #endCount = COUNT(*) FROM #Db
-- Loop through the databases
WHILE (#count < #endCount )
BEGIN
-- Get the database we are going to look into
DECLARE #dbWeAreChecking VARCHAR(99)
SELECT TOP 1 #dbWeAreChecking = DatabaseName FROM #Db
DELETE FROM #Db WHERE DatabaseName = #dbWeAreChecking
-- Create and execute our query
DECLARE #Query NVARCHAR(3000)
SET #Query = N'SELECT #outParam = COUNT(*) FROM '+#dbWeAreChecking+'.sys.sysobjects WHERE type = ''P'' and name = #ProcedureName'
Declare #outParam INT
print (#Query)
DECLARE #ParmDefinition NVARCHAR(500)
DECLARE #IntVariable INT
SET #ParmDefinition = N'#ProcedureName VARCHAR(99),#outParam INT OUTPUT'
SET #IntVariable = 35
EXECUTE sp_executesql
#Query ,
#ParmDefinition,
#ProcedureName,
#outParam = #outParam OUTPUT
-- If we have a result insert it into the results table
If (#outParam > 0)
BEGIN
INSERT INTO #results(DatabaseName) VALUES(#dbWeAreChecking)
END
-- Increment the counter
SET #count = (#count + 1)
END
-- SELECT ALL OF THE THINGS!!!
SELECT * FROM #results
END
Helo,
My question is I have one Stored Procedure in SQL Server that returns counts of a field. I want to store the results of this Stored Procedure in a variable (scalar?) of a different stored procedure.
sp_My_Other_SP:
CREATE PROCEDURE [dbo].sp_My_Other_SP
#variable int OUTPUT -- The returned count
AS
BEGIN -- SP
SET NOCOUNT ON;
SET #SQL = "SELECT COUNT(*) FROM blah"
EXEC(#SQL)
END -- SP
I currently do it like:
DECLARE #count int
EXEC sp_My_Other_SP #count OUTPUT
Then I use it like
IF (#count > 0)
BEGIN
...
END
However its returning the other Stored Procedure results as well as the main Stored Procedure results which is a problem in my .NET application.
-----------
NoColName
-----------
14
-----------
MyCol
-----------
abc
cde
efg
(Above is an attempted representation of the results sets returned)
I would like to know if there is a way to store the results of a Stored Procedure into a variable that doesn't also output it.
Thanks for any help.
You can capture the results of the stored procedure into a temp table so it is not returned by the calling stored procedure.
create table #temp (id int, val varchar(100))
insert into #temp
exec sp_My_Other_SP #value, #value, #value, #count OUTPUT
Well, the easiest way to fix this is to recode the stored proc so that the select statement that returns the 'other' result set you don't want in this case is conditionally extecuted, only when you are NOT asking for the count
Add another parameter called #GetCount
#GetCount TinyInt Defualt = 0 // or
#GetCount Bit Default = 0
Then
instead of just
Select ...
write
If #GetCount = 1
Select ...
Have you tried changing
SET #SQL = "SELECT COUNT(*) FROM blah"
EXEC(#SQL)
to
SELECT #variable = COUNT(*) FROM blah"
-- don't do EXEC(#SQL)
?
THE FIRST PROCEDURE:
CREATE PROC DD43
#ID INT OUTPUT AS
(SELECT #ID=COUNT(*) FROM CS2)
SECOND PROCEDURE:
CREATE PROC DD45 AS
DECLARE #COUNT INT
DECLARE #COUN INT
EXEC DD43 #COUN OUT --CALLING THE FIRST PROCEDURE
SET #COUNT= (SELECT #COUN)
SELECT #COUNT
EXEC DD45