SQL Asking To Declare Variable That Is Declared - sql-server-2008

I am attempting to run this SQL Syntax, but I keep getting an error of:
Msg 137, Level 15, State 2, Line 1
Must declare the scalar variable "#startDate".
However, it looks to me that the variable is already declared at the beginning of my procedure. Why is the error being thrown, and what do I need to do to fix it?
I am using this syntax to call my stored procedure:
exec [dbo].[DoThis] '01/01/2015','01/31/2015'
And this is full procedure which presents compile error above.
ALTER Procedure [dbo].[DoThis]
(
#startDate datetime,
#endDate datetime
)
As
Declare #storename varchar(500), #dblocation varchar(500), #sql varchar(max)
Select storename, dblocation
INTO #allstores
FROM tbl_allstores
where sales >= '1,000,000'
Declare c1 Cursor For
Select storename, dblocation
FROM #allstores
Open c1
Fetch Next From c1 Into #storename, #dblocation
While ##FETCH_STATUS = 0
Begin
Set #sql = 'Insert Into #storeinfo (storename, employeename, employeeaddress, employeephone) '
+'Select '''+#storename+''' As ''storename'', '
+'employeename, employeeaddress, employeephone '
+'From '+#dblocation+' '
+'where employeestatus = ''Active'' '
+'and CAST(hiredate As Date) BETWEEN CAST(#startDate As Date) AND CAST(#endDate As Date) '
Print(#sql)
exec(#sql)
Fetch Next From c1 Into #storename, #dblocation
End
Close c1
Deallocate c1
Select * from #storeinfo
Drop Table #allstores
Drop Table #storeinfo

When you exec(#sql) this creates a new context with its own local variables, and you have not passed the #startDate value into that context.
Instead, declare parameters for your SQL string like this:
exec sp_executesql #sql, '#startDate datetime, #endDate datetime',
#startDate, #endDate;
These names will now be available to your SQL, and the parameters will be passed to them.
This method is better because it treats the variables as parameters which is safer because it reduces the risk of SQL Injection.
As an additional tip, you should also pass #storeName as a parameter. As far as I know you cannot pass #dbname as a parameter, so you should instead ensure that it is quoted correctly.
So the full thing would be:
Set #sql = 'Insert Into #storeinfo (storename, employeename, employeeaddress, employeephone) '
+' Select #storename As ''storename'', '
+' employeename, employeeaddress, employeephone '
+' From '+QUOTENAME(#dblocation)+' '
+' where employeestatus = ''Active'' '
+' and CAST(hiredate As Date) '
+' BETWEEN CAST(#startDate As Date) AND CAST(#endDate As Date) '
Print(#sql)
exec sp_executesql #sql,
'#startDate datetime, #endDate datetime, #storeName nvarchar(100)',
#startDate, #endDate, #storeName;

Changing to:
+'and CAST(hiredate As Date) BETWEEN CAST(''' + #startDate + ''' As Date) AND CAST(''' + #endDate + ''' As Date) '
I would think would work, or at least be close. Your issue is #startDate and #endDate are not actually declared within the separate scope that is executing your dynamic sql.
Basically exec(#sql) is a separate scope from the stored proc scope that is actually declaring your #startDate and #endDate.

Related

Is it possible to set the select list dynamically in MSSQL server2008?

I have a requirement to populate the select list dynamically, Could you please suggest any solution?
Example:
DECLARE #DATE CHAR
SET #DATE='E.TERMINATION_DATE'
--SET #DATE='E.HIRE_DATE'
SELECT E.ID,E.FIRSTNAME,E.LASTNAME, #DATE FROM DBO.EMPLOYEES E
Is it possible to set the column name in select query?
For some query i need to set the variable #DATE as TERMINATION DATE and for some query i need to set #DATE as E.HIREDATE.
When i will run the query based on the TERMINATION_DATE then i will comment in the other option.
or do you have any other workaround?
You can use the Dynamic sql. But you should read this blog to know about it.
DECLARE #DATE VARCHAR(100),
#SQL VARCHAR(MAX)
IF YOURCONDITION
BEGIN
SET #DATE='E.TERMINATION_DATE'
END
ELSE
BEGIN
SET #DATE='E.HIRE_DATE'
END
SET #SQL = 'SELECT E.ID,E.FIRSTNAME,E.LASTNAME,' + #DATE +' FROM DBO.EMPLOYEES E'
exec sp_executesql #SQL
you can use variables in select query but you should assign that query to some variable and execute that variable
because in SSMS at first it will checks(run time) then it executes. In our scenario the variable is assigning at execution only
so we need to assign it to variable as #coderofcode told
DECLARE #DATE VARCHAR(100),
#SQL VARCHAR(MAX)
SET #DATE='E.TERMINATION_DATE'
SET #SQL = 'SELECT E.ID,E.FIRSTNAME,E.LASTNAME,' + #DATE +' FROM DBO.EMPLOYEES E'
exec(#sql)
I was writing a function for which i need to use execution_date for some case and for some other case i need to use hire_date.
So i think i can now populate this things dynamically and it's a great invention for me. Thanks all specially Giorgi, coder of code and koushik.
I am using this pseudo code here.
--Setting the condition parameters
DECLARE #HIRE_DATE DATETIME,
SET #HIRE_DATE= SELECT MIN(HIRE_DATE) FROM E.EMPLOYEES WHERE E.PERSON_ID>30000
--based on condition setting the variable dynamically.
IF #HIRE_DATE IS NULL
BEGIN
SET #DATE='E.TERMINATION_DATE'
END
ELSE
BEGIN
SET #DATE='E.HIRE_DATE'
END
--setting the sql to execute.
SET #SQL = 'SELECT COUNT(E.NR) AS ACTIVITIES,E.CLI_FIRSTNAME,E.CLI_LASTNAME,E.DEBTORNR,' + #DATE +' FROM DBO.EMPLOYEES E
GROUP BY E.CLI_FIRSTNAME,E.CLI_LASTNAME,E.DEBTORNR,' + #DATE
exec(#sql)

Cast in SQL Server query

I am having a problem with executing one SQL query, Below is my stored procedure
Query
ALTER PROCEDURE ProcName
(
#iID VARCHAR(50),
#AccountID INT
)
AS
SET NOCOUNT ON
DECLARE #Sql VARCHAR(MAX)
SET #Sql = 'DELETE FROM ReferringPhysician WHERE iID IN(' + #iID + ') AND AccountID = '+ #AccountID + ''
EXEC (#Sql)
I am trying to execute this query but it gives me error because i am using exec(), Here in my where condition i am dealing with the string, and in another condition i am dealing with the int, so in second condition i am getting casting error! how can i get through this?
Any help is greatly Appreciated!
Thanks
Your query is susceptible to SQL injection.
One way to avoid the data type problem you are having is to pass proper data types where you can and not use EXEC() (more details here):
DECLARE #sql NVARCHAR(MAX) = N'DELETE dbo.referringPhysician
WHERE iID IN (' + #iID + ') AND AccountID = #AccountID;';
EXEC sp_executesql #sql, N'#AccountID INT', #AccountID;
You can completely protect this from SQL injection by using table-valued parameters and passing in a DataTable or other collection with proper types instead of a comma-delimited string. e.g.:
CREATE TYPE dbo.iIDs TABLE(iID INT PRIMARY KEY);
Now your stored procedure can avoid dynamic SQL altogether:
ALTER PROCEDURE dbo.ProcName -- always use schema prefix!
#iIDs dbo.iIDs READONLY,
#AccountID INT
AS
BEGIN
SET NOCOUNT ON;
DELETE r
FROM dbo.ReferringPhysician AS r
INNER JOIN #iIDs AS i
ON r.iID = i.iID
WHERE r.AccountID = #AccountID;
END
GO
Try this:
ALTER PROCEDURE ProcName
(
#iID VARCHAR(50),
#AccountID INT
)
AS
SET NOCOUNT ON
DECLARE #Sql VARCHAR(MAX)
SET #Sql = 'DELETE FROM ReferringPhysician WHERE iID IN(' + CAST(#iID AS VARCHAR) + ') AND AccountID = '+ CAST(#AccountID AS VARCHAR) + ''
EXEC (#Sql)

Passing a Scalar reference into another Scalar

In SQL Server, I have a stored procedure that accepts a date parameter. That date paramater is then passed along to another stored procedure. However, the 2nd stored procedure parameter is named as the first parameter. and then I need to I have 2 different scalar variables:
DECLARE #date date, #sql varchar(100);
SET #date = '2012-07-01';
SELECT #sql = ('EXEC pStoredprocedure #date = '''+#date+'''')
I need the #sql scalar to contain this text so the stored procedure can call the other stored procedure:
EXEC pStoredprocedure #date = '2012-07-01'
But, when I run my code above, I get this error:
Msg 402, Level 16, State 1, Line 4
The data types varchar and date are incompatible in the add operator.
It's almost like I need to escape the #date operator. Can this be done?
Side note: Yes, I'm trying to dynamically pass along some variables. I understand the dangers of doing so, but doing it this way is much simpler...
The date variable is being used in string concatenation, so it should be treated as a string, either through its declaration, a convert function, or a cast. I tried this:
DECLARE #date varchar(20), #sql varchar(100);
SET #date = '2012-07-01';
SELECT #sql = ('EXEC pStoredprocedure #date = ''' + #date + '''')
PRINT #sql
and got this:
EXEC pStoredprocedure #date = '2012-07-01'
I am stuck on 2005, so I don't have the date datatype, but when I tried to use datetime, I got this error:
Msg 241, Level 16, State 1, Line 4 Conversion failed when converting
datetime from character string.
You can also try
DECLARE #date datetime, #sql varchar(100);
SET #date = '2012-07-01';
SELECT #sql = ('EXEC pStoredprocedure #date = ''' + CONVERT(varchar(20), #date, 102) + '''')
PRINT #SQL;
and get
EXEC pStoredprocedure #date = '2012.07.01'

Conversion failed when converting the nvarchar value 'SELECT * FROM

Using Sql Server 2008, developed a view "vw_MasterView" I get this error:
Conversion failed when converting the nvarchar value 'SELECT * FROM VW_MASTERVIEW v WHERE 1=1 AND v.ClinicId = '' to data type int.
when I run the following stored procedure:
USE [xxxxxxx]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[ClientSummary1]
#LocationId int,
#startDate datetime,
#endDate datetime,
#userName nvarchar(50)
AS
declare #sql nvarchar(2000);
SET NOCOUNT ON
set #sql = 'SELECT * FROM VW_MASTERVIEW v WHERE 1=1 ';
IF #LocationId is not null
begin
set #sql = #sql + ' AND v.LocationId = ''' + #LocationId + '''';
end
if #userName is not null
begin
set #sql = #sql + ' AND (v.FirstUser = '' OR v.SecondUser = '')' + #userName + '''';
end
if #startDate is not null
begin
set #sql = #sql + ' AND v.FirstVisitDate = ''' + #startDate + '''';
end
if #endDate is not null
begin
set #sql = #sql + ' AND v.LastVisitDate = ''' + #endDate + '''';
end
EXEC(#sql)
I get both the LocationId and userName from a VS2010 application.
Thanks in Advance
When appending strings together in SQL Server, you have to cast non-textual types (such as int) to a textual type (such as varchar):
set #sql = #sql + ' AND v.LocationId = ''' +
cast(#LocationId as varchar(10)) + '''';
-- ^^^^ have to cast ^^ make sure size is big enough
Note that dynamic SQL should not be necessary in the first place. You can just run the query directly with the parameters (I implemented the null checks with the extra or conditions):
SELECT * FROM VW_MASTERVIEW v
WHERE (v.LocationId = #LocationId OR #LocationId is null)
AND (v.FirstUser = #userName OR v.Seconduser = #userName OR #userName is null)
AND (v.FirstVisitDate = #startDate OR #startDate is null)
AND (v.LastVisitDate = #endDate OR #endDate is null)
I may not have the logic right for FirstUser and SecondUser - I took an educated guess from your incomplete code.
Hope this helps!

How to CREATE TYPE type_name AS existing_table_name

Is there a way to create table type in SQL Server 2008 based on scheme of existing table?
CREATE TABLE A (id INT, name VARCHAR(30))
CREATE TYPE type_a AS TABLE.A
Something like that.
No, this kind of composable DML is not yet possible. Microsoft has rejected this suggestion in the past, but with enough votes (e.g. more than 1!) it may get reconsidered in the future:
http://connect.microsoft.com/SQLServer/feedback/details/294130/table-valued-parameters-add-support-for-create-type-type-from-table-table-name-options-syntax-construct
You can use following stored procedure to create a type with same schema existing table may have.
Create PROCEDURE [dbo].[Sp_DefineTypeOutOfTableSchema]
#TableNames NVARCHAR(500)
AS
BEGIN
DECLARE #TableName NVARCHAR(100)
DECLARE #strSQL NVARCHAR(max)
DECLARE #strSQLCol NVARCHAR(1000)
DECLARE #ColName NVARCHAR(100)
DECLARE #ColDataTaype NVARCHAR(50)
DECLARE #ColDefault NVARCHAR(50)
DECLARE #ColIsNulable NVARCHAR(50)
DECLARE #ColCharMaxlen NVARCHAR(50)
DECLARE #ColNumPrec NVARCHAR(50)
DECLARE #ColNumScal NVARCHAR(50)
IF LEN(#TableNames) > 0 SET #TableNames = #TableNames + ','
WHILE LEN(#TableNames) > 0
BEGIN
SELECT #TableName = LTRIM(SUBSTRING(#TableNames, 1, CHARINDEX(',', #TableNames) - 1))
DECLARE schemaCur CURSOR FOR
SELECT COLUMN_NAME,DATA_TYPE,IS_NULLABLE,COLUMN_DEFAULT,CHARACTER_MAXIMUM_LENGTH,NUMERIC_PRECISION,NUMERIC_SCALE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME =#TableName
OPEN schemaCur
SELECT #strSQL=''
FETCH NEXT FROM schemaCur
INTO #ColName,#ColDataTaype,#ColIsNulable,#ColDefault,#ColCharMaxlen,#ColNumPrec,#ColNumScal
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #strSQLCol=''
SELECT #strSQLCol= '['+#ColName+'] '+'[' + #ColDataTaype +'] '
IF #ColDataTaype='nvarchar' or #ColDataTaype='char' or #ColDataTaype='varchar' or #ColDataTaype='vchar'
BEGIN
SELECT #strSQLCol=#strSQLCol+ '(' + #ColCharMaxlen +') '
END
ELSE IF #ColDataTaype='numeric' or #ColDataTaype='decimal'
BEGIN
SELECT #strSQLCol=#strSQLCol +'(' + #ColNumPrec +',' +#ColNumScal + ') '
END
IF #ColIsNulable='YES'
BEGIN
SELECT #strSQLCol=#strSQLCol+ 'NULL '
END
ELSE
BEGIN
SELECT #strSQLCol=#strSQLCol+ ' NOT NULL '
END
IF #ColDefault IS NOT NULL
BEGIN
SELECT #strSQLCol=#strSQLCol+ ' DEFAULT(' +#ColDefault + '),'
END
ELSE
BEGIN
SELECT #strSQLCol=#strSQLCol+ ' ,'
END
SELECT #strSQL=#strSQL+#strSQLCol
--print #strSQL
FETCH NEXT FROM schemaCur
INTO #ColName,#ColDataTaype,#ColIsNulable,#ColDefault,#ColCharMaxlen,#ColNumPrec,#ColNumScal
END
CLOSE schemaCur
DEALLOCATE schemaCur
--print #strSQL
SELECT #strSQL=left( #strSQL, len(#strSQL)-1)
--print #strSQL
IF EXISTS (SELECT * FROM sys.types WHERE IS_TABLE_TYPE = 1 AND name = 't_' +#TableName)
BEGIN
EXEC('DROP TYPE t_' +#TableName )
END
SELECT #strSQL = 'CREATE TYPE t_' + #TableName + ' AS TABLE (' + #strSQL + ')'
--print #strSQL
EXEC (#strSQL)
SELECT #TableNames = SUBSTRING(#TableNames, CHARINDEX(',', #TableNames) + 1, LEN(#TableNames))
END
END
you can use it like this
Exec Sp_DefineTypeOutOfTableSchema 'Table1name,Table2name'
You could experiment with creating a function that pulled the table definition out of sysobjects, systypes, syscolumns, syscomments, etc., and built a CREATE statement out of it. You'd just have to make sure to grab all of the important pieces (columns, constraints, comments, etc.) from the various sys tables.
Then call it like... EXEC myCREATEtable #template_table_name or some such...