I have a dynamic query which reads like this
Alter PROCEDURE dbo.mySP
-- Add the parameters for the stored procedure here
(
#DBName varchar(50),
#tblName varchar(50)
)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
declare #string as varchar(50)
declare #string1 as varchar(50)
set #string1 = '[' + #DBName + ']' + '.[dbo].' + '[' + #tblName + ']'
set #string = 'select * from ' + #string1
exec #string
END
I am calling like this
dbo.mySP 'dbtest1','tblTest'
And I am experiencing an error
"Msg 203, Level 16, State 2, Procedure mySP, Line 27
The name 'select * from [dbtest1].[dbo].[tblTest]' is not a valid identifier."
What is wrong? and How to overcome?
Thanks in advance
It thinks that the contents of #string refer to a stored procedure name. You need to put
EXEC (#string)
or better use the stored procedure sp_executesql
You should also set up some guard code to check that the values you are passing in are the names of real tables and databases. You can query the views in the INFORMATION_SCHEMA to validate the input.
You can read more on safer dynamic SQL on my blog.
Change
exec #string
To
exec(#string)
Here's a working SP I just tested:
CREATE PROCEDURE [dbo].[test]
#DBName varchar(50),
#tblName varchar(50)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #string AS VARCHAR(50)
DECLARE #string1 AS VARCHAR(50)
SET #string1 = '[' + #DBName + '].[dbo].[' + #tblName + ']'
SET #string = 'select * from ' + #string1
EXEC(#string)
END
if you use EXEC as:
EXEC #String
it is trying to run a procedure with the name contained within the #String variable. try it out:
create procedure TestProc
as
print 'you called TestProc!'
go
declare #string varchar(20)
set #string='TestProc'
exec #string
if you use EXEC as:
EXEC (#Query)
you run the sql within the #Query variable, try it out:
DECLARE #Query varchar(50)
set #Query='Print ''just ran it!'''
EXEC (#Query)
ALTER PROCEDURE test_sp
-- Add the parameters for the stored procedure here
(
#DBName varchar(50),
#tblName varchar(50)
)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON
-- Insert statements for procedure here
declare #string as varchar(100)
declare #string1 as varchar(50)
set #string1 = '[' + #DBName + ']' + '.[dbo].' + '[' + #tblName + ']'
Print #string1
set #string = 'select * from' + #string1
Print #string
exec (#string)
SET NOCOUNT OFF
END
Related
I have to use this exact script below:
ALTER PROCEDURE [dbo].[sp_CheckIfSQLLoginExistsAndCreateLogin]
#SearchDomain NVARCHAR(MAX),
#SearchUsername NVARCHAR(MAX)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(max);
DECLARE #params NVARCHAR(MAX);
IF #SearchUsername != ''
BEGIN
SET #sql = N'IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE [name] = #Domain\#Username) CREATE LOGIN [#Domain\#Username] FROM WINDOWS';
SET #params = N'#Username NVARCHAR(MAX), #Domain NVARCHAR(MAX)';
END
exec sp_executesql #sql, #params, #Username=#SearchUsername, #Domain=#SearchDomain
END
The problem I am having is that every time this SP is called I get the following error:
Using this data: #SearchDomain = OFFICE and #SearchUsername = BJackson
The problem here is you think SQL is a scripting language; it is not. For example '#Domain' means the literal string '#Domain' not replace the literal string '#Domain' with the value in the variable #Domain.
If you have EXEC sys.sp_executesql N'SELECT '#V1 + #V2';', N'#V1 varchar(30), #V2 varchar(30)','This', 'works'; you don't get the value 'Thisworks' you get the value '#V1 + #V2' Why? Because they are literals.
What you need to do is safely inject your parameters for the object names, and properly parametrise your WHERE:
ALTER PROCEDURE [dbo].[CheckIfSQLLoginExistsAndCreateLogin] --removed prefix
#SearchDomain sysname, --Corrected datatype
#SearchUsername sysname --Corrected datatype
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(max);
DECLARE #Login sysname = CONCAT(#SearchDomain,'\',#SearchUsername);
IF #Login != ''
BEGIN
SET #sql = N'IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE [name] = #Login) CREATE LOGIN ' + QUOTENAME(Login) + N' FROM WINDOWS';
END
EXEC sys.sp_executesql #sql, N'#Login sysname', #Login;
END;
Here is a simple example:
CREATE OR ALTER PROCEDURE [dbo].[sp_CheckIfSQLLoginExistsAndCreateLogin]
#SearchDomain NVARCHAR(MAX),
#SearchUsername NVARCHAR(MAX)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(max);
DECLARE #params NVARCHAR(MAX);
IF #SearchUsername != ''
BEGIN
SET #sql = N'IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE [name] = '''+ #SearchDomain +'\'+ #SearchUsername +''') CREATE LOGIN [#Domain\#Username] FROM WINDOWS';
SET #params = N'#Username NVARCHAR(MAX), #Domain NVARCHAR(MAX)';
END
exec sp_executesql #sql, #params, #Username=#SearchUsername, #Domain=#SearchDomain
END
I created a stored procedure which takes a view name and date as parameters and checks if there is record for that date in the view. However, I get the following error
'Operand type clash: date is incompatible with int'.
I am hoping that if the record exists that 1 will be returned else 0 will be returned and I can use that to make a decision in another stored procedure.
The code is listed below
CREATE PROCEDURE [dbo].[usr_RecordExist]
-- Add the parameters for the stored procedure here
#ViewName SYSNAME,
#TransDate Date
--<#Param2, sysname, #p2> <Datatype_For_Param2, , int> = <Default_Value_For_Param2, , 0>
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #DATEVARCHAR nvarchar(4000);
SET #DATEVARCHAR = CONVERT(NVARCHAR, #TransDate, 103);
-- Insert statements for procedure here
DECLARE #SQLCommand NVARCHAR(MAX) =
N'SELECT COUNT(SYMBOL) FROM ' + QUOTENAME(#ViewName) + 'WHERE TRANSDATE = ' + '' + #DATEVARCHAR + '';
EXECUTE [dbo].[sp_executesql]
#sqlCommand;
END
The expression + '' does nothing, use + '''' to add a single quote.
... + '''' + #DATEVARCHAR + '''';
You are using the right tools but in the wrong way, You should not concatenate parameters but pass them as parameters to the system stored procedure sp_executesql as shown below:
CREATE PROCEDURE [dbo].[usr_RecordExist]
#ViewName SYSNAME,
#TransDate Date
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQLCommand NVARCHAR(MAX);
SET #SQLCommand = N'SELECT COUNT(SYMBOL) FROM ' + QUOTENAME(#ViewName)
+ N' WHERE TRANSDATE = #TransDate';
EXECUTE [dbo].[sp_executesql] #sqlCommand
,N'#TransDate Date'
,#TransDate
END
Edit
To get the count in an output parameter you would do the following:
CREATE PROCEDURE [dbo].[usr_RecordExist]
#ViewName SYSNAME,
#TransDate Date,
#Count INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQLCommand NVARCHAR(MAX);
SET #SQLCommand = N'SELECT #Count = COUNT(SYMBOL) FROM ' + QUOTENAME(#ViewName)
+ N' WHERE TRANSDATE = #TransDate';
EXECUTE [dbo].[sp_executesql] #sqlCommand
,N'#TransDate Date, #Count INT OUTPUT'
,#TransDate
,#Count OUTPUT
END
Since you used QUOTENAME() for ViewName why not QUOTENAME(#DATEVARCHAR, '''') or QUOTENAME(#DATEVARCHAR, CHAR(39))
Cosmin got it. Although I also noticed you set #DATEVARCHAR to NVARCHAR(4000) even though convert(NVARCHAR without a length defaults to 30.
I want to create a view that unions specific tables based on their prefix.
It's a database running on SQL 2008 server.
Tables are named as following:
Table_XXX_13_1 ; Table_XXX_14_2 ;Table_XXX_15_3 ; Table_XXX_15_4 ... etc
New view name: 'View_XXX_15'
How should i proceed?
I want to filter on 'Table_XXX_15%', tried to use Select stuff,
Please list some information, I'm new to SQL.
link to similar question: I need to create a view that unions all tables based on their prefix (new tables added monthly)
EDIT
Following code doesn't work:
CREATE PROCEDURE spCreateView2015
#BaseTableName varchar(100),
#View varchar(100),
#s varchar(max),
#v varchar(max)
AS
BEGIN
SET NOCOUNT ON;
set #BaseTableName = 'Table%15%';
set #View = 'View_097_001_test';
set #v =
N'(
select stuff((
select cast('' union all select * from '' as nvarchar(max)) + quotename(''CEE_097_001'')
from information_schema.tables
where table_name like #BaseTableName))
)';
set #s = 'DROP VIEW ' + #View;
EXEC (#s);
set #c = N'CREATE VIEW #View AS ' + #v;
EXEC Sp_executesql #c, N'#View varchar(100), #BaseTableName varchar(100)', #View, #BaseTableName;
OUTPUT:
Procedure or function 'spCreateView2015' expects parameter '#BaseTableName', which was not supplied.
You missing quotation mark near Database and quotation marks should be added when setting dynamic variable #v. Should be something like that:
CREATE PROCEDURE spCreateView2015
#BaseTableName varchar(100) = 'Table_XXX_15%',
#View varchar(100) = 'View_XXX_2015'
AS
BEGIN
SET NOCOUNT ON;
declare #v nvarchar(max)
set #v =
'(
select stuff((
select cast('' union all select * from '' as nvarchar(max)) + quotename(''Database'')
from information_schema.tables
where name like #BaseTableName))
);'
declare #s nvarchar(max) = 'DROP VIEW ' + #View;
EXEC Sp_executesql #s, N'#BaseTableName varchar(100), #View varchar(100)', #BaseTableName, #View
declare #c nvarchar(max) = 'CREATE VIEW ' + #View + ' AS ' + #v;
EXEC Sp_executesql #c, N'#BaseTableName varchar(100), #View varchar(100)', #BaseTableName, #View
END
I am totally confused with this procedure.please correct my mistakes in quotes.
create procedure queryingsfor
#Tabname nvarchar(250),
#colname nvarchar(250),
#opname nvarchar(290),
#valuesname nvarchar(239)
as
begin
set NOCOUNT on;
declare #sql varchar(4000)
set #sql='select * from' +#Tabname+ 'where' +#colname+''''+#opname+''''+ ''''+#valuesname+''''
exec(#sql)
end
exec queryingsfor 'education','eduCurrentStudy','=','DME'
I'm only getting:
Error: Msg 102, Level 15, State 1, Line 1
Incorrect syntax near 'fromeducationwhereeduCurrentStudy'.
You might want to add some spaces in there
set #sql='select * from ' +#Tabname+ ' where '
+#colname+''''+#opname+''''+ ''''+#valuesname+''''
The correct statement would be something like
set #sql='select * from ' +#Tabname+ ' where '
+#colname + #opname+ ''''+#valuesname+''''
Or
even better
set #sql='select * from [' +#Tabname+ '] where
[' +#colname + ']' + #opname+ ''''+#valuesname+''''
To protect you from SQL injection you should do like this instead.
alter procedure queryingsfor
#Tabname nvarchar(250),
#colname nvarchar(250),
#opname nvarchar(4),
#valuesname nvarchar(239)
as
begin
set NOCOUNT on;
declare #sql nvarchar(4000)
set #sql = 'select * from '+quotename(#Tabname)+ ' where ' +quotename(#colname)+#opname+'#valuesname'
exec sp_executesql #sql, N'#valuesname nvarchar(239)', #valuesname
end
I have a block code to create a procedure:
CREATE PROCEDURE GetTableinfomation
#table nvarchar(50),
#column nvarchar(50),
#valuedk nvarchar(50)
AS
BEGIN
SELECT *
FROM #table
WHERE #column = #valuedk
END
and I have an error.
Msg 1087, Level 15, State 2, Procedure GetTableinfomation, Line 7
Must declare the table variable "#tenbang".
Why?
You cannot use SQL parameters for table names and columns, only for variables.
You could get around this by using dynamic SQL:
DECLARE #SQL nvarchar(4000)
DECLARE #PARAMS nvarchar(4000)
SET #SQL = 'SELECT * FROM '
+ QUOTENAME(#table,'"') + ' WHERE '
+ QUOTENAME(#column,'"') + '= #param1'
SET #PARAMS = '#param1 nvarchar(50)'
EXEC sp_executesql #SQL, #PARAMS, #param1=#valuedk
See the documentation on sp_executesql for more information:
http://msdn.microsoft.com/en-us/library/ms188001.aspx