SQL query about stored procedure parameter - sql

I wanted know the reason, when we create a stored procedure in update, delete or insert like
update TABLE_NAME set column_name = #variable_name
it is fine.
Why can't we pass parameter or variable to select like
select #column_variable from #table_variable
I know that as a work around you need to use dynamic SQL, but what is reason it won't work?

If this is about SQL Server, then the reason why you cannot parametrise column names and table names with this statement,
select #column_variable from #table_variable
is because this can already be a valid statement and interpreted in a different way:
#name would be interpreted as a reference to a scalar variable whose value is to be returned as a dataset column;
#name would be interpreted as a table variable name, i.e. the name of a variable of a table type.
In each of these cases, the use of #name to denote a parameter holding the name of an actual column or table to select from would simply be very confusing.
On the other hand, one might think that a different syntax could have been devised for this (i.e. specifically for parametrisation of names) and yet it hasn't.
My opinion (and I have to admit that it's just my opinion) why there isn't such a syntax is that by building parametrisable names into SQL you would probably end up with less efficient query planner. If at the time of query compilation you don't know what and whence the query is trying to select, you can't really build a really efficient plan for it, can you.
Of course, building a query plan could have been delayed until the time when the name parameters have been evaluated, but the planner would have had to build a plan every time such a query is invoked, unlike now, when the query plan is stored once and then used many times.

Related

How to suppress record sets returned by SELECT statements in a Stored Procedure

I'm writing a stored procedure which checks for the existence of various tables in various databases, as well as the permissions that the user executing the stored procedure has on those tables. The stored procedure itself resides within a user database (i.e. it's not in the Master db).
To perform my checks, my stored procedure contains lots of SELECT statements. Each of those obviously returns a record set. What I would like is to somehow suppress these record sets so that they are not returned by the stored procedure, and instead return my own, single record set which is just a collection of messages relating to each check the stored procedure performs.
I think the obvious answer is to use a table-valued function instead, but I've not been able to recreate my tests successfully in a Function as they appear in the stored procedure. For starters, I'm having to use temporary tables (not possible in a function) and dynamic SQL (not very compatible with table parameters).
I think I've basically got two choices:
Rewrite my stored procedure as a function and figure out how to do the checks a different way.
Continue using my stored procedure and use an OUTPUT parameter to return my result messages, probably as a delimited string, and in the associated ASP.NET application just ignore all the record sets the stored procedure returns .
Neither of these solutions is very satisfactory. Before I spend any more time pursuing either one, is there a way to discard the record sets produced by the SELECT statements in a stored procedure and explicitly define what record I want it to return?
Hmm, I only can speculate here...
Are you using something like
SELECT ...;
IF ##rowcount > 0
BEGIN
...
END;
?
Then you can rewrite it using something like
IF EXISTS (SELECT ...)
BEGIN
...
END;
or
DECLARE #variable integer;
SELECT #variable = count(*) ...;
IF #variable > 0
BEGIN
...
END;
In general point the results of your queries to a target (variable, table, expression, ...), then they don't get outputted.
And then just execute the query for your desired result in the end.
In my opinion, here is almost no reason to have stored procedures produce record sets. That is what stored functions are for. On occasion, it is needed, because of the use of dynamic SQL or other stored procedures, but not as a general practice. Much, much too often, I see stored procedures being used where stored functions or views are more appropriate.
What should you do? Even SELECT statement in the stored procedure should be one of the following:
Setting (local) variables.
Saving the results in a temporary table or table variable.
The logic for the stored procedure should be working on the local variables. The results should be returned using OUTPUT parameters.
If you need to return rows in a tabular format, you can do that using tables explicitly (such as a global temporary table or real table). Or, you can have one SELECT at the end that does return a single result set. However, if you need this and can phrase the stored procedure as a function, that is better in my opinion.

nvarchar as query argument

I am trying to create a stored procedure that has a table and as an argument and executes some queries on that table.
So...
CREATE PROCEDURE blabla
#TableName nvarchar(50)
AS
DROP TABLE #TableName -- just an example, real queries are much longer
GO
This query gives me incorrect syntax error.
I know I can always use sp_executesql procedure, but I want a neater way where I don't need to worry about building an endless sql string.
Thanks
Here is a good article on why not to use Dynamic SQL in most cases as well as how to use it properly when it is the best solution:
http://www.sommarskog.se/dynamic_sql.html
Basically, doing what you are looking to do has a number of issues, including not allowing the system to properly check for permission issues before executing, not being able to optimize the stored procedure, and (most importantly) opening yourself up to SQL injection. You can mitigate this last issue somewhat but it involves a much more complex statement. Here is a quote from the above article:
Passing table and column names as parameters to a procedure with dynamic SQL is rarely a good idea for application code. (It can make perfectly sense for admin tasks). As I've said, you cannot pass a table or a column name as a parameter to sp_executesql, but you must interpolate it into the SQL string. Still you should protect it against SQL injection, as a matter of routine. It could be that bad it comes from user input.
To this end, you should use the built-in function quotename() (added in SQL 7). quotename() takes two parameters: the first is a string, and the second is a pair of delimiters to wrap the string in. The default for the second parameter is []. Thus, quotename('Orders') returns [Orders]. quotename() takes care of nested delimiters, so if you have a really crazy table name like Left]Bracket, quotename() will return [Left]]Bracket].
Note that when you work with names with several components, each component should be quoted separately. quotename('dbo.Orders') returns [dbo.Orders], but that is a table in an unknown schema of which the first four characters are d, b, o and a dot. As long as you only work with the dbo schema, best practice is to add dbo in the dynamic SQL and only pass the table name. If you work with different schemas, pass the schema as a separate parameter. (Although you could use the built-in function parsename() to split up a #tblname parameter in parts.)
I know you want a "neater" way of creating a dynamic statement but the reality is that no only is that not possible for how you want to do this, really you need to make the statement even more complex in order to ensure that the stored procedure is safe. I would try very hard to look at a different way to solve this issue (the article had a few suggestions). If you can avoid making this statement into dynamic SQL, you really should.
There are very few places that parameters can be used in T-SQL. Usually, it's exactly the places where you would find a quoted string - not just any arbitrary place within the query (where the query is necessarily in a string form anyway)
E.g., you could use a parameter or variable to replace 'hello' below:
SELECT * from Table2 where ColA = 'hello'
But you couldn't use it where Table2 appears. I don't know why people seem to expect such things to be possible in T-SQL, when it's generally not possible in most other programming languages either, outside of exec/eval style functions.
If you have multiple tables that share the same structure (names and types of columns), it generally suggests that what you should actually have is a single table, with possibly additional column(s) that distinguish between rows that would originally be in different tables. E.g. if you currently have:
CREATE TABLE MaleEmployees (
EmployeeNo int not null,
Name varchar(50) not null,
)
and
CREATE TABLE FemaleEmployees (
EmployeeNo int not null,
Name varchar(50) not null
)
You should instead have:
CREATE TABLE Employees (
EmployeeNo int not null,
Name varchar(50) not null,
Gender char(1) not null,
constraint CK_Gender_Valid CHECK (Gender in ('M','F'))
)
You can then query this Employees table, regardless of gender, rather than trying to parametrize the table name within your query. Of course, the above is an exaggerated example.
set #l = 'DROP TABLE ' + #TableName
exec #l
But if that's what you mean by 'endless string', not sure what you want
The correct syntax(notice the begin):
CREATE PROCEDURE blabla
#TableName nvarchar(50)
AS
begin
DROP TABLE #TableName -- just an example, real queries are much longer
END
GO

In a stored procedure, it it better to simply query data or to construct a query and then execute it? why?

I have worked on SQL stored procedures and I have noticed that many people use two different approaches -
First, to use select queries i.e. something like
Select * from TableA where colA = 10 order by colA
Second, is to do the same by constructing a query i.e. like
Declare #sqlstring varchar(100)
Declare #sqlwhereclause varchar(100)
Declare #sqlorderby varchar(100)
Set #sqlstring = 'Select * from TableA '
Set #sqlwhereclause = 'where colA = 10 '
Set #sqlorderby = 'order by colA'
Set #sqlstring = #sqlstring + #sqlwhereclause + #sqlorderby
exec #sqlstring
Now, I know both work fine. But, the second method I mentioned is a little annoying to maintain.
I want to know which one is better? Is there any specific reason one would resort to one method over the other? Any benefits of one method over other?
Use the first one. This will allow a query plan to be cached properly, apart from being the way you are supposed to work with SQL.
The second one is open to SQL Injection attacks, apart from the other issues.
With the dynamic SQL you will not get compile time checking, so it may fail only when invoked (the sooner you know about incorrect syntax, the better).
And, you noted yourself, the maintenance burden is also higher.
The second method has the obvious drawback of not being syntax checked at compile time. It does however allow a dynamic order by clause, which the first does not. I recommend that you always use the first example unless you have a very good reason to make the query dynamic. And, as #Oded has already pointed out, be sure to guard yourself against sql injection if you do go for the second approach.
I don't have a full comprehensive answer for you, but I can tell you right now that the latter method is much more difficult to work with when importing the stored procedure as a function in an ORM. Since the SQL is constructed dynamically, you have to manually create any type-classes that are returned from the stored procedure that aren't directly correlated to entities in your model.
With that in mind, there are times where you simply can't avoid constructing a SQL statement, especially when where clauses and joins depend on the parameters passed in. In my experience, I have found that stored procs that are creating large, variably joined/whered statements for EXECs are trying to do too many things. In these situations, I would recommend you keep the Single Responsibility Principle in mind.
Executing dynamic SQL inside a stored procedure reduces the value of using stored procedures to just a saved query container. Stored procedures are mostly beneficial in that the query execution plan (a very costly operation) is compiled and stored in memory the first time the procedure is executed. This means that every subsequent execution of the procedure is bypassing the query plan calculations, and jumping right to the data retrieval portiion of the operation.
Also, allowing a stored procedure to take an executable query string as a parameter is dangerous. Anyone with execute permission on granted on the procedure could potentially cause havoc on the rest of the database.

Cast Stored Procedure Result as a Table? [duplicate]

This question already has answers here:
SQL: how to predicate over stored procedure's result sets?
(3 answers)
Closed 6 years ago.
I currently have a stored procedure that runs a complex query and returns a data set. I'd like to cast this data set to a table (on which I can perform further queries) if at all possible. I know I can do this using a table-valued UDF but I'd prefer to avoid that at this point. Is there any way I can accomplish this task?
EDIT: OK... so the SProc I'm using (written by third party and I'm not supposed to change it) runs a fairly complex select statement to return a bunch of line item data about purchase orders. I can recreate it as a UDF but then I'd have to support the UDF and ensure it gets changed as and when our vendor changes their SProc. I'd like to further refine this line item info by a number of criteria such as (but not limited to) item numbers, vendor codes, cost centers, etc. All of this information is brought back by the original SProc and I just need to be able to manipulate it further. My thought process was that if I can somehow treat the results of the SProc as a table (or get them into a table format of some type) then I can run further queries against the original result set to limit by the criteria mentioned above. Please let me know if any further details are needed.
There's various means of sharing data between stored procedures - this link is pretty exhaustive.
But I'm curious why you want a table valued stored procedure (which doesn't exist in SQL Server) when there are table valued functions...
Cast Stored Procedure Result as a
Table?
Yes and this is used quite often. It simply needs one or more select statements:
Create Procedure #Foo
As
Select object_id, name
From sys.columns
That said, you cannot join to this resultset nor can you easily consume it from another stored proc (although there is a way). Given your edit, it appears the question is whether you can consume the results of a stored proc by another stored proc. Technically, yes. You can populate a temp table with the results of a proc. However, you must declare your temp variable or temp table with the same column structure as is returned by the first resultset of the stored proc.
Declare #Data Table ( object_id int, name nvarchar(128) )
Insert #Data
Exec #Foo
Select *
From #Data
(Or use the far more clever OPENROWSET solution as mentioned by Cade Roux and OMG Ponies)
Have you considered using table-valued parameters? They are new in SQL 2008.
-- Edit --
Nope, never mind, they're only good for passing data into stored procedures.
You could try using a View instead of a Stored Procedure. Store your complex query as part of the view, and you have the functionality to perform more queries on the view.

How do I supply the FROM clause of a SELECT statement from a UDF parameter

In the application I'm working on porting to the web, we currently dynamically access different tables at runtime from run to run, based on a "template" string that is specified. I would like to move the burden of doing that back to the database now that we are moving to SQL server, so I don't have to mess with a dynamic GridView. I thought of writing a Table-valued UDF with a parameter for the table name and one for the query WHERE clause.
I entered the following for my UDF but obviously it doesn't work. Is there any way to take a varchar or string of some kind and get a table reference that can work in the FROM clause?
CREATE FUNCTION TemplateSelector
(
#template varchar(40),
#code varchar(80)
)
RETURNS TABLE
AS
RETURN
(
SELECT * FROM #template WHERE ProductionCode = #code
)
Or some other way of getting a result set similar in concept to this. Basically all records in the table indicated by the varchar #template with the matching ProductionCode of the #code.
I get the error "Must declare the table variable "#template"", so SQL server probably things I'm trying to select from a table variable.
On Edit: Yeah I don't need to do it in a function, I can run Stored Procs, I've just not written any of them before.
CREATE PROCEDURE TemplateSelector
(
#template varchar(40),
#code varchar(80)
)
AS
EXEC('SELECT * FROM ' + #template + ' WHERE ProductionCode = ' + #code)
This works, though it's not a UDF.
The only way to do this is with the exec command.
Also, you have to move it out to a stored proc instead of a function. Apparently functions can't execute dynamic sql.
The only way that this would be possible is with dynamic SQL, however, dynamic SQL is not supported by SqlServer within a function.
I'm sorry to say that I'm quite sure that it is NOT possible to do this within a function.
If you were working with stored procedures it would be possible.
Also, it should be noted that, be replacing the table name in the query, you've destroyed SQL Server's ability to cache the execution plan for the query. This pretty much reduces the advantage of using a UDF or SP to nil. You might as well just call the SQL query directly.
I have a finite number of tables that I want to be able to address, so I could writing something using IF, that tests #template for matches with a number of values and for each match runs
SELECT * FROM TEMPLATENAME WHERE ProductionCode = #code
It sounds like that is a better option
If you have numerous tables with identical structure, it usually means you haven't designed your database in a normal form. You should unify these into one table. You may need to give this table one more attribute column to distinguish the data sets.