Define a table type parameter - sql-server

I just try to create a table type parameter called "ListOfName", and set a parameter of such type in my procedure. But an error incurred:
Must declare the scalar variable "#Table_Name_List"
But I declare
CREATE TYPE ListOfName AS TABLE (Table_Name nvarchar(50) );
CREATE PROCEDURE spTest
#Table_Name_List ListOfName READONLY
AS
SELECT * from dbo.Table_Size_Daily
WHERE Table_NAME IN (#Table_Name_List)
Go
If I add a statement before the definition chunk,
DECLARE #Table_Name_List AS ListOfName;
The error was:
The variable name '#Table_Name_List' has already been declared.
Variable names must be unique within a query batch or stored
procedure.
How can I fix it? Thanks a lot

I can see that there is misconception on what a Table Type is. Even though there is only one column in your table type, you can't treat that as a single-column argument.
Consider a Table type parameter as any other table irrespective of
number of columns you have.
In order to fix your issue, I would prefer your WHERE condition to change as below:
WHERE Table_NAME IN (SELECT Table_Name FROM #Table_Name_List)
However, I would strongly recommend you to read a few tutorials on Table type before you proceed with any implementation. For ex: This Table Valued Parameters article on MSDN gives clear insight into it.

Related

How do i modify a particular parameter datatype in Stored Procedures?

I have a Stored Procedure, see this picture .
I want to modify Department parameter as nvarchar(30), where it is currently as nvarchar(10).
So how could I achieve this?
Thank you so much
declare #department nvarchar(30)
set #department = CAST (#department_in as nvarchar(30))
If you right click on it, you have the option 'modify' which will generate an alter procedure script for you.
Change the parameter to an nvarchar(30) and run the alter script.
You can't change the type of variable if you already dclared it. In other words:
to change the type of variable, yo need to declare it once again, and as this thread states, you cannot do it.
It is possible to query database objects according to their parameter names, types, length etc using the information schema view
[INFORMATION_SCHEMA].[PARAMETERS]
But you cannot simply update the length on some tables to modify the length, etc of a procedure parameter.
All these objects should be updated using
ALTER PROCEDURE ...
command, at least for stored procedures
You can get the source codes of a procedure by running sp_helptext as follows
sp_helptext yourStoredProcedureName
Then modify the parameter and execute the script as ALTER command

Stored procedure Not Accepting Newline character in parameter

I am trying to put comma separated values as parameter to stored procedure, but when I am pressing "Enter" key for continuing the values so that it can be seen in single screen (without scrolling) , SP is not accepting the values which are provided after "Enter" key is pressed(means in new line)
USP_SAMPLE_SP #YEAR, #EMPIDS
If I am executing stored procedure as below :
USP_SAMPLE_SP '2016','111,222,333,444,555'
It is executing properly
But if I am trying to execute like below :
USP_SAMPLE_SP '2016','111,222,
333,444,555'
It is accepting all values however it is showing values up to '222' after that it is not considering any value.
Referring to "Newline" I mean is it is accepting like below :
USP_SAMPLE_SP '2016','111, 222,333,444,555'
But if I am pressing "Enter" it is not accepting values after that
If any one can explain this scenario it will be helpful.
Note: Sometimes I need to pass large number of values(ex. 1000 or above).
Please suggest if any one knows better and efficient way to do this.
First I am creating User-Defined Table Type as EMPType
CREATE TYPE [dbo].[EMPType] AS TABLE(
FinStartYear varchar(4),
EmpId nvarchar(max) NULL
)
Then I am trying to pass those values for altering my stored procedure as below :
ALTER PROCEDURE [USP_SAMPLE_SP]
-- Add the parameters for the stored procedure here
#FinStartYear EMPType readonly ,
#EmpIDs EMPType readonly
AS
But when I hover mouse over the parameter it shows me below error :
The Parameter '#FinStartYear' cannot be declared READONLY since it is not table valued parameter.
The Parameter '#EmpIDs' cannot be declared READONLY since it is not table valued parameter.
But when I am trying to alter the stored procedure it is showing me below error :
Must declare the scalar variable "#EmpIDs".
Must declare the scalar variable "#FinStartYear"
When I hover over 'EMPType' it shows me below error :
Parameter or variable '#FinStartYear' has an invalid datatype.
NOTE : Under Programmability -> Types -> User-Defined Table Types ->dbo.EMPType
It is showing me EMPType has been created.
It's not possible to be certain without seeing the code of your stored procedure, but I suspect that the newline character somehow breaks the code which is used to split the delimited string into a result set; the newline character is interpreted as a terminator and all the values following it are ignored.
There are several possible solutions.
One is to build your list of input values from a series of shorter strings, enabling you to control how the list appears on the screen without affecting its content. Note that this requires the use of a variable:
-- I am guessing the data type of the #EMPIDS parameter. Use a matching type for #IDS
DECLARE #IDS VARCHAR(MAX) = '111,222,'
+ '333,444,555'
EXEC USP_SAMPLE_SP #YEAR = '2016', #EMPIDS = #IDS
Another would be to amend the code of the stored procedure to strip out newline characters before splitting the delimited list. I wouldn't recommend this, since this behaviour might be relied on by other users of the code.
Edit
Here's an example of how to replace newline characters:
DECLARE #EMPID varchar(100) = '111,222,
333,444,555'
SET #EMPID = REPLACE(REPLACE(#EMPID,CHAR(13),''),CHAR(10),'')
SELECT #EMPID
Before you use this, make sure you're not going to affect any other users of this code who might rely on the current behaviour.
Another solution would be to amend the stored procedure to accept a table-valued parameter rather than a delimited list. This is a little more complex to set up but likely to perform than better than splitting a delimited string, especially if the string contains thousands of values.
Edit 2
I think you might have misunderstood how a table-valued parameter works. It enables you to completely avoid the concatenated list of EMPIDs, and instead work with a table variable containing one EMPID per row which can be passed as an argument to a stored procedure.
Here's a worked example:
CREATE TYPE dbo.EMPType AS TABLE(
EmpId int NOT NULL PRIMARY KEY
)
GO
-- I don't know what your procedure does so I've created a simple stub
-- which shows how to use the table-valued parameter like a table
CREATE PROCEDURE dbo.USP_SAMPLE_SP
#FinStartYear varchar(4),
#empids EMPType readonly
AS
SELECT #FinStartYear AS out_year, EmpId
FROM #empids
ORDER BY EmpId
GO
-- use the code from here to the end of the example every time
-- you want to execute the procedure (setting the list of EMPID values appropriately
DECLARE #in_emp EMPType --declare an instance of EMPType as a variable
INSERT #in_emp(EmpId) VALUES (111),(333),(222),(555),(444) --insert some values into the variable
EXEC dbo.USP_SAMPLE_SP #FinStartYear = '2016',#empids = #in_emp --pass the variable as a parameter to the stored procedure
GO

Pass TABLE as parameter without creating TYPE

I know it is possible to create a stored procedure and pass a TABLE as parameter by doing the following:
CREATE TYPE MyTableType AS TABLE (Id UNIQUEIDENTIFIER)
CREATE PROCEDURE MyProcedure
#ids MyTableType READONLY
AS
...
I don't want to create a TYPE as above, because I was told to not create type dependencies in the database. So I want to do the following:
CREATE PROCEDURE MyProcedure
#ids TABLE (Id UNIQUEIDENTIFIER)
AS
But for some reason I get error highlights. Am I doing something wrong in the syntax? Or is this not possible to do?
I don't think it's possible to declare a table type inline like you're trying to do. The first line in this article on table-valued parameters says:
Table-valued parameters are declared by using user-defined table types.

EF4 - The selected stored procedure returns no columns

I have query in a stored procedure that calls some linked servers with some dynamic SQL. I understand that EF doesn't like that, so I specifically listed all the columns that would be returned. Yet, it still doesn't like that. What am I doing wrong here? I just want EF to be able to detect the columns returned from the stored procedure so I can create the classes I need.
Please see the following code that makes up the last lines of my stored procedure:
SELECT
#TempMain.ID,
#TempMain.Class_Data,
#TempMain.Web_Store_Class1,
#TempMain.Web_Store_Class2,
#TempMain.Web_Store_Status,
#TempMain.Cur_1pc_Cat51_Price,
#TempMain.Cur_1pc_Cat52_Price,
#TempMain.Cur_1pc_Cat61_Price,
#TempMain.Cur_1pc_Cat62_Price,
#TempMain.Cur_1pc_Cat63_Price,
#TempMain.Flat_Length,
#TempMain.Flat_Width,
#TempMain.Item_Height,
#TempMain.Item_Weight,
#TempMain.Um,
#TempMain.Lead_Time_Code,
#TempMain.Wp_Image_Nme,
#TempMain.Wp_Mod_Dte,
#TempMain.Catalog_Price_Chg_Dt,
#TempMain.Description,
#TempMain.Supersede_Ctl,
#TempMain.Supersede_Pn,
TempDesc.Cust_Desc,
TempMfgr.Mfgr_Item_Nbr,
TempMfgr.Mfgr_Name,
TempMfgr.Vendor_ID
FROM
#TempMain
LEFT JOIN TempDesc ON #TempMain.ID = TempDesc.ID
LEFT JOIN TempMfgr ON #TempMain.ID = TempMfgr.ID
EF doesn't support importing stored procedures which build result set from:
Dynamic queries
Temporary tables
The reason is that to import the procedure EF must execute it. Such operation can be dangerous because it can trigger some changes in the database. Because of that EF uses special SQL command before it executes the stored procedure:
SET FMTONLY ON
By executing this command stored procedure will return only "metadata" about columns in its result set and it will not execute its logic. But because the logic wasn't executed there is no temporary table (or built dynamic query) so metadata contains nothing.
You have two choices (except the one which requires re-writing your stored procedure to not use these features):
Define the returned complex type manually (I guess it should work)
Use a hack and just for adding the stored procedure put at its beginning SET FMTONLY OFF. This will allow rest of your SP's code to execute in normal way. Just make sure that your SP doesn't modify any data because these modifications will be executed during import! After successful import remove that hack.
Adding this Non-Logical block of code solved the problem. Even though it will never Hit
IF 1=0 BEGIN
SET FMTONLY OFF
END
Why does my typed dataset not like temporary tables?
http://social.msdn.microsoft.com/Forums/en-US/adodotnetdataset/thread/fe76d511-64a8-436d-9c16-6d09ecf436ea/
Or you can create a User-Defined Table Type and return that.
CREATE TYPE T1 AS TABLE
( ID bigint NOT NULL
,Field1 varchar(max) COLLATE Latin1_General_CI_AI NOT NULL
,Field2 bit NOT NULL
,Field3 varchar(500) NOT NULL
);
GO
Then in the procedure:
DECLARE #tempTable dbo.T1
INSERT #tempTable (ID, Field1, Field2, Field3)
SELECT .....
....
SELECT * FROM #tempTable
Now EF should be able to recognize the returned columns type.
As some others have noted, make sure the procedure actually runs. In particular, in my case, I was running the procedure happily without error in SQL Server Management Studio completely forgetting that I was logged in with admin rights. As soon as I tried running the procedure using my application's principal user I found there was a table in the query that that user did not have permission to access.
Interesting side note: Had the same problem which I first solved by using Table Variables, rather than Temp Tables (just for the import). That wasn't particularly intuitive to me, and threw me off when initially observing my two SProcs: one using Temp tables and one with Table Variables.
(SET FMTONLY OFF never worked for me, so I just changed my SProcs temporarily to get the column info, rather than bothering with the hack on the EF side just as an FYI.)
My best option was really just manually creating the complex type and mapping the function import to it. Worked great, and the only difference ended up being that an additional FactoryMethod to create the properties was included in the Designer.
What I would add is:
That the import also fails if the stored procedures has parameters and returns no result set for the default parameter values.
My stored procedure had 2 float parameters and would not return anything when both parameters are 0.
So in order to add this stored procedure to the entity model, I set the value of these parameters in the stored procedure so that it is guaranteed to return some rows, no matter what the parameters actually are.
Then after adding this stored procedure to the entity model I undid the changes.
both solutions :
1- Define the returned complex type manually (I guess it should work)
2- Use a hack and just for adding the stored procedure put at its beginning SET FMTONLY OFF.
not working with me in some procedure however it worked with other one!
my procedure ends with this line:
SELECT machineId, production [AProduction]
, (select production FROM #ShiftBFinalProd WHERE machineId = #ShiftAFinalProd.machineId) [BProduction]
, (select production FROM #ShiftCFinalProd WHERE machineId = #ShiftAFinalProd.machineId) [CProduction]
FROM #ShiftAFinalProd
ORDER BY machineId
Thanks
In addition to what #tmanthley said, be sure that your stored procedure actually works by running it first in SSMS. I had imported some stored procedures and forgot about a couple dependent scalar functions, which caused EF to determine that the procedure returned no columns. Seems like a mistake I should have caught earlier on, but EF doesn't give you an error message in that case.
Entity Framework will try to get the columns by executing your stored procedure, passing NULL for every argument.
Please make sure that the stored procedure will return something under all the circumstances. Note it may have been smarter for Entity Framework to execute the stored proc with default values for the arguments, as opposed to NULLs.
ER does the following to get the metadata of the table:
SET FMTONLY ON
This will break your stored procedure in various circumstances, in particular, if it uses a temporary table.
So to get a result as complex type; please try by adding
SET FMTONLY OFF;
This worked for me - hope it works for you too.
Referred from https://social.msdn.microsoft.com/Forums/en-US/e7f598a2-6827-4b27-a09d-aefe733b48e6/entity-model-add-function-import-stored-procedure-returns-no-columns?forum=adodotnetentityframework
In my case adding SET NOCOUNT ON; at the top of the procedure fixed the problem. It's best practice anyway.
In my case SET FMTONLY OFF did not work. The method I followed is, I took backup of original stored procedure and replace with only column name like the below query.
Select Convert(max,'') as Id,Convert(max,'') as Name
After this change, create new function import, complex type in entity framework.
Once the function import and complex type is created, replace the above query with your original stored procedure.
SET FMTONLY OFF
worked for me for one of the procedure but failed for other procedure. Following steps helps me to resolve my problem
Within a stored procedure, I have created temporary table with the same column type and inserted all the data returned by dynamic query to temp table.
and selected the temp table data.
Create table #temp
(
-- columns with same types as dynamic query
)
EXEC sp_executeSQL #sql
insert into #temp
Select * from #temp
drop table #temp
Deleted existing complex type, import function and stored procedure instance for old stored procedure and updated entity model for current new procedure.
Edit the imported Function in entity modal for desired complex type, you will get all the column information there which is not getting for previous stored procedure.
once you have done with the type creation you can delete the temporary table from stored procedure and then refresh Entity Framework.
In Entity framework, while getting column information the sql executes the procedure with passing null values in parameter. So I handled null case differently by creating a temp table with all the required columns and returning all the columns with no value when null is passed to the procedure.
In my procedure there was dynamic query, something like
declare #category_id int
set #category_id = (SELECT CATEGORY_ID FROM CORE_USER where USER_ID = #USER_ID)
declare #tableName varchar(15)
declare #sql VARCHAR(max)
declare #USER_IDT varchar(100)
declare #SESSION_IDT varchar(10)
IF (#category_id = 3)
set #tableName = 'STUD_STUDENT'
else if(#category_id = 4)
set #tableName = 'STUD_GUARDIAN'
if isnull(#tableName,'')<>''
begin
set #sql = 'SELECT [USER_ID], [FIRST_NAME], SCHOOL_NAME, SOCIETY_NAME, SCHOOL_ID,
SESSION_ID, [START_DATE], [END_DATE]
from #tableName
....
EXECUTE (#sql)
END
ELSE
BEGIN
SELECT * from #UserPrfTemp
END
I was not getting the column information in
my case after using the set FMTONLY OFF trick.
This is temp table I created to get the blank data.
Now I am getting the column info
Create table #UserPrfTemp
(
[USER_ID] bigint,
[FIRST_NAME] nvarchar(60),
SCHOOL_NAME nvarchar(60),
SOCIETY_NAME nvarchar(200)
.....
}
I solved this problem creating a table variable and then returning from it.
DECLARE #VarTable TABLE (
NeededColumn1 VARCHAR(100),
NeededColumn2 INT,
NeededColumn3 VARCHAR(100)
)
...
--Fetch Data from Linked server here
...
INSERT INTO #VarTable (NeededColumn1,NeededColumn2,NeededColumn3)
SELECT Column1, Column2, Column3
FROM #TempTable
SELECT * FROM #VarTable.
In that manner, your the SP result will be bounded to the table variable, which EF has access to.
I discovered a method that should help most people out whatever's happening.
Pull up your favourite SQL client and run the proc that you're trying to update with every parameter = null. Visual Studio is literally trying to do this when SET FMTONLY ON. Run a trace. You'll see.
You'll probably get an error, or unexpected data out. Fix that and your issue is fixed.
In my case the function read in JSON and failed because the JSON string was empty.
I just put something like
IF(#FooJSON IS NULL)
BEGIN
SELECT 1 VAR1, 2 VAR2;
END
ELSE
--OTHER LOGIC
That's probably an ugly solution, but I inherited this mess and we don't go into Ravenholm.
Change #Temp tables with WITH SQL EXPRESSION

How to return temporary table from stored procedure

CREATE PROCEDURE [test].[proc]
#ConfiguredContentId int,
#NumberOfGames int
AS
BEGIN
SET NOCOUNT ON
RETURN
#WunNumbers TABLE (WinNumb int)
INSERT INTO #WunNumbers (WinNumb)
SELECT TOP (#NumberOfGames) WinningNumber
FROM [Game].[Game] g
JOIN [Game].[RouletteResult] AS rr ON g.[Id] = rr.[gameId]
WHERE g.[ConfiguredContentId] = #ConfiguredContentId
ORDER BY g.[Stoptime] DESC
SELECT WinNumb, COUNT (WinNumb) AS "Count"
FROM #WunNumbers wn
GROUP BY wn.[WinNumb]
END
GO
This stored procedure returns values from first select statement, but I would like to have values from second select statement to be returned. Table #WunNumbers is a temporary table.
Any ideas???
Take a look at this code,
CREATE PROCEDURE Test
AS
DECLARE #tab table (no int, name varchar(30))
insert #tab select eno,ename from emp
select * from #tab
RETURN
What version of SQL Server are you using? In SQL Server 2008 you can use Table Parameters and Table Types.
An alternative approach is to return a table variable from a user defined function but I am not a big fan of this method.
You can find an example here
A temp table can be created in the caller and then populated from the called SP.
create table #GetValuesOutputTable(
...
);
exec GetValues; -- populates #GetValuesOutputTable
select * from #GetValuesOutputTable;
Some advantages of this approach over the "insert exec" is that it can be nested and that it can be used as input or output.
Some disadvantages are that the "argument" is not public, the table creation exists within each caller, and that the name of the table could collide with other temp objects. It helps when the temp table name closely matches the SP name and follows some convention.
Taking it a bit farther, for output only temp tables, the insert-exec approach and the temp table approach can be supported simultaneously by the called SP. This doesn't help too much for chaining SP's because the table still need to be defined in the caller but can help to simplify testing from the cmd line or when calling externally.
-- The "called" SP
declare
#returnAsSelect bit = 0;
if object_id('tempdb..#GetValuesOutputTable') is null
begin
set #returnAsSelect = 1;
create table #GetValuesOutputTable(
...
);
end
-- populate the table
if #returnAsSelect = 1
select * from #GetValuesOutputTable;
YES YOU CAN.
In your stored procedure, you fill the table #tbRetour.
At the very end of your stored procedure, you write:
SELECT * FROM #tbRetour
To execute the stored procedure, you write:
USE [...]
GO
DECLARE #return_value int
EXEC #return_value = [dbo].[getEnregistrementWithDetails]
#id_enregistrement_entete = '(guid)'
GO
The return type of a procedure is int.
You can also return result sets (as your code currently does) (okay, you can also send messages, which are strings)
Those are the only "returns" you can make. Whilst you can add table-valued parameters to a procedure (see BOL), they're input only.
Edit:
(Or as another poster mentioned, you could also use a Table Valued Function, rather than a procedure)
First create a real, permanent table as a template that has the required layout for the returned temporary table, using a naming convention that identifies it as a template and links it symbolically to the SP, eg tmp_SPName_Output. This table will never contain any data.
In the SP, use INSERT to load data into a temp table following the same naming convention, e.g. #SPName_Output which is assumed to exist. You can test for its existence and return an error if it does not.
Before calling the sp use this simple select to create the temp table:
SELECT TOP(0) * INTO #SPName_Output FROM tmp_SPName_Output;
EXEC SPName;
-- Now process records in #SPName_Output;
This has these distinct advantages:
The temp table is local to the current session, unlike ##, so will not clash with concurrent calls to the SP from
different sessions. It is also dropped automatically when out of scope.
The template table is maintained alongside the SP, so if changes are
made to the output (new columns added, for example) then pre-existing
callers of the SP do not break. The caller does not need to be changed.
You can define any number of output tables with different naming for
one SP and fill them all. You can also define alternative outputs
with different naming and have the SP check the existence of the temp
tables to see which need to be filled.
Similarly, if major changes are made but you want to keep backwards
compatibility, you can have a new template table and naming for the later
version but still support the earlier version by checking which temp
table the caller has created.

Resources