Can a table valued parameter to stored procedure have default values - sql

Suppose that I have a two column User Defined Table type
CREATE TYPE [Schema].[Type] AS TABLE (
[Name] NVARCHAR (100) NULL
[Value] int NULL
);
Further suppose that I have stored procedure that I pass the table type to
CREATE PROCEDURE [Schema].[sp_SomeProcedure]
#TVP [Type] READONLY
AS
SELECT
[Name]
,1 + [Value]
FROM
#TVP
Can I default the value of #TVP to be
(SELECT 'John', 1)

You can use the 'DEFAULT' keyword for the TVP when calling the procedure. That will pass an empty table of the type.
Example - if the TVP is the second parameter passed to a procedure:
Exec myProcedure (intParam, DEFAULT)

SQL Server does not allow to make a TVP an optional parameter but setting some sort of default value to it, if you have a TVP in your procedure you will need to pass a TVP at runtime.
But there is a way around to it, add another parameter to your procedure which decides the behavior of TVP in your procedure.
Mind you in any case you would need to pass a TVP to your procedure at runtime.
CREATE PROCEDURE [Schema].[sp_SomeProcedure]
#TVP_Default BIT = 1
,#TVP [dbo].[Test_Type] READONLY
AS
BEGIN
DECLARE #tempTable TABLE (
[Name] NVARCHAR(100) NULL
,[Value] int NULL
);
-- If #TVP_Default = 1 default values will be populated
-- else values passed to TVP will be used
IF (#TVP_Default = 1)
BEGIN
INSERT INTO #tempTable([Name] , [Value])
SELECT 'John' , 1
END
ELSE
BEGIN
INSERT INTO #tempTable([Name] , [Value])
SELECT [Name]
,1 + [Value]
FROM #TVP
END
/*
rest of the code
use #tempTable instead of the TVP in rest of the code
*/
END

Related

How to pass the table name as a parameter in SQL and update the table name

For example:
exec SP_ChangeNameOfTable tbl1_data_20200124
SELECT *
INTO #TEMP_DATA
FROM [LOADED].DBO.tbl1_data_20200123
WHERE [Owner] IS NULL
I want to change this table name from 20200123 to 20200124 using a stored procedure.
you need to use dynamic sql while trying to pass dynamic table name in SP.
something like,
create procedure SP_ChangeNameOfTable
(
#table_name as varchar(200)
)
As
begin
declare #sc as varchar(max)='SELECT * into #TEMP_DATA FROM
[LOADED].[dbo].'+#table_name+' where [Owner] is null'
execute(#sc);
end
Call the SP as you like,
exec SP_ChangeNameOfTable 'tbl1_data_20200124'
Demo

Pass user-defined table type "on the fly" into stored procedure

If I have a stored procedure that uses a user-defined table as one of its inputs, is there any way to call the stored procedure without declaring the variable beforehand?
i.e. I have this type:
CREATE TYPE [IDs] AS TABLE
(
[ID] [BIGINT] NULL
)
and this procedure
CREATE PROCEDURE spDoSomething (#IDs IDs)
AS
...
Normally, this would be called by declaring the table variable first:
DECLARE #IDs IDs
INSERT #IDs VALUES (1),(2)
EXEC spDoSomething #IDs
Is there any way to call this in one line, i.e.
EXEC spDoSomething #IDs = (SELECT 1 AS ID UNION ALL SELECT 2)
Use case is that I am accessing the database through a UI, which builds the SQL dynamically and doing a DECLARE #IDs... is not possible

Insert results of a table into stored procedure as parameters

I have a stored procedure which inserts values into a table.
Let's say its name is usp_InsertTableA with parameters #ID int and Name varchar(100).
I have a requirement to call this stored procedure multiple times from another stored procedure. I am thinking to call this stored procedure something like below
exp usp_InsertTableA
select ID, Name from #tempTable
Is this possible in SQL Server to execute this with the value of the table and send it into a stored procedure?
You can use table type parameters to stored procedure.
CREATE TYPE [dbo].[udt_MyCustomTable] AS TABLE(
[id] [int] NOT NULL,
[name] [nvarchar](100) NOT NULL
)
GO
And then you stored procedure would be:
CREATE PROC [dbo].[usp_InsertTableA]
(
#myCustomTable udt_MyCustomTable READONLY
)
AS
BEGIN
-- Your code goes in here
END
Is this possible in SQL Server to execute this with the value of the table and send it into a stored procedure?
No, not with the stored procedure you have there. There are ugly hacks that could make it happen, but it's not how you're supposed to do things in T-SQL. Everything you do in SQL Server is supposed to be optimized to work on a set of rows, not a single row / row by row
In practice what this means is, if you have a query like this that produces 100 rows:
select ID, Name from #tempTable
You would pass those 100 rows to your insert procedure and insert them in one operation:
--expanding on sam's advice
--create a type
CREATE TYPE [dbo].[udt_MyCustomTable] AS TABLE(
[id] [int] NOT NULL,
[name] [nvarchar](100) NOT NULL
)
--your insert procedure
CREATE PROC [dbo].[usp_InsertTableA]
(
#myCustomTable udt_MyCustomTable READONLY
)
AS
BEGIN
INSERT INTO TableA(idcolumn, namecolumn)
SELECT is, name FROM #myCustomTable
END
Now in your main sp that wants to insert 100 rows:
#DECLARE tmpVar udt_MyCustomTable;
--put 100 rows into table variable
INSERT INTO tmpVar(id,name)
select ID, Name from #tempTable
--pass 100 rows in variable to SP to insert all at once
EXECUTE usp_InsertTableA tmpVar
DECLARE #ID INT, #Name VARCHAR(255)
SELECT #ID = ID, #Name=Name FROM #tempTable -- assumes one record in the table.
EXEC dbo.usp_insertdata #id, #Name

SQL Server Simple example for the use of Delimiter in Stored Procedure

Surprisingly there isn't any straight forward example to demonstrate the use of delimiter in Stored Procedure?
I have a string variable as input (with delimiter) and would like to make it as condition in Stored Procedure. Something like below:
CREATE PROCEDURE testing
(
#stringVar NVARCHAR(255) = NULL
)
....
BEGIN
Select * from table where column not in (#stringVar)
.....
END
Sample value for #stringVar will be a~b~c~d
How should I handle such case? Is there any built-in delimiter function in Sql Server?
From SQL Server 2008 on, you can use table valued parameters.
I cannot explain it better than here: http://www.sommarskog.se/arrays-in-sql.html
it is a bit of tricky situation and there is no "Simple way" of doing it. but I can give you the simplest way of doing it.
You will need to do two things to make this work,
1) Create a Split function which takes a parameter of deliminited
string and split these values. 2) Make your stored procedure in a
way that passed deliminited string is passed to that function and then
splited values are passed to the stored procedure
Split Function
CREATE FUNCTION [dbo].[FnSplit]
(
#List nvarchar(2000),
#SplitOn nvarchar(5)
)
RETURNS #RtnValue table (Id int identity(1,1),Value nvarchar(100))
AS
BEGIN
While(Charindex(#SplitOn,#List)>0)
Begin
Insert Into #RtnValue (value)
Select Value = ltrim(rtrim(Substring(#List,1,Charindex(#SplitOn,#List)-1)))
Set #List = Substring(#List,Charindex(#SplitOn,#List)+len(#SplitOn),len(#List))
End
Insert Into #RtnValue (Value)
Select Value = ltrim(rtrim(#List))
Return
END
Your Proc
CREATE PROCEDURE testing
(
#stringVar NVARCHAR(255) = NULL
)
....
BEGIN
Select * from table
where column not IN(SELECT Value
FROM dbo.FnSplit(#stringVar,'~'))
.....
END
There is n't any built-in delimiter function in Sql Server. You can use user defined table value function for this purpose.
Create FUNCTION [dbo].[fnSplitString]
(#pString varchar(max),#pSplitChar char(1))
returns #tblTemp table (tid int,value varchar(1000))
as
begin
declare #vStartPosition int
declare #vSplitPosition int
declare #vSplitValue varchar(1000)
declare #vCounter int
set #vCounter=1
select #vStartPosition = 1,#vSplitPosition=0
set #vSplitPosition = charindex( #pSplitChar , #pString , #vStartPosition )
if (#vSplitPosition=0 and len(#pString) != 0)
begin
INSERT INTO #tblTemp
(
tid ,
value
)
VALUES
(
1 ,
#pString
)
return --------------------------------------------------------------->>
end
set #pString=#pString+#pSplitChar
while (#vSplitPosition > 0 )
begin
set #vSplitValue = substring( #pString , #vStartPosition , #vSplitPosition - #vStartPosition )
set #vSplitValue = ltrim(rtrim(#vSplitValue))
INSERT INTO #tblTemp
(
tid ,
value
)
VALUES
(
#vCounter ,
#vSplitValue
)
set #vCounter=#vCounter+1
set #vStartPosition = #vSplitPosition + 1
set #vSplitPosition = charindex( #pSplitChar , #pString , #vStartPosition )
end
return
end
There is no easy way of this. I would recomend reading this article.
You need to split your string progmaticly. There is no such function in sql.
There are many ways of implementing split functions in SQL Server, with lots of different performance characteristics: some (ab)use the XML functionality of SQL server, some use Tally tables, some use the CLR etc. For a description of one of these methods and a performance comparison, I'd recommend you'd look at Jeff Moden's article on SQL Server Central (registration required).
Once you have a table-valued splitter function available, you can implement your select simply:
select *
from MyTable t
where t.MyColumn not in (select Item from dbo.MySplit(#stringVar, '~') where Item is not null)
;
If it's just for testing existence then:
Select * from table where CHARINDEX('~'+column+'~' , '~'+#stringVar+'~' ,1)=0

How to pass a temp table as a parameter into a separate stored procedure

I have a stored procedure that takes an input parameter #CategoryKeys varchar, and parses its contents into a temp table, #CategoryKeys.
-- create the needed temp table.
CREATE TABLE #CategoryKeys
(
CategoryKey SMALLINT
);
-- fill the temp table if necessary
IF Len(rtrim(ltrim(#CategoryKeys))) > 0
BEGIN
INSERT INTO #CategoryKeys
(CategoryKey)
SELECT value
FROM dbo.String_To_SmallInt_Table(#CategoryKeys, ',');
END
If the temp table has rows, I would like to pass the table into a separate stored procedure. How would I go about creating a parameter in the separate procedure to hold the temp table?
While understanding scoping addresses the direct need, thought it might be useful to add a few more options to the mix to elaborate on the suggestions from the comments.
Pass XML into the stored procedure
Pass a table-valued parameter into the stored procedure
1. Pass XML into the stored procedure
With XML passed into a parameter, you can use the XML directly in your SQL queries and join/apply to other tables:
CREATE PROC sp_PassXml
#Xml XML
AS
BEGIN
SET NOCOUNT ON
SELECT T.Node.value('.', 'int') AS [Key]
FROM #Xml.nodes('/keys/key') T (Node)
END
GO
Then a call to the stored procedure for testing:
DECLARE #Text XML = '<keys><key>1</key><key>2</key></keys>'
EXEC sp_PassXml #Text
Sample output of a simple query.
Key
-----------
1
2
2. Pass a table-valued parameter into the stored procedure
First, you have to define the user defined type for the table variable to be used by the stored procedure.
CREATE TYPE KeyTable AS TABLE ([Key] INT)
Then, you can use that type as a parameter for the stored proc (the READONLY is required since only IN is supported and the table cannot be changed)
CREATE PROC sp_PassTable
#Keys KeyTable READONLY
AS
BEGIN
SET NOCOUNT ON
SELECT * FROM #Keys
END
GO
The stored proc can then be called with a table variable directly from SQL.
DECLARE #Keys KeyTable
INSERT #Keys VALUES (1), (2)
EXEC sp_PassTable #Keys
Note: If you are using .NET, then you can pass the SQL parameter from a DataTable type matching the user defined type.
Sample output from the query:
Key
-----------
1
2
When you create a #TEMP table, the "scope" is bigger than just the procedure it is created in.
Below is a sample:
IF EXISTS
(
SELECT * FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_TYPE = N'PROCEDURE' and ROUTINE_SCHEMA = N'dbo' and ROUTINE_NAME = N'uspProc002'
)
BEGIN
DROP PROCEDURE [dbo].[uspProc002]
END
GO
CREATE Procedure dbo.uspProc002
AS
BEGIN
/* Uncomment this code if you want to be more explicit about bad "wiring" */
/*
IF OBJECT_ID('tempdb..#TableOne') IS NULL
begin
THROW 51000, 'The procedure expects a temp table named #TableOne to already exist.', 1;
end
*/
/* Note, I did not Create #TableOne in this procedure. It "pre-existed". An if check will ensure that it is there. */
IF OBJECT_ID('tempdb..#TableOne') IS NOT NULL
begin
Insert into #TableOne ( SurrogateKey , NameOf ) select 2001, 'Hello-From-uspProc002'
end
END
GO
IF EXISTS
(
SELECT * FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_TYPE = N'PROCEDURE' and ROUTINE_SCHEMA = N'dbo' and ROUTINE_NAME = N'uspProc001'
)
BEGIN
DROP PROCEDURE [dbo].[uspProc001]
END
GO
CREATE Procedure dbo.uspProc001 (
#Param1 int
)
AS
BEGIN
IF OBJECT_ID('tempdb..#TableOne') IS NOT NULL
begin
drop table #TableOne
end
CREATE TABLE #TableOne
(
SurrogateKey int ,
NameOf varchar(12)
)
Insert into #TableOne ( SurrogateKey , NameOf ) select 1001, 'Hello-From-uspProc001'
Select 'before-nested-call' as MyStatus1, * from #TableOne
EXEC dbo.uspProc002
Select 'after-nested-call' as MyStatus1, * from #TableOne
IF OBJECT_ID('tempdb..#TableOne') IS NOT NULL
begin
drop table #TableOne
end
END
GO
exec dbo.uspProc001 0
HAVING SAID THAT, PLEASE DO NOT CODE UP ALOT OF THESE. ITS THE SQL EQUIVALENT OF A GLOBAL VARIABLE AND IT IS DIFFICULT TO MAINTAIN AND BUG PRONE.
Stored proc that uses temp table
CREATE OR ALTER Procedure Engine.TestProcTempTable
AS
BEGIN
--DROP TABLE IF EXISTS #TestProcTempTable ;
SELECT * from #TestProcTempTable;
END
Create put data in to temp table which will be used by SP
DROP TABLE IF EXISTS #TestProcTempTable ;
select * into #TestProcTempTable from <TABLE_NAME>;
execute Engine.TestProcTempTable