Cast in SQL Server query - sql-server-2008

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)

Related

How to pass list of items as parameter to a stored procedure

I have a stored procedure
create PROCEDURE [dbo].[SP]
(
#OrderList varchar(500)
)
AS
Begin
select *
from table
where id in ('+ #OrderList +')
Here I am passing orderlist....
When I execute like this
exec sp 'iss005,iss006'
I am not getting data
but when I hardcode in sp like this ...
select * from table where id in ('iss005','iss006')
then am getting data...
Thank you
Unfortunately it won't work that way. If you change your procedure to something like the following, this will work:
Create Procedure dbo.SP
#OrderList varchar(500)
AS
Declare #SQL VarChar(1000)
Select #SQL = 'SELECT * FROM table '
Select #SQL = #SQL + 'WHERE id in (' + #OrderList +')'
Exec ( #SQL)
GO
Looking more into your query, your ID's value varchar, so the procedure will fail as you'll still be getting :
WHERE id in (iss005,iss006)
when you want :
WHERE id in ('iss005','iss006')
You would need to either pass in the quote values, e.g. :
#OrderList = 'iss005','iss006'
Or work out some SQL to split the #OrderList by comma and use the QUOTENAME() function to add the quotes to the new variable.
I strongly recommend in this case the use of XML parameters, will give you a lot of flexibility.
Your XML might be something like
<ids>
<id>iss006</id>
<id>iss005</id>
</ids>
Your procedure should be something like this:
create PROCEDURE [dbo].[SP]
(
#OrderList XML
)
AS
Begin
select * from table
where id in (
select ParamValues.ID.value('.','VARCHAR(50)')
FROM #OrderList.nodes('/ids/id') as ParamValues(id)
)
Besides the use of store procedures outputs I also would recommend the use of functions but that is up to you.
Regards.
I had the same kind of requirement. i was getting list of user in a int list variable and i need to get all the order of those user. I have use a very simple trick which had solve my issue. please find the code.
public DataTable GetAllOrderData(List<int> UserID)
{
try
{
string listofuser = String.Join(",", UserID.ToArray());
SqlParameter[] parameters = new SqlParameter[]
{
new SqlParameter("#USERID", listofuser)
};
return SqlDBHelper.ExecuteParamerizedSelectCommand("GetOrderByUserID", System.Data.CommandType.StoredProcedure, parameters);
}
finally { UserID = null; }
}
And this is the stored procedure
CREATE PROCEDURE [dbo].[GetOrderByUserID] (#USERID varchar(700))
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
Declare #SQL VarChar(1000)
Select #SQL = 'SELECT *,ORM.OrganisationName FROM OrderTransaction ORT LEFT JOIN OrganisationMaster ORM ON (ORT.OrganisationID=ORM.OrganisationID) '
Select #SQL = #SQL + 'WHERE ORT.CreatedBy IN (' + #USERID +')'
Exec ( #SQL)
END

copy entire row (without knowing field names)

Using SQL Server 2008, I would like to duplicate one row of a table, without knowing the field names. My key issue: as the table grows and mutates over time, I would like this copy-script to keep working, without me having to write out 30+ ever-changing fields, ugh.
Also at issue, of course, is IDENTITY fields cannot be copied.
My code below does work, but I wonder if there's a more appropriate method than my thrown-together text string SQL statement?
So thank you in advance. Here's my (yes, working) code - I welcome suggestions on improving it.
Todd
alter procedure spEventCopy
#EventID int
as
begin
-- VARS...
declare #SQL varchar(8000)
-- LIST ALL FIELDS (*EXCLUDE* IDENTITY FIELDS).
-- USE [BRACKETS] FOR ANY SILLY FIELD-NAMES WITH SPACES, OR RESERVED WORDS...
select #SQL = coalesce(#SQL + ', ', '') + '[' + column_name + ']'
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = 'EventsTable'
and COLUMNPROPERTY(OBJECT_ID('EventsTable'), COLUMN_NAME, 'IsIdentity') = 0
-- FINISH SQL COPY STATEMENT...
set #SQL = 'insert into EventsTable '
+ ' select ' + #SQL
+ ' from EventsTable '
+ ' where EventID = ' + ltrim(str(#EventID))
-- COPY ROW...
exec(#SQL)
-- REMEMBER NEW ID...
set #EventID = ##IDENTITY
-- (do other stuff here)
-- DONE...
-- JUST FOR KICKS, RETURN THE SQL STATEMENT SO I CAN REVIEW IT IF I WISH...
select EventID = #EventID, SQL = #SQL
end
No, there isn't any magic way to say "SELECT all columns except <foo>" - the way you're doing it is how you'll have to do it (the hack in the other answer aside).
Here is how I would alter your code, with these changes (some are hyperlinked so you can read my opinion about why):
use sys.columns over INFORMATION_SCHEMA.COLUMNS
use nvarchar instead of varchar
use scope_identity instead of ##identity
use sp_executesql instead of exec
use stuff instead of coalesce
use SET NOCOUNT ON
add semi-colons
use the schema prefix
use QUOTENAME since it's safer than '[' + ... + ']'
ALTER PROCEDURE dbo.spEventCopy
#EventID INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql += ',' + QUOTENAME(name)
FROM sys.columns
WHERE [object_id] = OBJECT_ID('dbo.EventsTable')
AND is_identity = 0;
SET #sql = STUFF(#sql, 1, 1, '');
SET #sql = N'INSERT dbo.EventsTable(' + #sql + ')
SELECT ' + #sql + ' FROM dbo.EventsTable
WHERE EventID = ' + CONVERT(VARCHAR(12), #EventID) + ';';
EXEC sp_executesql #sql;
SELECT #EventID = SCOPE_IDENTITY();
-- do stuff with the new row here
SELECT EventID = #EventID, SQL = #SQL;
END
If you know the what your identity column is called (and it won't be the column changing), you could do this:
SELECT * INTO #dummy FROM EventsTable where EventID = #EventID;
ALTER TABLE #dummy
DROP COLUMN MyIdentityColumn
INSERT EventsTable SELECT * FROM #dummy
DROP TABLE #dummy
Since a table can only every have one identity column, specifying that in the query shouldn't limit you too much.
As Aaron Bertrand points out, there are risks associated with this approach. Please read the discussion in the comments below.

how to use openrowset to execute a stored procedure with parameters

I'm creating a stored procedure which gets some parameters and in turn these parameters are sent to another stored procedure which I'm calling from openrowset but I'm getting some syntax errors.
CREATE PROCEDURE UpdatePrevFYConfigData
-- Add the parameters for the stored procedure here
#startDate datetime,
#endDate datetime,
#productGroup varchar(8000) = 'All',
#projectType varchar(500) = 'All',
#businessUnit nvarchar(50) = 'All',
#developmentLocation nvarchar(100) = 'All'
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
declare #start varchar(50)
declare #end varchar(50)
set #start = cast(#startDate as varchar(40))
set #end = cast(#endDate as varchar(40))
-- Insert statements for procedure here
select round(avg(a.DeviationDeadline),2) as DeviationDeadline,
round(avg(a.DeviationDefinition),2) as DeviationDefinition,
round(avg(a.DeviationRDCosts),2) as DeviationRDCosts,
round(avg(a.FunctionsAdded) + avg(a.FunctionsDeleted),2) as NotRealizedFuncs,
round(avg(a.DeviationPM2000Aufwand),2) as DeviationPM200Aufwand,
round(avg(b.Defect),2) as Defect
into #tempTable
from openrowset('SQLNCLI',
'Server=.\sqlexpress;Trusted_Connection=yes;',
'SET NOCOUNT ON;SET FMTONLY OFF;EXEC [BSC_DB].dbo.SelectScorecardGraphData
'''+#start+''',
'''+#end+''',
'''+#productGroup+''',
'''+#projectType+''',
''1'',
''0'',
''All'',
''Current'',
'''+#businessUnit+''',
'''+#developmentLocation+'''
') as a,
openrowset('SQLNCLI', 'Server=.\sqlexpress;Trusted_Connection=yes;', 'SET NOCOUNT ON;SET FMTONLY OFF;EXEC [BSC_DB].dbo.GetSPCDefectDistributionData
'''+cast(#startDate as varchar(40))+''',
'''+cast(#endDate as varchar(40))+''',
''Defect'',
'''+#projectType+''',
'''+#productGroup+''',
'''+#businessUnit+''',
'''+#developmentLocation+'''') as b
update dbo.EA_ProcessScorecard_Config_Tbl
set EPC_Deviation = case EPC_Metric
when 'PM200' then (select DeviationDefinition from #tempTable)
when 'PM300' then (select DeviationDeadline from #tempTable)
when 'Cost' then (select DeviationRDCosts from #tempTable)
when 'PM150' then (select DeviationPM200Aufwand from #tempTable)
when 'Defect' then (select Defect from #tempTable)
when 'Funcs' then (select NotRealizedFuncs from #tempTable)
END
where EPC_Description = 'PrevFY' and EPC_FYYear = '0'
drop table #tempTable
END
GO
I'm not able to create it and I get the error message:
Msg 102, Level 15, State 1, Procedure UpdatePrevFYConfigData,
Line 38 Incorrect syntax near '+'.
... but if I use hard coded values for the parameters it works!!
Please help!
Both OPENROWSET and OPENDATASOURCE should be used only for accessing external data for, let's say, quick and dirty solutions, or when it is not possible to configure a permanent linked server. These functions do not provide all of the functionality available from a linked server.
The arguments of OPENROWSET and OPENDATASOURCE do not support variables. They have to be specified as string-literal. If variables need to be passed in as arguments to these functions, a query string containing these variables can be constructed dynamically and executed using the EXEC statement.
Similar to (not syntax checked)
DECLARE #sqlCommand varchar(1000)
SET #sqlCommand = 'SELECT *
FROM OPENROWSET(''SQLNCLI'',''server=.\sqlexpress;Trusted_Connection=yes'',''SET NOCOUNT ON;SET FMTONLY OFF;EXEC [BSC_DB].dbo.SelectScorecardGraphData ''''' + cast(#param1 as varchar(10)) + ''''',''' + cast(#param2 as varchar(n)) ''')'
EXEC #sqlCommand
And so on...
Hope that helps. Kind regards,
Stefan
-- FOR USING OPENROWSETS
EXEC sp_configure 'Ad Hoc Distributed Queries'
,1
RECONFIGURE
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = 'INSERT INTO #TABLESIZESYEAR SELECT NULL AS [TABLE NAME], * FROM OPENROWSET
(''SQLOLEDB'',''Server=(local);TRUSTED_CONNECTION=YES;'',''set fmtonly off EXEC one.[dbo].[InvestigateDataGrowthByYearAndClient] #pDATECOLUMN =' + #YEARCOLUMN + ' ,
#pTABLENAME = ' + #TABLENAME + ' WITH RESULT SETS(
([YEAR NAME] NVARCHAR(5) NULL
, [NUMBER OF ROWS] CHAR(11)
, [RESERVED SPACE] VARCHAR(18)
, [DATA SPACE] VARCHAR(18)
, [INDEX SIZE] VARCHAR(18)
, [UNUSED SPACE] VARCHAR(18) )
)
;'') '
DECLARE #ParmDefinition NVARCHAR(500) = '#pDATECOLUMN NVARCHAR(20)
,#YEARCOLUMN NVARCHAR(20)
,#pTABLENAME NVARCHAR(60)';
EXECUTE sp_executesql #sql
,#ParmDefinition
,#YEARCOLUMN = #YEARCOLUMN
,#pDATECOLUMN = #YEARCOLUMN
,#pTABLENAME = #TABLENAME

Dynamic sql insert into returns 'invalid column name'

I'm trying my first dynamic sql stored procedure. I need to append the exact same records into multiple tables with the same column names. What I have compiles, but when it runs I get 'invalid column name 'TradeDate. The driver sproc is first below, then the sproc containing the dynamic statement. If anyone could help, that'd be great..
ALTER PROCEDURE dbo.StoredProcedure2
AS
DECLARE #tableName varchar(120)
SET #tableName = 'tblDailyATR'
EXEC sprocAddDatesAndSymbolsToAggregatedStudy #tableName
RETURN
ALTER PROCEDURE dbo.sprocAddDatesAndSymbolsToAggregatedStudy
#table varchar(120)
AS
DECLARE #tableName varchar(120)
SET #tableName = #table
EXEC(
'INSERT INTO ' + #tableName + '(Symbol, TradeDate)
SELECT Symbol, TradingDate
FROM (SELECT tblSymbolsMain.Symbol, tblTradingDays.TradingDate
FROM tblSymbolsMain CROSS JOIN tblTradingDays
WHERE (tblTradingDays.TradingDate <= dbo.NextAvailableDataDownloadDate())) AS T1
WHERE (NOT EXISTS (SELECT TradeDate, Symbol
FROM' + #tableName +
' WHERE (TradeDate = T1.TradingDate) AND (Symbol = T1.Symbol)))')
RETURN
You're missing a space after the "FROM" in this line:
FROM' + #tableName +
Should be
FROM ' + #tableName +
Otherwise it's going to try running SELECT FROMTABLE.

Something equivalent to "SELECT * FROM (SELECT table_name FROM...)"?

This query runs, but it produces the name of a table as a result, rather than actually selecting from that table.
SELECT T.*
FROM (SELECT tablename
FROM ListOfTables
WHERE id = 0) AS T
where ListOfTables contains id=0, tablename='some_table', I want to return the same result set as if I had written this directly:
SELECT * FROM some_table
Is there a native way to do this in MySQL 5, or do I have to do in in the application?
To do this in MySQL, you need to create a prepared statement which you can only create from a user variable:
SELECT #tn := tablename FROM ListOfTables WHERE id = 0;
SET #qs = CONCAT('SELECT * FROM ', #tn);
PREPARE ps FROM #qs;
EXECUTE ps;
You need to use dynamic SQL to get this result (the below code assumes SQL Server, I can't speak for other RDBMS').
declare #tableName varchar(100)
declare #query varchar(500)
select #tableName = tablename
from ListOfTables
where id = 0
select #query = 'select * from ' + #tableName
exec (#query)
Almost the same as #Shark's answer, except you also quote the name of the table to avoid syntax errors.
-- Using variables just for better readability.
DECLARE #Name NVARCHAR(4000)
DECLARE #Query NVARCHAR(4000)
-- Get the relevant data
SET #Name = QUOTENAME(SELECT tablename FROM ListOfTables WHERE id=0)
-- Build query
SET #Query = 'SELECT * FROM ' + #Schema + '.' + #Name + ''
-- execute it.
EXEC(#Query)