TSQL Stored proc argument error - sql

If I have this stored proc definition minus the body
ALTER PROCEDURE sp_AlloctionReport(#where NVARCHAR(1000), #alldate NVARCHAR(200), #alldateprevweek NVARCHAR(200))
AS
And I call like this.
sp_AllocationReport "ProductCode = 'FA' AND AllocationDate = '20090112' AND VenueInfo.VenueID In(SELECT vf.VenueID FROM VenueFilters vf INNER JOIN FilterTypes ft ON vf.FilterTypeID = ft.FilterTypeID WHERE ft.FilterDescription = 'Coke') AND State = 'NSW'","CampaignAllocations.AllocationDate = '20090112'","CampaignAllocations.AllocationDate = '20090105'"
Why do I get this error when my first argument is defined NVARCHAR(1000).
The identifier that starts with 'ProductCode = 'FA' AND AllocationDate = '20090112' AND VenueInfo.VenueID In(SELECT vf.VenueID FROM VenueFilters vf INNER JOIN Fi' is too long. Maximum length is 128.

Take the where clause and edit it in a new file
Replace ' with '' (single quote -> double single quote)
EXEC dbo.sp_AllocationReport #where= '<THE TEXT EDITED ABOVE>'

The maximum length of a object in sql server in 128 chars (I think). Perhaps a syntax problem when you build the query has made it think that the whole string is a table/view name?

You must use single quotes instead of double quotes unless the connection has QUOTED_IDENTIFIER turned ON
http://msdn.microsoft.com/en-us/library/ms174393.aspx
This is the default from SQL 2000 onwards, but if you've upgraded from an older version then the old default will still be active.

If we had the body of the stored proc, it might be clearer. But ... what it looks like is that SQL Server is interpreting your parameter as an indentifer (i.e., column name). The clue is the error message which states "The identifier that starts with 'ProductCode = 'FA' AND [...] is too long". i.e., SQL Server is looking for a column named "ProductCode = 'FA' AND [...etc...]"
So what I suspect is you've done this in within the stored proc:
SELECT col1, col2, col3, ... FROM table WHERE #where
...and your hoping the where clause to work just like that.
Assuming this is what you've done, it won't work. If this isn't what you've done, the rest of this answer may be completely bogus :-) If you can give an example of the body of the sproc, it might make things clearer.
So assuming my suspision is correct, you need to write it as a dynamic SQL statement like this:
DECLARE #sql NVARCHAR(2000)
SET #sql = 'SELECT col1, col2, col3, ... FROM table WHERE ' + #where
EXEC sp_ExecuteSQL #sql
HOWEVER ... even this isn't the end of the story as this is prone to injection attacks, which are a very bad thing. What you're better off doing is changing the params to your stored proc to make use of parameterised SQL which won't be prone to injection attacks. Something like this...
ALTER PROCEDURE sp_AllocationReport (#ProductCode VARCHAR(10), #AllocationDate DATETIME, {rest of your parameters})
AS
DECLARE #sql NVARCHAR(2000)
SET #sql = 'SELECT col1, col2, col3, ... FROM table WHERE 1 = 1'
IF ISNULL(#ProductCode, '') <> ''
SET #sql = #sql + ' AND ProductCode = #pProductCode'
IF #AllocationDate IS NOT NULL
SET #sql = #sql + ' AND AllocationDate = #pAllocationDate'
{other conditionals, depending on what you need to pass in}
EXEC sp_ExecuteSQL #sql, '#pProductCode VARCHAR(10),
#pAllocationDate DATETIME,
{other passed in params}
', #ProductCode, #AllocationDate
This code isn't prone to injection attacks. It's also more performant as SQL Server will cache execution plans more reliably. Read up about this; there's plenty out there on it.

Check your double quotes around "Coke". The syntax highligher makes the error obvious.
Use single quote for the main string, and double escape them where needed. Yes, it's a pain, but it's the right way to do it.
Prefix NVarchar literals with a capital N
This smells of an sql injection hack waiting to happen. Are you sure you need to pass sql around as string variables?

Related

SQL Server stored procedures to copy tables

I have to move a table into other table using a stored procedure by passing the table names as parameters.
Syntax is:
alter procedure [dbo].[moving]
(
#to_table varchar(50),
#from_table varchar(50)
)
as
begin
EXEC('Select * into '+#to_table+'from '+#from_table)
end
while executing by.
exec moving newtable,hello
It is giving an error:
Incorrect syntax near 'hello'
pls anyone give solution for this
Try:
exec moving 'newtable','hello'
It also looks like you are going to need to fix your SP. You will need a space before from:
EXEC('Select * into '+#to_table+' from '+#from_table)
Read EXECUTE syntax and try,
EXEC moving 'newtable','hello'
SELECT ... INTO needs to create table, if table exists use INSERT INTO ... SELECT ..FROM
AND
in your case you need to run SP in such a way:
EXEC dbo.moving 'table1', 'table2'
BUT
EXEC('Select * into '+#to_table+' from '+#from_table)
will not work, you need to rewrite it with variable:
declare #sql nvarchar(max)
SET #sql = N'Select * into ['+#to_table+N'] from ['+#from_table+N']'
EXEC(#sql)
BUT
you also need to worry of sql injections and complex table names AT LEAST, so - for complex table names I already framed your tables with square braces, and you need to do something to prevent sql injections.
And once more - SELECT...INTO works only if you creating new table with name from #to_table parameter
add an extra space after single quote and FROM
EXEC('Select * into ' + #to_table + ' from ' + #from_table)

SQL: Building where clause

Is there a way to get build a WHERE clause on the fly in a sql statement?
This code is within a Stored Procedure. I have x amount of parameters and each parameter's default value is NULL
SELECT *
FROM MyTable m
WHERE
IF(NOT(#Param1 IS NULL))
m.Col1 = #Param1
END IF
AND
IF(NOT(#Param2 IS NULL))
m.Col2 = #Param2
END IF
[EDIT:] I'm running SQL server 2005.
[EDIT:] The number of parameters are fixed, but can have a NULL value. If a parameter has a NULL value it shouldn't be included in the WHERE clause. Each parameter also correlates to a specific column.
Isn't this equivalent to the following, without any dynamic behavior in it?
SELECT *
FROM MyTable m
WHERE
(#Param1 IS NULL OR m.Col1 = #Param1)
AND
(#Param2 IS NULL OR m.Col2 = #Param2)
Or is there a possibility that the columns themselves might be missing?
Assuming SQL Server 2005+ syntax because the database wasn't specified... Highly recommended reading before addressing the query: The curse and blessings of dynamic SQL
DECLARE #SQL NVARCHAR(4000)
SET #SQL = N'SELECT m.*
FROM MyTable m
WHERE 1 = 1 '
SET #SQL = #SQL + CASE
WHEN #param1 IS NOT NULL THEN ' AND m.col1 = #param1 '
ELSE ' '
END
SET #SQL = #SQL + CASE
WHEN #param2 IS NOT NULL THEN ' AND m.col2 = #param2 '
ELSE ' '
END
BEGIN
EXEC sp_executesql #SQL,
N'#param1 [replace w/ data type], #param2 [replace w/ data type]'
#param1, #param2
END
You may be forced to use dynamic sql whether you're using a stored proc or not. Before you implement this stored proc, think about a few things.
Are the parameters themselves dynamic? Would you use 2 parameters one call, 10 the next? If this is the case you will need to create a ton of "placeholder" stored proc parameters that may not be used every call. What if you define 10 placeholder parameters but then need 11? This is not clean. Maybe you could use some sort of array parameter....
If parameters are dynamic, you will be forced to use dynamic SQL within your proc. This opens you up to injection attacks. You will have to manually escape all input within your stored proc. Using dymamaic sql in a proc defeats one of the main reasons for using procs.
If parameters are truly dynamic, I may actually favor generating sql from the code rather than the stored proc. Why? If you generate from the code you can use the ADO library to escape the input.
Just make sure you do something like....
sql = "select * from table where colA=#colA"; //dyanmically generated
SQlCommand.Parameters.add("#colA", valueA);
And not....
sql = "select * from table where colA=" + valueA; //dyanmically generated the wrong way
Also a 3rd thing to think about. What if the data type of the parameters is dynamic? Stored procs start to fall apart because they are a defined interface. If your operations do not conform to a interface, trying to squish them into a pre-set interface is going to be ugly.
If you're making some sort of open-ended graphical query tool, this situation will pop-up. Most of the time your data access will conform to an interface, but when it doesn't..... stored procs may not be the way to go.

Using Parameter Values In SQL Statement

I am trying to write a database script (SQL Server 2008) which will copy information from database tables on one server to corresponding tables in another database on a different server.
I have read that the correct way to do this is to use a sql statement in a format similar to the following:
INSERT INTO <linked_server>.<database>.<owner>.<table_name> SELECT * FROM <linked_server>.<database>.<owner>.<table_name>
As there will be several tables being copied, I would like to declare variables at the top of the script to allow the user to specify the names of each server and database that are to be used. These could then be used throughout the script. However, I am not sure how to use the variable values in the actual SQL statements. What I want to achieve is something like the following:
DECLARE #SERVER_FROM AS NVARCHAR(50) = 'ServerFrom'
DECLARE #DATABASE_FROM AS NVARCHAR(50) = 'DatabaseTo'
DECLARE #SERVER_TO AS NVARCHAR(50) = 'ServerTo'
DECLARE #DATABASE_TO AS NVARCHAR(50) = 'DatabaseTo'
INSERT INTO #SERVER_TO.#DATABASE_TO.dbo.TableName SELECT * FROM #SERVER_FROM.#DATABASE_FROM.dbo.TableName
...
How should I use the # variables in this code in order for it to work correctly?
Additionally, do you think my method above is correct for what I am trying to achieve and should I be using NVARCHAR(50) as my variable type or something else?
Thanks
There is probably a better way to do this, but what you are probably trying to do in your example is what's called dynamic SQL where you treat the statement as a string and the execute it. This would be section #2 here:
http://www.mssqltips.com/tip.asp?tip=1160
There are some major downsides to dynamic SQL. You see a couple other approaches that might be better in that article.
If you want to execute a dynamically generated query then you have to use sp_ExecuteSQL
HTH
For the nvarchar(50) - you'd be better using sysname. This is a synonym in SQL Server (for nvarchar(128)) and represents the maximum length of an object identifier.
have a look at http://msdn.microsoft.com/en-us/library/ms188001.aspx - sp_executesql takes a parameter that is a string and executes the sql in that string. so you'd need to concatenate #SERVER_FROM and other params with the INSERT INTO part to make the entire sql statement, and then pass to sp_executesql.
nvarchar(50) is fine, unless your server/database names are longer than that :)
You can create the select statement by concatenating all the information together and then use sp_executesql
so:
sp_executesql 'INSERT INTO ' + #SERVER_TO + '.' + #DATABASE_TO +
'.dbo.TableName SELECT * FROM ' + #SERVER_FROM + '.' +
#DATABASE_FROM+'.dbo.TableName'
I like to make templates for dynamic SQL things like this - it's a lot easier to maintain complex statements and also sometimes easier to handle nested quotes - and definitely easier when terms need to be repeated in multiple places (like column lists):
DECLARE #sql AS nvarchar(max);
SET #sql = 'INSERT INTO {#SERVER_TO}.{#DATABASE_TO}.dbo.TableName
SELECT *
FROM {#SERVER_FROM}.{#DATABASE_FROM}.dbo.TableName'
SET #sql = REPLACE(#sql, '{#SERVER_TO}', QUOTENAME(#SERVER_TO))
SET #sql = REPLACE(#sql, '{#DATABASE_TO}', QUOTENAME(#DATABASE_TO))
SET #sql = REPLACE(#sql, '{#SERVER_FROM}', QUOTENAME(#SERVER_FROM))
SET #sql = REPLACE(#sql, '{#DATABASE_FROM}', QUOTENAME(#DATABASE_FROM))
EXEC(#sql)

SQL Update columns passing into the query the column name and value

I have the following code:
UPDATE myTable
SET Col1 = #Value
However, I have a table that has over a 100 columns and want to be able to specify a column name by passing the name into the query similar to:
UPDATE myTable
SET #ColName = #Value
When I do this I get an error. Is there a good solution to this? Its probably something simple!
Thank you in advanced.
You'll have to use dynamic SQL, and write it to make sure you don't let Little Bobby Tables in. Something like this:
DECLARE #sql NVARCHAR(500)
SET #sql = N'UPDATE myTable SET ' + QUOTENAME(#colName) + ' = #pUpdateVal'
EXEC sp_ExecuteSQL #sql, '#pUpdateVal NVARCHAR(20)', #value
Make sure you change the type of #pUpdateVal to something appropriate for your environment, but this will mitigate the risk of injection attacks.
You'd have to revert to dynamic SQL to do this.
Agreed with the others, you'll need dynamic SQL for this; you can't define object names at run time in native SQL. For a full discussion on dynamic SQL see http://www.sommarskog.se/dynamic_sql.html

SQL Server: Sanitizing #param against injection attacks

For the sake of argument, let's just say I have to create a local variable containing a SQL query that has an INSERT:
DECLARE #insert NVARCHAR(MAX)
SELECT #insert = 'INSERT INTO [dbo].[' + #table + '] VALUES...
EXEC (#insert)
This INSERT is also going to contain a column value:
DECLARE #insert NVARCHAR(MAX)
SELECT #insert =
'INSERT INTO [dbo].[' + #table + '] VALUES (N''' + #message + ''')'
EXEC (#insert)
Now, I'm obviously concerned about an injection attack, and would like to ensure that #message's value can't make #insert's value malicious or malformed as a query to EXEC.
This brings us to my question: is escaping the ' characters in #message sufficient? Are there any other characters that could appear in #message that could escape out?
Example:
DECLARE #insert NVARCHAR(MAX)
SELECT #message = REPLACE(#message,'''','''''')
SELECT #insert =
'INSERT INTO [dbo].[' + #table + '] VALUES (N''' + #message + ''')'
EXEC (#insert)
(When I say "have to", this is because my query is in a stored procedure, and this stored procedure accepts #table, which is the destination table to INSERT into. I'm not interested in discussing my architecture or why the table to INSERT into is "dynamically" specified via a procedure parameter. Please refrain from commenting on this unless there's another way besides EXEC()ing a query to specify a table to INSERT into when then table name is received as a procedure parameter.)
Use sp_executesql and the built-in quotename(). This article, The Curse and Blessings of Dynamic SQL, is pretty much the definitive reference.
You could first query the schema information with regular T-SQL and make sure the table name exists first. This way, if it's malformed SQL, it won't execute as code. It will just be a VARCHAR table name.
DECLARE #Table AS VARCHAR(MAX)
DECLARE #Exists AS BIT
SET #Table = 'Vicious malformed dynamic SQL'
SELECT #Exists = COUNT(TABLE_NAME)
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = #Table
IF (#Exists = 1)
BEGIN
PRINT 'Table exists'
-- Execute dynamic SQL.
END
ELSE
PRINT 'Invalid table'
(Or simply use IF EXISTS (SELECT ....) )
Rather than calling EXEC(#somesql), I suggest using the sp_executesql stored procedure. Specifically, this allows you to pass parameters, and the system will check that the parameters are valid.
Apparently there's a 128-length limit to quotename(), even in 2008 according to my test, since it expects a SQL identifier. The reference suggests creating a quotestring() function, which does the same thing as:
REPLACE(#variable,'''','''''')
Therefore I am proposing that the answer is to create a function out of the REPLACE() above, like so:
CREATE FUNCTION quotestring(#string nvarchar(MAX))
RETURNS nvarchar(MAX) AS
BEGIN
RETURN(REPLACE(#string,'''',''''''))
END
...Unless I've misunderstood something.
When writing dynamic SQL you'll want to parameterise as much as possible, and only resort to character escaping when you absolutely have to. You can't parameterise #table, but you can parameterise #message.
DECLARE #insert NVARCHAR(MAX)
set #insert = 'INSERT INTO [dbo].' + quotename(#table) + ' values(#message)'
exec sys.sp_executesql #insert, N'#message nvarchar(max)', #message = #inMessage;
There are a lot of ways attackers can exploit dynamic SQL, including buffer length attacks and using unicode equivalent characters. I encountered an example once where escaping the single quote char still had a vulnerability where one of the unicode equivalents of the quote char could be passed in. Part of the software stack was doing a unicode to ascii conversion, so it was possible to inject a quote back in after they were escaped. Ouch.