I have a SQL Server 2012 User-Defined Table Types that I am using to get data from a PHP array and pass that data to a Procedure. Sometimes I get white spaces from the web app and I would like to use ltrim/rtrim to clean it. Can I do this at the User-Defined Table Type level? IE: where I declare that it should expect a parm1 varchar(10) can I somehow trim is there? I haven't had any luck. Thank you
Example, my Table Type looks like this:
CREATE TYPE LocationTableType AS TABLE
( LocationName VARCHAR(50)
, CostRate INT );
GO
I want to change it so that when LocationName comes in, it will ltrim()/rtrim() to clear out any extra spaces.
I assume that reported issue is linked to READONLY property of table valued params, property that is mandatory. This means that rows from table valued params can not be updated/deleted and also we can't insert other rows.
CREATE PROCEDURE dbo.DoSomething
#list dbo.LocationTableType READONLY -- <-- table table param have to be READONLY
AS
BEGIN
SELECT * FROM #list
END
On short term solution could be
1)to declare another variable within stored procedure
2)insert trimmed data into this variable and then
3)to change next references to all variable - param to new variable.
ALTER PROCEDURE dbo.DoSomething
#list dbo.LocationTableType READONLY
AS
BEGIN
DECLARE #listNoSpc dbo.LocationTableType -- New variable
INSERT #listNoSpc (LocationName, CostRate) -- Inserting trimmed data
SELECT LTRIM(l.LocationName), l.CostRate
FROM #list l
SELECT * FROM #listNoSpc -- Update references from #list to #listNoSpc
END
GO
Permanent solution should be to update webapp (PHP) to remove those spaces before sending data to SQL Server.
I have an existing SP.
I want to start using database snapshots in some cases.
Users can save a snapshot at any time.
If they do, the SP should use it.
Otherwise, use the primary database.
I want to adapt the SPs to handle this, by making the database/table names dynamic instead of hard-coded in the SP.
I can imagine something like this working, with fully qualified table names, but it gives the error 'Must declare the table variable "#db1"':
declare #table1 varchar(25);
set #table1 = Snapshot.schema.tablename;
select * from #table1;
This gives "Incorrect syntax near '#db'."
declare #db varchar(25);
set #db = "Snapshot";
use #db;
This sorta works, but the "use" is only in effect during the execute. By the time the 'select' is executed, you are back to using the original database.
declare #db varchar(25);
set #db = 'use Snapshot';
EXECUTE(#db);
select * from Schema.Tablename;
I know I could generate the entire SP on the fly, but that seems guaranteed to get rejected by the DBAs. I'd rather a less radical solution.
Any better ideas?
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
I need to create some stored procedures that receive as parameter db name, table name, user name, etc and do some processing on them.
I can create them using dynamic sql, but I'd like to know if it's possible to use variable name in commands that expects db name or table name.
E.g. instead of
USE [MyDataBase]
CREATE LOGIN [MyUser] WITH PASSWORD=N'MyPass', DEFAULT_DATABASE=[MyDataBase]
to be able to have
DECLARE #MyDB nvarchar(100)
DECLARE #MyUser nvarchar(100)
-- .... set #MyUser here
Select #MyDB = DB_NAME()
USE #MyDB
CREATE LOGIN #MyUser WITH PASSWORD=#MyPass, DEFAULT_DATABASE= #DbName
Thanks
These can't be parameterised unfortunately.
You need to use dynamic SQL.
See The Curse and Blessings of Dynamic SQL - Dealing with Dynamic Table and Column Names for some advice on how to do this as safely as possible.
in my SQL Server 2008 database I have a number of different tables with the same structure. I query them in different stored procedures. My first try was to pass the table name to the stored procedure, like:
CREATE PROCEDURE MyTest
#tableName nvarchar(255)
AS
BEGIN
SELECT * FROM #tableName
END
But we can't use parameters for table names in SQL. So I asked you and tried the solution with using Synonyms instead of a parameter for the table name:
CREATE PROCEDURE MyTest
#tableName nvarchar(255)
AS
BEGIN
EXEC SetSimilarityTableNameSynonym #tbl = #tableName;
SELECT * FROM dbo.CurrentSimilarityTable
END
SetSimilarityTableNameSynonym is a SP to set the Synonym dbo.CurrentSimilarityTable to the passed value (the specific table name). It looks like:
CREATE PROCEDURE [dbo].[SetSimilarityTableNameSynonym]
#tbl nvarchar(255)
AS
BEGIN
IF object_id('dbo.CurrentSimilarityTable', 'SN') IS NOT NULL
DROP SYNONYM CurrentSimilarityTable;
-- Set the synonym for each existing table
IF #tbl = 'byArticle'
CREATE SYNONYM dbo.CurrentSimilarityTable FOR dbo.similarity_byArticle;
...
END
Now, as you probably see, the problem is with concurrent access to the SPs which will "destroy" each others assigned synonym. So I tried to create dynamic synonyms for each single SP-call with a GUID via NewID()
DECLARE #theGUID uniqueidentifier;
SET #theGUID=NEWID()
SET #theSynonym = 'dbo.SimTabSyn_' + CONVERT(nvarchar(255), #theGUID);
BUT ... I can't use the dynamical created name to create a synonym:
CREATE SYNONYM #theSynonym FOR dbo.similarity_byArticle;
doesn't work.
Has anybody an idea, how to get dynamical synonyms running? Is this even possible?
Thanks in advance,
Frank
All I can suggest is to run the CREATE SYNONYM in dynamic SQL. And this also means your code is running at quite high rights (db_owner or ddl_admin). You may need EXECUTE AS OWNER to allow it when you secure the code.
And how many synonyms will you end up with for the same table? If you have to do it this way, I'd use OBJECT_ID not NEWID and test first so you have one synonym per table.
But if you have one synonym per table then why not use the table name...?
What is the point is there creating 1 or more synonyms for the same table, given the table names are already unique...
I'd fix the database design.
Why would you want multiple concurrent users to overwrite the single resource (synonym)?
If your MyTest procedure is taking a the table name as a parameter, why not simply do dynamic SQL? You can validate the #tableName against against a hardcoded list of tables that this procedure is allowed to select from, or against sys.tables