Click here to Skip to main content
15,904,416 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more: , +
I have a MSSQL stored procedure as below:
SQL
ALTER procedure [dbo].[GetDataFromTable]
(
@rowval varchar(50),
@tablename varchar(50),
@oby varchar(50)
)
as
begin
EXEC('Select top (' + @rowval + ') * from '+@tablename+ 'ORDER BY sno DESC')
end

On execution, it gives following error: Msg 156, Level 15, State 1, Line 1 Incorrect syntax near the keyword 'BY'.
Note: @rowval represents number of rows to be fetched, @tablename represents name of table, @oby represents column based on which ordering should be done. Note: I am using ASP.Net with C# at frontend to fire this procedure and using MSSQL 2008 R2 Express Edition at the backend

What I have tried:

SQL
ALTER procedure [dbo].[GetDataFromTable]
(
@rowval varchar(50),
@tablename varchar(50),
@oby varchar(50)
)
as
begin
EXEC('Select top (' + @rowval + ') * from '+@tablename+ 'ORDER BY '+@oby+' DESC')
end
Posted
Updated 7-Oct-16 10:27am

Spaces, my friend, spaces.
Change this:
SQL
from '+@tablename+ 'ORDER BY '

To this:
SQL
from '+@tablename+ ' ORDER BY '
To separate the table name from the ORDER BY text...

But I hope you are sanitizing your inputs rather than just passing user input through - or you are going to lose your DB in short order - that code is wide open to SQL Injection attack...
 
Share this answer
 
Comments
Member 8057273 7-Oct-16 11:24am    
Please tell how to sanitize the procedure for security.
Vincent Maverick Durano 7-Oct-16 14:54pm    
google "sql injection" or "how to prevent it"
missing leading space before "order"

passing in tablename = fubar, the sql would become ...from fubarORDER BY...

using exec on SQL statements that are built from passed in parameters can be very dangerous. Please ensure that any data passed in is sanitized to prevent injection.

I personally find it simpler to debug cases where dynamic sql statements are built by setting them to a variable, that way the variable can be printed out.
Seeing the result printed often make the error more visible.

eg.
SQL
declare @s varchar(100)
set @s = 'Select top (' + @rowval + ') * from ' + @tablename + ' ORDER BY ' + @oby + ' DESC'

print @s
exec(@s)


-Richared
 
Share this answer
 
Comments
Member 8057273 7-Oct-16 11:24am    
Please tell how to sanitize the procedure for security.
rhgarner 7-Oct-16 12:38pm    
there are numerous methods. doing a search on this site for "sql injection" yields quite a few results.

one of my favorite cartoons on the topic is from xkcd. https://xkcd.com/327/
Member 8057273 8-Oct-16 0:40am    
Hi, I know what SQL Injection is and how it works. What I need here is a solutions to this problem in the Stored Procedures that I have very limited knowledge about.
As already told, you missed a space before ORDER.
SQL
EXEC('Select top (' + @rowval + ') * from '+@tablename+ ' ORDER BY sno DESC')

The way you use parameters is opening the door to SQL injection.
https://en.wikipedia.org/wiki/SQL_injection[^]
http://www.w3schools.com/sql/sql_injection.asp[^]

[UpDate]
Quote:
Hi, I know what SQL Injection is and how it works.
We can't guess that you know what is this dangerous practice, and your usage if it don't make it less dangerous.

Quote:
What I need here is a solutions to this problem in the Stored Procedures that I have very limited knowledge about.
For your question, please read the first line of my answer, other solutions also tell you the same.
 
Share this answer
 
v2
Comments
Member 8057273 8-Oct-16 0:35am    
Hi, I know what SQL Injection is and how it works. What I need here is a solutions to this problem in the Stored Procedures that I have very limited knowledge about.
These general-purpose procedures are always more trouble than they're worth.

To prevent a SQL Injection[^] vulnerability, you need to validate both the table name and the "order by" column name. This is relatively easy when you have a single column name, with no "ASC" or "DESC" modifier. If you want to order by multiple columns, or control the sort sequence, this becomes much harder.

You'll also need to change the @rowval parameter to an integer, and use sp_executesql[^] to pass that to your dynamic query as a parameter. (The TOP operator can accept a variable, so you don't need dynamic SQL for that.)

Assuming @oby only contains a single column name, with no modifier, then something like this should work:
SQL
ALTER PROCEDURE [dbo].[GetDataFromTable]
(
    @rowval int,
    @tablename varchar(50),
    @oby varchar(50)
)
As
BEGIN
DECLARE @ObjectID int, @RealTableName sysname, @RealSchemaName sysname, @RealColumnName;
DECLARE @Query nvarchar(max), @Parameters nvarchar(max);
    
    SET NOCOUNT ON;
    SET @ObjectID = OBJECT_ID(@tablename);
    If @ObjectID Is Null RAISERROR('The table "%s" was not found.', 16, 1, @tablename);
    
    SELECT
        @RealTableName = QUOTENAME(T.name),
        @RealSchemaName = QUOTENAME(S.name)
    FROM
        sys.tables As T
        INNER JOIN sys.schemas As S
        ON S.schema_id = T.schema_id
    WHERE
        T.object_id = @ObjectID
    And
        T.type = 'U'
    ;
    
    If @@ROWCOUNT = 0 RAISERROR('The table "%s" was not found.', 16, 1, @tablename);
    
    SELECT
        @RealColumnName = QUOTENAME(name)
    FROM
        sys.columns
    WHERE
        object_id = @ObjectID
    And
        name = @oby
    ;
    
    If @@ROWCOUNT = 0 RAISERROR('The column "%s" was not found in the table "%s".', 16, 1, @oby, @tablename);
    
    SET @Query = N'SELECT TOP (@rowval) * FROM ' + @RealSchemaName + N'.' + @RealTableName + N' ORDER BY ' + @RealColumnName + N' DESC';
    SET @Parameters = N'@rowval int';
    
    EXEC sp_executesql @Query, @Parameters, @rowvalue = @rowval;
END

However, it would be a much better idea to create a separate procedure for each table you need to access.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900