Pass a user-defined table as a select statement to a Stored Procedure or Function [duplicate] - sql

Using SQL Server 2012, is it possible to eliminate the need to declare a table-valued parameter (TVP) just to pass it into a stored procedure? Below is a really simple example of a stored procedure (SP) that takes a TVP and a working example to execute that SP where I have to declare the TVP, populate it and then pass it into the SP. I would like to be able to simply pass in the population criteria directly to the EXEC call. Is this possible?
Scenario Setup:
-- Create a sample Users table
CREATE TABLE Users (UserID int, UserName varchar(20))
INSERT INTO Users VALUES (1, 'Bob'), (2, 'Mary'), (3, 'John'), (4, 'Mark')
-- Create a TVP Type
CREATE TYPE UserIdTableType AS TABLE (UserID int)
-- Create SP That Uses TVP Type
CREATE PROCEDURE GetUsers
#UserIdFilter UserIdTableType READONLY
AS
SELECT * FROM #UserIdFilter WHERE UserID > 2
Working Method to Execute:
DECLARE #MyIds AS UserIdTableType
INSERT INTO #MyIds SELECT UserID FROM Users
EXEC GetUsers #MyIds
Requested Method to Execute:
EXEC GetUsers (SELECT UserID FROM Users)

No, you cannot create a TVP inline or CAST / CONVERT it. It is not a "Data Type" like INT, VARCHAR, DATETIME, etc.; it is a "Table Type" which is entirely different. The User-Defined Table Type (UDTT) is just meta-data that is used as the definition/schema for the declaration of a Table Variable. When such a Table Variable is used as an input parameter, that usage is considered a TVP (Table-Valued Parameter). But the thing is still a Table Variable which has its definition stored in tempdb. This is a physical structure, not a memory structure, and you can't CAST or CONVERT a Table, whether it is real, temporary, or a variable.
While the example given in the Question is simplistic for the sake of just getting the idea across, it does seem like your overall goal is code-reuse / creating subroutines (else you could have easily done SELECT * FROM Users WHERE UserID > 2). Unfortunately T-SQL doesn't allow for really elegant / clean code, so you will have to accept a certain level of repetition and/or clunkiness.
It is possible, however, to make slightly generic handlers for result sets, provided they at least have the required fields. You could either
pass in an XML parameter, or
dump the results to a temp table and just refer to it in the sub-proc call (doesn't need to be dynamic SQL) and hence no need to pass in any parameter (at least not one for the dataset / results / query)
In both of those cases, the structure is more flexible than using a TVP since the TVP has to be those exact fields. But referencing a temp table that is assumed to exist allows for something similar to the following:
Proc_1
SELECT *
INTO #MyTemp
FROM sys.tables;
EXEC dbo.Proc_4 #StartsWith = 'a', #HowMany = 10;
Proc_2
SELECT *
INTO #MyTemp
FROM sys.columns;
EXEC dbo.Proc_4 #StartsWith = 'bb', #HowMany = 20;
Proc_3
SELECT *
INTO #MyTemp
FROM sys.views;
EXEC dbo.Proc_4 #StartsWith = 'ccc', #HowMany = 33;
Proc_4
SELECT TOP (#HowMany) tmp.*
FROM #MyTemp tmp
WHERE tmp.[name] LIKE #StartsWith + '%'
ORDER BY tmp.[object_id] ASC;

Related

How to pass declared table to procedure as parameter?; How to access inside procedure the result cursor of another procedure?

I want to pass a declared table (or a cursor) to a procedure (or a function) as a parameter.
For example, in the following code, I want to pass the declared table temp_table to the procedure calculate_something_2 as a parameter.
I want to get inside a procedure the result cursor of another procedure.
For example, in the following code, I want to get inside calculate_something_1 the result cursor of calculate_something_2.
I have seen similar things being done in other databases, but is it possible to do any of these in HSQLDB?
I have looked at the documentation but could not find any way to do it.
The following code is the basic idea of what I want to achieve. However, it does not work.
create procedure calculate_something_1(in data int)
modifies sql data
dynamic result sets 1
begin atomic
declare table temp_table (id int, quantity int);
insert into temp_table values
(1, 10),
(2, 20),
(3, 30);
declare result_cursor cursor for
select id from (call calculate_something_2(temp_table)) where id > 10;
open result_cursor;
end;
create procedure calculate_something_2(in t table)
modifies sql data
dynamic result sets 1
begin atomic
declare result_cursor cursor for
select id from t where id > 5;
open result_cursor;
end;
It is not possible to pass cursors or tables among PROCEDUREs.
You can define the shared tables as CREATE GLOBAL TEMPORARY TABLE and reference them in different procedures.

Reading Table Type Output in HANA

How can I read the content of an out table type parameter of a procedure in SAP HANA SQL Script?
Sample Procedure:
create procedure "MYSCHEMA".ReturnTypeTest(out OUTPUT_TABLE "MYSCHEMA"."RESOUT")
as
begin
create local temporary table #temp ("COL1" bigint, "COL2" bigint, "COL3" bigint);
insert into #temp values(1, 2, 3);
insert into #temp values(4, 5, 6);
insert into #temp values(7, 8, 9);
OUTPUT_TABLE = select * from #temp;
drop table #temp;
end;
Table Type (Out Parameter):
create type "MYSCHEMA"."RESOUT" as table ("COL1" bigint, "COL2" bigint, "COL3" bigint);
When I call the procedure as below, it displays entire content in SAP HANA Studio's result pane but how can I get it programmatically?
call "MYSCHEMA"."RETURNTYPETEST"(?);
Output variables from procedures can only be assigned to variables in an SQLScript context.
An exception to this is the default resultset that gets bound to the last SELECT command executed in the procedure.
If your intention is to produce something that can be SELECTed, you may want to use a table typed user defined function (TUDF) instead.
Two comments to your example code:
using temporary tables is not a good idea if performance is of concern for your application. While imperative code often appears
to be more intuitive, it really tends to block parallelism during
statement execution.
It's very (too) easy to overload a single procedure function wise by including data manipulation, computation and resultset
returns. If possible, rather opt for smaller functional units and
split up the functionality into multiple objects.
Ok, after you clarified that you actually just want to access the resultset in SQLScript and not in plain SQL, I can add this to my answer:
Check what I wrote in the first sentence! You can simply assign any output variable from a procedure to a corresponding variable.
The documentation has examples on that HANA documentation: CALL.
For example, if your output structure is a table that contains user information it may look like this:
DECLARE uaccounts TABLE (USERID bigint, USERNAME NVARCHAR(256), CREATED date);
DECLARE expdate date := current_date;
/* In this example the procedure 'get_expired_useraccounts_by date' has got
the IN parameter expiry_date (date) and
the OUT parameter expired_accounts (table structure).
By assigning the variable uaccounts to the OUT parameter, the result set
automatically gets bound to uaccounts.*/
call get_expired_useraccounts_by_date (:expdate, :uaccounts);
/* from here you can use :uaccounts like a table variable*/
SELECT count(*) FROM :uaccounts;
All this is, of course, part of the reference documentation and the developer guides...
Could you please check following SQLScript
declare lt_list "MYSCHEMA"."RESOUT";
call "MYSCHEMA"."RETURNTYPETEST"(lt_list);
select * from :lt_list;
This should display the output parameter table using the last SELECT statement
The answer, after understanding the context with Lars' Q&A, is: define a table variable in your caller procedure code
DECLARE temp TABLE (n int);
DECLARE temp MY_TABLE_TYPE;
Then assign the output param of the callee to it.
https://help.sap.com/viewer/de2486ee947e43e684d39702027f8a94/2.0.01/en-US/ea5065d06d14426799d879234d8e3e7b.html
You can query the system views for metadata
Please check following SQLScript Select
select table_type_schema, table_type_name, *
from PROCEDURE_PARAMETERS
where
schema_name = UPPER('MYSCHEMA') and
procedure_name = UPPER('ReturnTypeTest') and
parameter_name = UPPER('OUTPUT_TABLE')
I hope it helps

sp_executesql with user defined table type not working with two databases [duplicate]

I'm using SQL Server 2008.
How can I pass Table Valued parameter to a Stored procedure across different Databases, but same server?
Should I create the same table type in both databases?
Please, give an example or a link according to the problem.
Thanks for any kind of help.
In response to this comment (if I'm correct and that using TVPs between databases isn't possible):
What choice do I have in this situation? Using XML type?
The purist approach would be to say that if both databases are working with the same data, they ought to be merged into a single database. The pragmatist realizes that this isn't always possible - but since you can obviously change both the caller and callee, maybe just use a temp table that both stored procs know about.
I don't believe it's possible - you can't reference a table type from another database, and even with identical type definitions in both DBs, a value of one type isn't assignable to the other.
You don't pass the temp table between databases. A temp table is always stored in tempdb, and is accessible to your connection, so long as the connection is open and the temp table isn't dropped.
So, you create the temp table in the caller:
CREATE TABLE #Values (ID int not null,ColA varchar(10) not null)
INSERT INTO #Values (ID,ColA)
/* Whatever you do to populate the table */
EXEC OtherDB..OtherProc
And then in the callee:
CREATE PROCEDURE OtherProc
/* No parameter passed */
AS
SELECT * from #Values
Table UDTs are only valid for stored procs within the same database.
So yes you would have to create the type on each server and reference it in the stored procs - e.g. just run the first part of this example in both DBs http://msdn.microsoft.com/en-us/library/bb510489.aspx.
If you don't need the efficency you can always use other methods - i.e. pass an xml document parameter or have the s.p. expect a temp table with the input data.
Edit: added example
create database Test1
create database Test2
go
use Test1
create type PersonalMessage as TABLE
(Message varchar(50))
go
create proc InsertPersonalMessage #Message PersonalMessage READONLY AS
select * from #Message
go
use Test2
create type PersonalMessage as TABLE
(Message varchar(50))
go
create proc InsertPersonalMessage #Message PersonalMessage READONLY AS
select * from #Message
go
use Test1
declare #mymsg PersonalMessage
insert #mymsg select 'oh noes'
exec InsertPersonalMessage #mymsg
go
use Test2
declare #mymsg2 PersonalMessage
insert #mymsg2 select 'oh noes'
exec InsertPersonalMessage #mymsg2
Disadvantage is that there are two copies of the data.
But you would be able to run the batch against each database simultaneously.
Whether this is any better than using a table table is really down to what processing/data sizes you have - btw to use a temp table from an s.p. you just access it from the s.p. code (and it fails if it doesn't exist).
Another way to solve this (though not necessarily the correct way) is to only utilize the UDT as a part of a dynamic SQL call.
USE [db1]
CREATE PROCEDURE [dbo].[sp_Db2Data_Sync]
AS
BEGIN
/*
*
* Presumably, you have some other logic here that requires this sproc to live in db1.
* Maybe it's how you get your identifier?
*
*/
DECLARE #SQL VARCHAR(MAX) = '
USE [db2]
DECLARE #db2tvp tableType
INSERT INTO #db2tvp
SELECT dataColumn1
FROM db2.dbo.tblData td
WHERE td.Id = ' + CAST(#YourIdentifierHere AS VARCHAR) '
EXEC db2.dbo.sp_BulkData_Sync #db2tvp
'
EXEC(#SQL)
END
It's definitely not a purist approach, and it doesn't work for every use case, but it is technically an option.

SELECT Query selecting values based on a value in another table

I have 2 tables
Account(AccountId, Encoding)
DeviceAccountMap(AccountId, DeviceId)
Now I need to fetch the devices from the DeviceAccountMap. I pass a list of AccountId to a stored procedure and while fetching the DeviceId from the DeviceAccountMap table I need to compare the Encoding value for each account with a particular value.
Which is the easy way to do this? I am totally lost.
The select clause in the stored procedure will look something like this:
DECLARE #Accounts [usp].[Array]
and [usp].[Array] is defined as below
CREATE TYPE [usp].[Array] AS TABLE
(
Value VARCHAR(36) NULL
)
SELECT
DeviceId,
AccountEncoding = A.Encoding
FROM
usp.DeviceControllerAccountMap DCAM
INNER JOIN
usp.Account A ON (DCAM.AccountId = A.AccountId)
WHERE
DCAM.AccountId IN (SELECT Value From #AccountIds)
AND DCAM.IsShared = 1
AND AccountEncoding LIKE A.Encoding + '.%'
In other words I need to fetch the encoding value for each account and use that in this where clause.
So you can look up information on Table-Valued Parameters (TVPs) in T-SQL.
Here is an article by Erland Sommarskog.
You can refer to this StackOverflow answer to see an example of C# code calling a stored procedure that uses a TVP. I believe TVPs require SQL Server 2008 or higher.
TVPs, as far as I understand, provide a way to make your own data type in sql server that gets treated as if it was a table. You're doing this when you declare your Array type and then when you use the #AccountIds in your stored procedure's select statement.
CREATE TYPE [usp].[Array] AS TABLE -- maybe choose a more descriptive name than 'Array'
(
Value VARCHAR(36) NULL -- choose a more descriptive name than 'Value'
)
CREATE PROCEDURE [usp].[your_procedure_name]
#AccountIds [usp].[Array] READONLY -- use TVP as a parameter
AS
SELECT …
It is not clear form your question details whether you also mean to have a parameter in the stored procedure for the Encoding. It seems like you're looking for accounts whose Encodings start with a period '.'.
So first, create your type, like you're doing.
Then create your stored procedure.
Then test your stored procedure, something like this:
DECLARE #mylist Array -- make TVP sample data
INSERT #mylist(Value) VALUES(1),(11),(27),(123) -- insert some values
exec your_procedure_name #mylist -- run stored procedure
The following line is completely unnecessary. The JOIN to Account does this filter for you.
DCAM.AccountId IN (SELECT Value From #AccountIds)
Or am I missing something?

Production uses of Table Value Parameters

I've just begun to learn how to write stored procedures and SQL code outside of the basic DML stuff. Something that I've recently come across is table value parameters. I've found a script to make a TVP and it works just fine but there are two things that I don't understand about it. One, when to use them. What's a typical real world scenario when a TVP would be beneficial. Two, how come when I remove the begin and end from the following script does it work the same; what's the difference between having those keywords and not? SQL Server 2008 R2
use [tempdb]
--this is the database table that will be populated
create table SampleTable
(
id int not null identity (1,1)
,SampleString varchar(50)
,SampleInt int null
)
go
--create the table data type
create type dbo.SampleDataType as table
(
SampleString varchar(50)
,SampleInt int
)
go
--the stored procedure takes the SampleDataType as an input parameter
create proc SampleProc
(
--accepts the TVP as the lone parameter
--make sure that the TVP is readonly
#Sample as dbo.SampleDataType readonly
)
as
begin
--we simply insert the values into the db table from the parameter
insert into SampleTable(SampleString,SampleInt)
select SampleString,SampleInt from #Sample
end
go
--this is the sample script to test that the sproc worked
declare #SampleData as dbo.SampleDataType
insert into #SampleData(SampleString,SampleInt) values ('one',1);
insert into #SampleData(SampleString,SampleInt) values ('two',2);
select * from #SampleData
One real world use is to parameterise an in clause.
Where a query has a filter on (x, y, z, ...) you no longer have to resort to one of the methods here such as passing it in as a comma delimited list and then splitting it.
The BEGIN ... END make no difference there. It defines a block. You might use that after an IF statement for example to group multiple statements together into one logical block.