Concat param name to loop over params - sql

I have a procedure that accepts multiple varchar(4000) params (26 of them).
Each of them is a comma-separated string of values.
Once they are passed in, I would like to break each of the strings apart and insert them into a temp table for use later in the proc.
I'd prefer not to write a statement that processes each parameter individually, but rather write a while loop that relies on a counter to loop over each parameter and process each one in turn. Currently, I've tried the following, but its not correct.
CREATE PROCEDURE [dbo].[myproc] (
#string1 varchar(4000) = null;
#string2 varchar(4000) = null;
#string3 varchar(4000) = null;
....declare #string4 -> #string25...
#string26 varchar(4000) = null;)
CREATE TABLE #emails (
address varchar(80)
)
Set #counter = 1
WHILE #counter < 27
BEGIN
INSERT INTO #emails(address) SELECT element as address from FT_SPLIT_LIST(isNull('#string'+convert(varchar,#counter),''),',')
SET #counter = #counter +1
END
SELECT * FROM #emails
Currently, this is not returning a table with all the CSVs from #string1 -> #string26.
FT_SPLIT_LIST works- I use it in many other places. I just need to know if there is a way to dynamically declare the parameter that is being passed in to it?
Is there any way to do what I'm trying to accomplish without writing a statement for each of the #string1->#string27 parameters?
Thanks,
C

SQL Server 2008 and above have table valued parameters:
Table-valued parameters are declared by using user-defined table types. You can use table-valued parameters to send multiple rows of data to a Transact-SQL statement or a routine, such as a stored procedure or function, without creating a temporary table or many parameters.
These are a much better option than comma delimited varchars and FT_SPLIT_LIST.
I suggest reading Arrays and Lists in SQL Server 2008 Using Table-Valued Parameters by Erland Sommarskog for a comprehensive discussion on this topic.

Related

How to represent dynamic query in SQL Server view

I have the following SQL code:
DECLARE #i INT = 1;
DECLARE #sql_code varchar(max) = '';
DECLARE #repeats INT = 4;
WHILE #i <= #repeats
BEGIN
SET #sql_code = #sql_code+'SELECT ''foo'+cast(#i as varchar)+''' as bar UNION ALL '
SET #i = #i + 1
END;
SET #sql_code = LEFT(#sql_code,LEN(#sql_code) - 10)
exec (#sql_code)
,which when run in SSMS produces this:
bar
----
foo1
foo2
foo3
foo4
How can I reproduce the same result as view (dynamically)?
I know you can't use declarations in view, but could it be done through stored procedure or function?
You can't use dynamic sql inside a view. But yes you can create table valued User-Defined functions as mentioned in this post.
Link to the post: https://social.msdn.microsoft.com/Forums/sqlserver/en-US/3cdeda6c-af19-46e9-b89f-e575fecd475b/dynamic-query-in-view?forum=transactsql
Answer by Gavin Campbell should give you the idea of what can be done.
Note : For more information on Table valued User-Defined Functions: Visit this documentation:
https://learn.microsoft.com/en-us/previous-versions/sql/sql-server-2008-r2/ms191165(v=sql.105)?redirectedfrom=MSDN
Actually, despite what Utsav's good answer says, you can do anything if you put your mind to it. 😉
While this is generally not recommended (this answer is for informational purposes), there are certain use cases where it makes sense to use OPENQUERY() inside a View. OPENQUERY() allows you to execute raw SQL against a remote or local SQL Server. Either in the raw SQL itself, or probably more organized in a stored procedure, there's essentially no limitations on the queries you can run, including dynamic SQL.
Example:
CREATE PROCEDURE dbo.RunSomeDynamicSQL
AS
DECLARE #DynamicSQL NVARCHAR(MAX) =
'
SELECT 1 AS Foobar;
';
EXEC sp_executesql #DynamicSQL;
GO
CREATE VIEW dbo.SomeViewThatExecutesDynamicSQL
AS
SELECT Foobar
FROM OPENQUERY
(
LocalServerName,
'
EXEC YourDatabaseName.dbo.RunSomeDynamicSQL
WITH RESULT SETS
((
Foobar INT
));
'
);
GO
SELECT Foobar
FROM dbo.SomeViewThatExecutesDynamicSQL;
You'll notice I'm using the WITH RESULT SETS keyword when executing my procedure inside of OPENQUERY(). This is because OPENQUERY() needs to know the shape of the result set from the executing query. This is one way to describe that when executing a procedure.
One use case for using OPENQUERY() in a View is so you can maximize your ability to performance tune your query (e.g. inside a stored procedure) without losing consumability of the database object.
One important fact about using OPENQUERY() is that the SQL Server Engine always estimates the cardinality of the results to be 10,000 rows. This means if your result set is much larger than 10,000 rows, for example 10 million rows, then you may not get the most optimal execution plan to serve your query.
Also, despite my informational answer, you can of course use a stored procedure alone if that's sufficient for your use case.
could it be done through stored procedure
Sure, just wrap a stored procedure around your code:
CREATE PROCEDURE dbo.SomeStoredProcedure
AS
DECLARE #i INT = 1;
DECLARE #sql_code NVARCHAR(MAX) = '';
DECLARE #repeats INT = 4;
WHILE #i <= #repeats
BEGIN
SET #sql_code = #sql_code+'SELECT ''foo'+cast(#i as varchar)+''' as bar UNION ALL '
SET #i = #i + 1
END;
SET #sql_code = LEFT(#sql_code,LEN(#sql_code) - 10)
EXEC sp_executesql #sql_code
Note I changed the last line of your code to use sp_executesql because it minimizes your risk for SQL injection issues. You should always use that procedure for dynamic SQL execution instead of directly executing your SQL string.

query inside the variable

Is it possible in SQL to use a variable to store query.
For example to save time when subquery is used multiple times inside the main query.
Example:
DECLARE #my_query as varchar(250) = select x from my_table where my_table = y.your_table
SELECT
a,b,c,(#my_query),d,e,f
FROM my_table_1
Is it possible in SQL to use a variable to store query.
Depend on your definition of "query". If you mean store the text which we use to execute the command, then the answer is YES. If you mean an object type query, then the answer is not - since there is no data type that fit this.
What I mean is that a variable can store a value which is string. The string can be any query command that you want. Therefore, you can store for example the text "select col1,col2 from table1".
Next you need to ask how can we use this text in order to execute it as part of a query, which is done using dynamic query.
We can execute a text of a query using the build-in stored procedure sp_executesql, which is build for such needs.
For example:
-- DECLARE VARIABLE
DECLARE #MyQuery NVARCHAR(MAX)
-- SET the value of the variable
SET #MyQuery = 'SELECT ''Yes I can'''
-- Executing a dynamic query
EXECUTE sp_executesql #MyQuery
Here is another example which look more close to your question:
-- First let's create a table
CREATE TABLE T(ID INT)
INSERT T(ID) VALUES (1),(2)
GO
-- And here is what you sked about:
-- DECLARE VARIABLE
DECLARE #MyQuery NVARCHAR(MAX)
-- SET the value of the variable
SET #MyQuery = 'select ID from T where ID = ''1'''
-- Let's combine the text to a full query now
DECLARE #FullQuery NVARCHAR(MAX)
SET #FullQuery = '
SELECT
ID,(' + #MyQuery + ')
FROM T
'
PRINT #FullQuery
-- Executing a dynamic query
EXECUTE sp_executesql #FullQuery
NOTE! Your specific sample of query will return error, which is not related to the question "Is it possible in SQL to use a variable to store query". This is a result of the "query" is not well formatted.
Important! It is HIGHLY recommended to read the document about this stored procedure and learn a bit more of the options it provides us.
https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-executesql-transact-sql?view=sql-server-ver15

how evaluate an arithmetic expression within a SQL scalar function

i am trying to execute this scalar function and i tried a lot of approaches to achieve this but i get stuck
Create FUNCTION CalculateElementFunc()
RETURNS int
AS
BEGIN
DECLARE #ResultVar numeric(18,6)
DECLARE #eq nvarchar(MAX)
set #eq = '7.5/100*1258.236'
declare #expression nvarchar(max)
set #expression = #eq
declare #result int
declare #SQLString nvarchar(max)
Set #SQLString = N'Select #result = #expression'
exec sp_executesql #SQLString, N'#expression nvarchar(100)',
#expression,
#result = #result output
select #ResultVar = #result
if( #ResultVar <> ROUND( #ResultVar, 2 ,1))
set #ResultVar = cast( ROUND( #ResultVar, 2 ,1) + .01 as numeric(18,2))
RETURN #ResultVar
END
When i try to execute it
select dbo.CalculateElementFunc()
i get this error
Msg 557, Level 16, State 2, Line 1
Only functions and some extended stored procedures can be executed from within a function.
Please Advice
What you want to do is not recommended in SQL Server. First, it is really hard. As you have learned, a SQL Server function cannot execute dynamic SQL.
This is subtly in the documentation:
EXECUTE statements calling extended stored procedures.
exec and sp_executesql are not extended stored procedures.
What can you do? Here are some options:
Is a stored procedure instead of a UDF a possibility? Stored procedures can execute the dynamic SQL.
Can you get around the problem of expression evaluation? Perhaps dynamic SQL can be used one level up in your code.
You can execute an extended stored procedure that starts another transaction and executes the dynamic SQL. Think: really bad performance.
You can write a CLR extended function.
Limitations on SQL User Defined Functions:
Non-deterministic build in functions cannot be used in user defined functions. e.g. GETDATE() or RAND().
XML data type is not supported.
Dynamic SQL queries are not allowed.
User defined functions does not support any DML statements (INSERT, UPDATE, DELETE) unless it is performed on Table Variable.
We cannot make a call to the stored procedure. Only extended stored procedure can be called from function.
We cannot create Temporary tables inside UDFs.
It does not support Error Handling inside UDF. Although, we can handle errors (RAISEERROR, TRY-CATCH) for the statements which uses this function.
And it looks like you are using/calling a stored procedure inside your User Defined Function. It is not the expression that's bugging you, it's that stored procedure call.
Try to replace it with some logic to achieve your desired output.
Hope this is helpful. If it helps to solve your problem then don't forget to mark it as an answer.

how to concatenate varying stored procedure parameters

please help me with writing this search sql stored procedure
procedure may have different number of parameters at different time
so could any body help me with writing this query. I don't know how to concatenate parameters.
i am new to stored procedure
CREATE PROCEDURE searchStudent
-- Add the parameters for the stored procedure here
#course int=null,
#branch int=null,
#admissionYear varchar(max)=null,
#passingYear varchar(max)=null,
#userName varchar(max)=null,
#sex varchar(max)=null,
#studyGap varchar(max)=null,
#firstName varchar(max)=null,
#lastName varchar(max)=null
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE query STR DEFAULT null
IF #course IS NOT NULL
THEN query=
SELECT * FROM [tbl_students] WHERE
END
GO
please complete the query so that it can have parameters which are having values and can search from database on the basis of parameters value. But parameter may vary every time depends on search criteria.
You would probably need to use Dynamic SQL to achieve this. First of all I would highly recommend reading this excellent article. http://www.sommarskog.se/dynamic_sql.html
You're dynamic sql would be something like this;
Declare #query varchar(max)
Set #query = 'Select * From dbo.MyTable Where '
If #Course Is Not Null
Begin
Set #query = #query + 'Course = ' + Convert(varchar(10), #Course)
end
If #Branch Is Not Null
Begin
Set #query = #query + ' and Branch = ' + Convert(varchar(10), #Branch )
end
This is only an example! You will need to build in some checks to ensure that you have one (and only one) Where clause, you must ensure that the integer values are converted to string values correctly. You must also check that the parameters don't have any special characters that could break the dynamic sql - like an apostrophe (')
Using dynamic SQL can be painful and very difficult to get right.
Good luck!
The key with a dynamic search conditions is to make sure an index is used, instead of how can I easily reuse code, eliminate duplications in a query, or try to do everything with the same query. Here is a very comprehensive article on how to handle this topic:
Dynamic Search Conditions in T-SQL by Erland Sommarskog
It covers all the issues and methods of trying to write queries with multiple optional search conditions. This main thing you need to be concerned with is not the duplication of code, but the use of an index. If your query fails to use an index, it will preform poorly. There are several techniques that can be used, which may or may not allow an index to be used.
here is the table of contents:
Introduction
The Case Study: Searching Orders
The Northgale Database
Dynamic SQL
Introduction
Using sp_executesql
Using the CLR
Using EXEC()
When Caching Is Not Really What You Want
Static SQL
Introduction
x = #x OR #x IS NULL
Using IF statements
Umachandar's Bag of Tricks
Using Temp Tables
x = #x AND #x IS NOT NULL
Handling Complex Conditions
Hybrid Solutions – Using both Static and Dynamic SQL
Using Views
Using Inline Table Functions
Conclusion
Feedback and Acknowledgements
Revision History
Sorry, I am having trouble understanding what you are asking. Do you mean the consumer of the sproc may specify some arbitrary subset of the parameters and you want to filter on those?
Assuming the above you have 2 options.
1.
use a where clause something like this:
WHERE ([tbl_students].firstName = ISNULL(#firstname,firstName)
AND ([tbl_students].lastName = ISNULL(#lastName ,lastName )
etc.
What this does is check if your parameter has a value, and, if so, it will compare it to the column. If the param is null, then it will compare the column to itself, which will never filter anything out.
use dynamic sql in your sproc and just include the line of the where clause you want if the param is not null.

Can I create a One-Time-Use Function in a Script or Stored Procedure?

In SQL Server 2005, is there a concept of a one-time-use, or local function declared inside of a SQL script or Stored Procedure? I'd like to abstract away some complexity in a script I'm writing, but it would require being able to declare a function.
Just curious.
You can create temp stored procedures like:
create procedure #mytemp as
begin
select getdate() into #mytemptable;
end
in an SQL script, but not functions. You could have the proc store it's result in a temp table though, then use that information later in the script ..
You can call CREATE Function near the beginning of your script and DROP Function near the end.
Common Table Expressions let you define what are essentially views that last only within the scope of your select, insert, update and delete statements. Depending on what you need to do they can be terribly useful.
I know I might get criticized for suggesting dynamic SQL, but sometimes it's a good solution. Just make sure you understand the security implications before you consider this.
DECLARE #add_a_b_func nvarchar(4000) = N'SELECT #c = #a + #b;';
DECLARE #add_a_b_parm nvarchar(500) = N'#a int, #b int, #c int OUTPUT';
DECLARE #result int;
EXEC sp_executesql #add_a_b_func, #add_a_b_parm, 2, 3, #c = #result OUTPUT;
PRINT CONVERT(varchar, #result); -- prints '5'
The below is what I have used i the past to accomplish the need for a Scalar UDF in MS SQL:
IF OBJECT_ID('tempdb..##fn_Divide') IS NOT NULL DROP PROCEDURE ##fn_Divide
GO
CREATE PROCEDURE ##fn_Divide (#Numerator Real, #Denominator Real) AS
BEGIN
SELECT Division =
CASE WHEN #Denominator != 0 AND #Denominator is NOT NULL AND #Numerator != 0 AND #Numerator is NOT NULL THEN
#Numerator / #Denominator
ELSE
0
END
RETURN
END
GO
Exec ##fn_Divide 6,4
This approach which uses a global variable for the PROCEDURE allows you to make use of the function not only in your scripts, but also in your Dynamic SQL needs.
In scripts you have more options and a better shot at rational decomposition. Look into SQLCMD mode (SSMS -> Query Menu -> SQLCMD mode), specifically the :setvar and :r commands.
Within a stored procedure your options are very limited. You can't create define a function directly with the body of a procedure. The best you can do is something like this, with dynamic SQL:
create proc DoStuff
as begin
declare #sql nvarchar(max)
/*
define function here, within a string
note the underscore prefix, a good convention for user-defined temporary objects
*/
set #sql = '
create function dbo._object_name_twopart (#object_id int)
returns nvarchar(517) as
begin
return
quotename(object_schema_name(#object_id))+N''.''+
quotename(object_name(#object_id))
end
'
/*
create the function by executing the string, with a conditional object drop upfront
*/
if object_id('dbo._object_name_twopart') is not null drop function _object_name_twopart
exec (#sql)
/*
use the function in a query
*/
select object_id, dbo._object_name_twopart(object_id)
from sys.objects
where type = 'U'
/*
clean up
*/
drop function _object_name_twopart
end
go
This approximates a global temporary function, if such a thing existed. It's still visible to other users. You could append the ##SPID of your connection to uniqueify the name, but that would then require the rest of the procedure to use dynamic SQL too.
Just another idea for anyone that's looking this up now. You could always create a permanent function in tempdb. That function would not be prefixed with ## or # to indicate it's a temporary object. It would persist "permanently" until it's dropped or the server is restarted and tempdb is rebuilt without it. The key is that it would eventually disappear once the server is restarted if your own garbage collection fails.
The scope of the function would be within TempDB but it could reference another database on the server with 3 part names. (dbname.schema.objectname) or better yet you can pass in all the parameters that the function needs to do its work so it doesn't need to look at other objects in other databases.