Dynamic Column Name and Value into Where clause - sql-server

I have a procedure that I working on and I don't know what's going wrong. I have reviewed all other sites and could not find the issue that I'm having
I want to create procedure that has a dynamic where clause base on a combination of bits being sent to the procedure. I don't want to have to create a bunch of similar procedures because they have slightly different conditions.
I'm placing the below query into a cursor then looping through the cursor. Please help.
CREATE PROCEDURE [dbo].[procContainTest] (
#USE_A BIT,
#USE_B BIT,
#ValueA VARCHAR(50),
#ValueB VARCHAR(50),
#USERID VARCHAR(50)
)
AS
DECLARE #TEMP_Col1 INT,
#TEMP_Col2 INT,
#TEMP_Col3 VARCHAR(50),
#TEMP_Col4 VARCHAR(50),
#TEMP_Col5 VARCHAR(50),
#POINT_ONE NVARCHAR(50),
#POINT_TWO NVARCHAR(50)
SET #TRIGGER = 0
WHILE #TRIGGER = 0
BEGIN
-- F2 Booking Term
IF #USE_A = 1
AND #USE_B = 1
BEGIN
SET #POINT_ONE = 'ColName2'
SET #POINT_TWO = 'ColName3'
END
-- F6 Booking Term
IF #USE_A = 0
AND #USE_B = 1
BEGIN
SET #POINT_ONE = 'ColName1'
SET #POINT_TWO = 'ColName2'
END
DECLARE INNER_CURSOR CURSOR
FOR
SELECT TOP 1 TEMP_Col1 INT,
TEMP_Col2,
TEMP_Col3,
#TEMP_Col4,
#TEMP_Col5
FROM TEMP_Table
WHERE #POINT_ONE = + '''' + #ValueA + ''''
AND #POINT_TWO = + '''' + #ValueB + ''''
AND USERID = #USERID
ORDER BY LENGTH

l
You can put your Select Statmement in a variable like:
declare #YourSelectStatement nvarchar(max)
set #YourSelectStatement = ' SELECT TOP 1 TEMP_Col1 INT,
TEMP_Col2,
TEMP_Col3,
FROM TEMP_Table
WHERE ' + #POINT_ONE + '=' + #ValueA + '
AND ' + #POINT_TWO + '=' + #ValueB + '
AND USERID = ' + #USERID + '
ORDER BY LENGTH'
sp_executesql(#YourSelectStatement)

this is probably help you:
SELECT
id, first, last, email, notes
FROM
My_Table
WHERE
CASE ''''+#column_name_variable+''''
WHEN ''''+column_1+''''=1 THEN column_1
WHEN ''''+column_2+''''=2 THEN column_2
...
ELSE 'not null'
END IS NOT NULL

I would avoid the use of dynamic SQL altogether. You can eliminate your IF statements and embed the same logid in your SELECT statement like this:
SELECT TOP 1
TEMP_Col1,
TEMP_Col2,
TEMP_Col3,
TEMP_Col4,
TEMP_Col5
FROM TEMP_Table
WHERE (#USE_A = 1 AND #USE_B = 1 AND ColName2 = '''' + #ValueA + '''' AND ColName3 = '''' + #ValueB + '''')
OR (#USE_A = 0 AND #USE_B = 1 AND ColName1 = '''' + #ValueA + '''' AND ColName2 = '''' + #ValueB + '''')

Related

Getting ever while SQL script input as a string in the stored procedure

This is the code I used to run, but while running I get the following error:
Msg 156, Level 15, State 1, Line 24
Incorrect syntax near the keyword 'AS'.
Msg 153, Level 15, State 2, Line 34
Invalid usage of the option NEXT in the FETCH statement
Code:
ALTER PROCEDURE [dbo].[GetCustomers_Pager] --'RowNumber','asc',0,10,''
(#sortColumn VARCHAR(50),
#sortOrder VARCHAR(50),
#OffsetValue INT,
#PagingSize INT,
#SearchText VARCHAR(50) )
AS
BEGIN
DECLARE #sqlQuery NVARCHAR(MAX) =
'SELECT
ROW_NUMBER() OVER (ORDER BY document_id ASC) AS RowNumber,
document_id AS ID,
document_title AS Title,
document_url AS Download,
online_filing_link AS FileOnline,
CASE
WHEN LTRIM(document_fee) IS NULL THEN ''
ELSE CASE
WHEN LEN(document_fee) = 0 THEN ''
ELSE ''$'' + CONVERT(VARCHAR, document_fee)
END
END AS FilingFee ,
CASE
WHEN LTRIM(document_fee_enhanced) IS NULL THEN ''
ELSE CASE
WHEN LEN(document_fee_enhanced) = 0 THEN ''
ELSE ''$'' + CONVERT(VARCHAR, document_fee_enhanced)
END
END AS EnhancedFee,
COUNT(document_id) OVER() AS FilterTotalCount
FROM
documents';
SET #sqlQuery = #sqlQuery +
' WHERE ((''' + #SearchText + ''' <> '''' AND (document_title LIKE ''%' + #SearchText + '%'' OR FilingFee LIKE ''%' + #SearchText + '%'')) OR ('''+#SearchText+''' = ''''))';
SET #sqlQuery = #sqlQuery + ' ORDER BY ' + #sortColumn + ' ' + #sortOrder;
SET #sqlQuery = #sqlQuery + ' OFFSET ' + CAST(#OffsetValue AS varchar(100)) + ' ROWS FETCH NEXT ' + CAST(#PagingSize AS varchar(100)) + ' ROWS ONLY';
EXEC (#sqlQuery);
END
The error because the generated SQL statement syntax is invalid. The literals THEN '' should be THEN '''' so the resultant statement has matching quotes. I suggest you add a PRINT statement for the generated SQL during debugging like the below example.
More importantly, use parameters for values that vary by execution instead of building the statement with literals for those values. Make sure the values for #SortColumn and #sortOrder are provided from a trusted source or validate the values against the catalog views in the proc code.
CREATE OR ALTER PROCEDURE dbo.GetCustomers_Pager
#sortColumn VARCHAR(50)
, #sortOrder VARCHAR(50)
, #OffsetValue INT
, #PagingSize INT
, #SearchText VARCHAR(50)
AS
DECLARE #sqlQuery NVARCHAR(MAX) =
N'SELECT
ROW_NUMBER() OVER (ORDER BY document_id ASC) AS RowNumber,
document_id AS ID,
document_title AS Title,
document_url AS Download,
online_filing_link AS FileOnline,
CASE
WHEN LTRIM(document_fee) IS NULL THEN ''''
ELSE CASE
WHEN LEN(document_fee) = 0 THEN ''''
ELSE ''$'' + CONVERT(VARCHAR, document_fee)
END
END AS FilingFee ,
CASE
WHEN LTRIM(document_fee_enhanced) IS NULL THEN ''''
ELSE CASE
WHEN LEN(document_fee_enhanced) = 0 THEN ''''
ELSE ''$'' + CONVERT(VARCHAR, document_fee_enhanced)
END
END AS EnhancedFee,
COUNT(document_id) OVER() AS FilterTotalCount
FROM
documents';
SET #sqlQuery += N' WHERE ((#SearchText <> '''' AND (document_title LIKE ''%'' + #SearchText + ''%'' OR FilingFee LIKE ''%'' + #SearchText + ''%'')) OR (#SearchText = ''''))';
SET #sqlQuery += N' ORDER BY ' + #sortColumn + ' ' + #sortOrder;
SET #sqlQuery += N' OFFSET #OffsetValue ROWS FETCH NEXT #PagingSize ROWS ONLY;';
--use PRINT to debug SQL query text
--PRINT #sqlQuery;
EXEC sp_executesql
#sqlQuery
, N'#OffsetValue INT, #PagingSize INT, #SearchText VARCHAR(50)'
, #OffsetValue = 0
, #PagingSize = 10
, #SearchText = '';
GO
EXECUTE [dbo].[GetCustomers_Pager]
#sortColumn ='RowNumber'
,#sortOrder = 'asc'
,#OffsetValue = 0
,#PagingSize = 10
,#SearchText = '';
GO

How to avoid while loop

I have one query and because of this query execution time is increasing and taking time to execute in SQL Server 2016.
SET #Count = (SELECT COUNT(1) FROM #SlabWeight);
WHILE ( #ID <= #Count )
BEGIN
SELECT #Weight = CONVERT(VARCHAR(11), weight)
FROM #SlabWeight
WHERE ID = #ID;
SET #WeightList += '[' + #Weight + '],';
SET #SQL = #SQL + ' ALTER TABLE #TempData ADD ['
+ #Weight + '] DECIMAL(18,2), [' + #Weight
+ '_BasedOn] VARCHAR(10)';
SET #SelectStatment = #SelectStatment + ' , [' + #Weight + '] , [' + #Weight + '_BasedOn]';
SET #ID += 1;
END;
Can anyone tell me how to avoid using a while loop in this query?

mssql dynamic query entity does not get values

hello this my dynamic query and this procedure I did tested is working.
but Does not bring data to the server-side (entity)
visual studio 2012
framework 4.5
entity store procedure
public IEnumerable<spGetInvoiceDetailSearch_Result> GetInvoiceDetailedSearch(InvoiceModel item)
{
return DALContext.GetInvoiceDetailedSearch(item);
}
ALTER PROCEDURE [dbo].[spGetInvoiceDetailSearch] #InvoiceItemID INT
,#InvoiceTypeID INT
,#VesselID INT
,#PaidBy NVARCHAR(50)
,#InvoiceNo NVARCHAR(50)
,#CompanyID INT
,#InvoiceFromDate DATE
,#InvoiceToDate DATE
,#FromDueDate DATE
,#ToDueDate DATE
,#FromAmount DECIMAL(18, 4)
,#ToAmount DECIMAL(18, 4)
,#DueDateType NVARCHAR(50)
AS
BEGIN
DECLARE #SQLQuery AS NVARCHAR(4000)
SELECT #SQLQuery =
'SELECT dbo.Invoices.InvoiceID, dbo.Invoices.CompanyID, dbo.Invoices.VesselID, dbo.Invoices.InvoiceNo, dbo.Invoices.DueDate, dbo.Invoices.Amount,
dbo.Invoices.Comment, dbo.Invoices.IsPaid, dbo.Invoices.PaymentDate, dbo.Invoices.PaidBy, dbo.Invoices.Period, dbo.Invoices.InvoiceDate,
dbo.Invoices.InvoiceCurrencyCode, dbo.Invoices.InvoiceAmount, dbo.Invoices.IsReceived, dbo.Invoices.IsProforma, dbo.Invoices.InvoiceTypeID,
dbo.Invoices.IsDeleted, dbo.Invoices.Parity, dbo.Invoices.DueDateType, dbo.Vessels.Name AS VesselName, dbo.InvoiceVsInvoiceItems.ItemPrice as ItemPrice,
dbo.InvoiceVsInvoiceItems.InvoiceItemID as InvoiceItemID, dbo.InvoiceVsInvoiceItems.VAT as VAT, dbo.InvoiceVsInvoiceItems.ItemType as ItemType, dbo.InvoiceItems.Name AS InvoiceItemName,
dbo.Companies.Name AS CompanyName, dbo.InvoiceTypes.Name AS InvoiceTypeName
FROM dbo.Invoices LEFT OUTER JOIN
dbo.Companies ON dbo.Invoices.CompanyID = dbo.Companies.CompanyID LEFT OUTER JOIN
dbo.InvoiceTypes ON dbo.Invoices.InvoiceTypeID = dbo.InvoiceTypes.InvoiceTypeID LEFT OUTER JOIN
dbo.InvoiceVsInvoiceItems ON dbo.Invoices.InvoiceID = dbo.InvoiceVsInvoiceItems.InvoiceID LEFT OUTER JOIN
dbo.InvoiceItems ON dbo.InvoiceVsInvoiceItems.InvoiceVsInvoiceItemID = dbo.InvoiceItems.InvoiceItemID LEFT OUTER JOIN
dbo.Vessels ON dbo.Invoices.VesselID = dbo.Vessels.VesselID WHERE
dbo.Invoices.IsDeleted != 1
and dbo.Vessels.IsDeleted != 1
and dbo.Companies.IsDeleted != 1 '
SET FMTONLY OFF
IF #InvoiceItemID > 0
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.InvoiceItems.InvoiceItemID= ''' + CAST(#InvoiceItemID AS NVARCHAR(50)) + ''''
END
IF #InvoiceTypeID > 0
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.InvoiceTypeID= ''' + CAST(#InvoiceTypeID AS NVARCHAR(50)) + ''''
END
IF #VesselID > 0
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.VesselID= ''' + CAST(#VesselID AS NVARCHAR(50)) + ''''
END
IF #PaidBy IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + 'AND dbo.Invoices.PaidBy = ''' + CAST(#PaidBy AS NVARCHAR(50)) + ''''
END
IF #InvoiceNo IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + 'AND dbo.Invoices.InvoiceNo = ''' + CAST(#InvoiceNo AS NVARCHAR(50)) + ''''
END
IF #CompanyID > 0
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.CompanyID = ''' + CAST(#CompanyID AS NVARCHAR(50)) + ''''
END
IF #FromAmount IS NOT NULL AND #ToAmount IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.Amount BETWEEN ''' + CAST(#FromAmount AS NVARCHAR(100)) + ''' AND ''' + CAST(#ToAmount AS NVARCHAR(100)) + ''''
END
IF #DueDateType IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + 'AND dbo.Invoices.DueDateType = ''' + CAST(#DueDateType AS NVARCHAR(50)) + ''''
END
IF #InvoiceFromDate IS NOT NULL AND #InvoiceToDate IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.InvoiceDate Between ''' + CAST(#InvoiceFromDate AS NVARCHAR(100)) + ''' AND ''' + CAST(#InvoiceToDate AS NVARCHAR(100)) + ''''
END
IF #FromDueDate IS NOT NULL AND #ToDueDate IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.DueDate Between ''' + CAST(#FromDueDate AS NVARCHAR(100)) + ''' AND ''' + CAST(#ToDueDate AS NVARCHAR(100)) + ''''
END
EXECUTE (#SQLQuery)
END
and end question
my table date type : date format but
server shows it like datetime how can I do to change it to date format..
thank you
regards
ALTER PROCEDURE [dbo].[spGetInvoiceDetailSearch] #InvoiceItemID INT
,#InvoiceTypeID INT
,#VesselID INT
,#PaidBy NVARCHAR(50)
,#InvoiceNo NVARCHAR(50)
,#CompanyID INT
,#InvoiceFromDate DATE
,#InvoiceToDate DATE
,#FromDueDate DATE
,#ToDueDate DATE
,#FromAmount DECIMAL(18, 4)
,#ToAmount DECIMAL(18, 4)
,#DueDateType NVARCHAR(50)
AS
BEGIN
DECLARE #SQLQuery AS NVARCHAR(4000)
SELECT #SQLQuery =
'SELECT dbo.Invoices.InvoiceID, dbo.Invoices.CompanyID, dbo.Invoices.VesselID, dbo.Invoices.InvoiceNo, dbo.Invoices.DueDate, dbo.Invoices.Amount,
dbo.Invoices.Comment, dbo.Invoices.IsPaid, dbo.Invoices.PaymentDate, dbo.Invoices.PaidBy, dbo.Invoices.Period, dbo.Invoices.InvoiceDate,
dbo.Invoices.InvoiceCurrencyCode, dbo.Invoices.InvoiceAmount, dbo.Invoices.IsReceived, dbo.Invoices.IsProforma, dbo.Invoices.InvoiceTypeID,
dbo.Invoices.IsDeleted, dbo.Invoices.Parity, dbo.Invoices.DueDateType, dbo.Vessels.Name AS VesselName, dbo.InvoiceVsInvoiceItems.ItemPrice as ItemPrice,
dbo.InvoiceVsInvoiceItems.InvoiceItemID as InvoiceItemID, dbo.InvoiceVsInvoiceItems.VAT as VAT, dbo.InvoiceVsInvoiceItems.ItemType as ItemType, dbo.InvoiceItems.Name AS InvoiceItemName,
dbo.Companies.Name AS CompanyName, dbo.InvoiceTypes.Name AS InvoiceTypeName
FROM dbo.Invoices LEFT OUTER JOIN
dbo.Companies ON dbo.Invoices.CompanyID = dbo.Companies.CompanyID LEFT OUTER JOIN
dbo.InvoiceTypes ON dbo.Invoices.InvoiceTypeID = dbo.InvoiceTypes.InvoiceTypeID LEFT OUTER JOIN
dbo.InvoiceVsInvoiceItems ON dbo.Invoices.InvoiceID = dbo.InvoiceVsInvoiceItems.InvoiceID LEFT OUTER JOIN
dbo.InvoiceItems ON dbo.InvoiceVsInvoiceItems.InvoiceVsInvoiceItemID = dbo.InvoiceItems.InvoiceItemID LEFT OUTER JOIN
dbo.Vessels ON dbo.Invoices.VesselID = dbo.Vessels.VesselID WHERE
dbo.Invoices.IsDeleted != 1
and dbo.Vessels.IsDeleted != 1
and dbo.Companies.IsDeleted != 1 '
SET FMTONLY OFF
IF #InvoiceItemID > 0
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.InvoiceItems.InvoiceItemID= ' + CAST(#InvoiceItemID AS NVARCHAR(50)) + ''
END
IF #InvoiceTypeID > 0
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.InvoiceTypeID= ' + CAST(#InvoiceTypeID AS NVARCHAR(50)) + ''
END
IF #VesselID > 0
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.VesselID= ' + CAST(#VesselID AS NVARCHAR(50)) + ''
END
IF #PaidBy IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.PaidBy = ''' + CAST(#PaidBy AS NVARCHAR(50)) + ''''
END
IF #InvoiceNo IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.InvoiceNo = ''' + CAST(#InvoiceNo AS NVARCHAR(50)) + ''''
END
IF #CompanyID > 0
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.CompanyID = ' + CAST(#CompanyID AS NVARCHAR(50)) + ''
END
IF #FromAmount IS NOT NULL AND #ToAmount IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.Amount BETWEEN ''' + CAST(#FromAmount AS NVARCHAR(100)) + ''' AND ''' + CAST(#ToAmount AS NVARCHAR(100)) + ''''
END
IF #DueDateType IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.DueDateType = ''' + CAST(#DueDateType AS NVARCHAR(50)) + ''''
END
IF #InvoiceFromDate IS NOT NULL AND #InvoiceToDate IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.InvoiceDate Between ''' + CAST(#InvoiceFromDate AS NVARCHAR(100)) + ''' AND ''' + CAST(#InvoiceToDate AS NVARCHAR(100)) + ''''
END
IF #FromDueDate IS NOT NULL AND #ToDueDate IS NOT NULL
BEGIN
SET #SQLQuery = #SQLQuery + ' AND dbo.Invoices.DueDate Between ''' + CAST(#FromDueDate AS NVARCHAR(100)) + ''' AND ''' + CAST(#ToDueDate AS NVARCHAR(100)) + ''''
END
PRINT (#SQLQuery)
END
First of all, debugging it's very easy. Replace EXEC(#SQLQUery) with print and then you see your actual query.
You had some sintax error ( some places where AND was missing a space in front) and also you have some interger that were treated as strings.
Try my updated procedure.
It seems that procedure is getting called properly but no rows are getting returned, to debug the exact problem you can write actual hardcoded query returning 1 or more records instead of dynamic query.
So after doing that there are two possibilities
procedure call via edmx returns data, that means parameter values are causing some problem.
Any Data is not returned.
To solve any of the problem you need to check corresponding sql query which is getting generated while calling SP via Enitity Framework.

Not able to run a statement because of issue with the sql syntax

This solution is for an unbounded Gridview paging and having problem with the syntax of this query:
> #currTable varchar(20),
#startRowIndex int,
#maximumRows int,
#totalRows int OUTPUT
AS
DECLARE #first_id int, #startRow int
IF #startRowIndex = 1
SET #startRowIndex = 1
ELSE
SET #startRowIndex = ((#startRowIndex - 1) * #maximumRows)+1
SET ROWCOUNT #startRowIndex
DECLARE #sql varchar(250);
SET #sql = 'SELECT ID, StringID_from_Master, GUID, short_Text, lang_String, date_Changed, prev_LangString, needsTranslation, displayRecord, brief_Descrip FROM ' + #currTable + ' ';
EXECUTE(#sql);
PRINT #first_id
SET ROWCOUNT #maximumRows
SELECT #sql = 'SELECT ' + CAST(#first_id as varchar(20)) + ' = ID FROM ' + QUOTENAME(#currTable) + ' ORDER BY ID ' ;
EXEC (#sql);
SET ROWCOUNT 0
-- Get the total rows
SET #sql = 'SELECT ' + + CAST(#totalRowsas varchar(20)) + ' = COUNT(ID) FROM ' + #currTable + ' ';
EXECUTE(#sql);
RETURN
<
The errors is:
Conversion failed when converting the varchar value ''SELECT ' to data type int.
Tried also
nvarchar and varchar. = + CAST(#first_id as varchar(10)) +
If you're trying to implement paging, this is wrong in so many ways. First, you're using SET ROWCOUNT to limit to #startRowIndex, but then you're selecting ALL n rows (with no ORDER BY), then getting the first ID, then counting the total rows by selecting from the table? Might I suggest a better approach?
CREATE PROCEDURE dbo.PageSmarter
#Table NVARCHAR(128), -- table names should not be varchar(20)
#FirstRow INT,
#PageSize INT,
#TotalRows INT OUTPUT
AS
BEGIN
SET NOCOUNT ON; -- always, in every stored procedure
DECLARE
#first_id INT,
#startRow INT,
#sql NVARCHAR(MAX);
SET #sql = N'WITH x AS
(
SELECT
ID,
rn = ROW_NUMBER() OVER (ORDER BY ID)
FROM
' + #Table + '
)
SELECT rn, ID
INTO #x FROM x
WHERE rn BETWEEN ' + CONVERT(VARCHAR(12), #FirstRow)
+ 'AND (' + CONVERT(VARCHAR(12), #FirstRow)
+ ' + ' + CONVERT(VARCHAR(12), #PageSize) + ' - 1);
SELECT first_id = MIN(ID) FROM #x;
SELECT
ID, StringID_from_Master, GUID, short_Text, lang_String, date_Changed,
prev_LangString, needsTranslation, displayRecord, brief_Descrip
FROM ' + #Table + ' AS src
WHERE EXISTS
(
SELECT 1 FROM #x
WHERE ID = src.ID
);';
EXEC sp_executeSQL #sql;
SELECT #totalRows = SUM(row_count)
FROM sys.dm_db_partition_stats
WHERE [object_id] = OBJECT_ID(#Table);
END
GO
DECLARE #tr INT;
EXEC dbo.PageSmarter 'dbo.tablename', 10, 2, #tr OUTPUT;
SELECT #tr;
I haven't tested all edge cases with this specific implementation. I will confess, there are much better ways to do this, but they usually aren't complicated with the additional requirement of dynamic table names. This suggests that there is something inherently wrong with your design if you can run the exact same queries against any number of tables and get similar results.
In any case, you can review some of the (quite lengthy) discussion about various approaches to paging over at SQL Server Central:
http://www.sqlservercentral.com/articles/T-SQL/66030/
There are 62 comments following up on the article:
http://www.sqlservercentral.com/Forums/Topic672980-329-1.aspx
I am guessing your #first_id field is an int. If so, then you need to CAST/Convert your #first_id value to a string/varchar.
CAST(#first_id as varchar(10))
or
Convert(varchar(10), #first_id)
MSDN documentation on CAST/Convert for SQL server
EDIT: After looking at your query again, I notice that you are setting your #first_id = ID, This is incorrect syntax, the correct syntax would be below.
SELECT #sql = 'SELECT ID AS ' + CAST(#first_id as varchar(10)) + ' FROM ' +
QUOTENAME(#currTable) + ' ORDER BY ID ' ;
EXEC (#sql);
It appears you're trying to create an alias for your column ID. The string you're building won't result in a valid SQL statement if it contains a number. It would come out to something like this:
SELECT 123 = ID FROM dbo.MyTable ORDER BY ID
Try this:
SELECT ID AS '123' FROM dbo.MyTable ORDER BY ID
To achieve that:
SELECT #sql = 'SELECT ID AS ''' + CAST(#first_id as varchar(10)) +
''' FROM ' + QUOTENAME(#currTable) +
' ORDER BY ID ' ;
I would do it this way
create table #e (a int)
SET #sql = 'insert #e SELECT COUNT(ID) FROM ' + #currTable + ' ';
exec(#sql)
select #totalRows = a from #e
drop table #e

What are the dangers of dynamic SQL, and can they be avoided?

We've just been given the following code as a solution for a complicated search query in a new application provided by offshore developers. I'm skeptical of the use of dynamic SQL because I could close the SQL statement using '; and then excute a nasty that will be performed on the database!
Any ideas on how to fix the injection attack?
ALTER procedure [dbo].[SearchVenues] --'','',10,1,1,''
#selectedFeature as varchar(MAX),
#searchStr as varchar(100),
#pageCount as int,
#startIndex as int,
#searchId as int,
#venueName as varchar(100),
#range int,
#latitude varchar(100),
#longitude varchar(100),
#showAll int,
#OrderBy varchar(50),
#SearchOrder varchar(10)
AS
DECLARE #sqlRowNum as varchar(max)
DECLARE #sqlRowNumWhere as varchar(max)
DECLARE #withFunction as varchar(max)
DECLARE #withFunction1 as varchar(max)
DECLARE #endIndex as int
SET #endIndex = #startIndex + #pageCount -1
SET #sqlRowNum = ' SELECT Row_Number() OVER (ORDER BY '
IF #OrderBy = 'Distance'
SET #sqlRowNum = #sqlRowNum + 'dbo.GeocodeDistanceMiles(Latitude,Longitude,' + #latitude + ',' + #longitude + ') ' +#SearchOrder
ELSE
SET #sqlRowNum = #sqlRowNum + #OrderBy + ' '+ #SearchOrder
SET #sqlRowNum = #sqlRowNum + ' ) AS RowNumber,ID,RecordId,EliteStatus,Name,Description,
Address,TotalReviews,AverageFacilityRating,AverageServiceRating,Address1,Address2,Address3,Address4,Address5,Address6,PhoneNumber,
visitCount,referalCount,requestCount,imgUrl,Latitude,Longitude,
Convert(decimal(10,2),dbo.GeocodeDistanceMiles(Latitude,Longitude,' + #latitude + ',' + #longitude + ')) as distance
FROM VenueAllData '
SET #sqlRowNumWhere = 'where Enabled=1 and EliteStatus <> 3 '
--PRINT('#sqlRowNum ='+#sqlRowNum)
IF #searchStr <> ''
BEGIN
IF (#searchId = 1) -- county search
BEGIN
SET #sqlRowNumWhere = #sqlRowNumWhere + ' and Address5 like ''' + #searchStr + '%'''
END
ELSE IF(#searchId = 2 ) -- Town search
BEGIN
SET #sqlRowNumWhere = #sqlRowNumWhere + ' and Address4 like ''' + #searchStr + '%'''
END
ELSE IF(#searchId = 3 ) -- postcode search
BEGIN
SET #sqlRowNumWhere = #sqlRowNumWhere + ' and Address6 like ''' + #searchStr + '%'''
END
IF (#searchId = 4) -- Search By Name
BEGIN
IF #venueName <> ''
SET #sqlRowNumWhere = #sqlRowNumWhere + ' and ( Name like ''%' + #venueName + '%'' OR Address like ''%'+ #venueName+'%'' ) '
ELSE
SET #sqlRowNumWhere = #sqlRowNumWhere + ' and ( Name like ''%' + #searchStr + '%'' OR Address like ''%'+ #searchStr+'%'' ) '
END
END
IF #venueName <> '' AND #searchId <> 4
SET #sqlRowNumWhere = #sqlRowNumWhere + ' and ( Name like ''%' + #venueName + '%'' OR Address like ''%'+ #venueName+'%'' ) '
set #sqlRowNum = #sqlRowNum + ' ' + #sqlRowNumWhere
--PRINT(#sqlRowNum)
IF #selectedFeature <> ''
BEGIN
DECLARE #val1 varchar (255)
Declare #SQLAttributes varchar(max)
Set #SQLAttributes = ''
Declare #tempAttribute varchar(max)
Declare #AttrId int
while (#selectedFeature <> '')
BEGIN
SET #AttrId = CAST(SUBSTRING(#selectedFeature,1,CHARINDEX(',',#selectedFeature)-1) AS Int)
Select #tempAttribute = ColumnName from Attribute where id = #AttrId
SET #selectedFeature = SUBSTRING(#selectedFeature,len(#AttrId)+2,len(#selectedFeature))
SET #SQLAttributes = #SQLAttributes + ' ' + #tempAttribute + ' = 1 And '
END
Set #SQLAttributes = SUBSTRING(#SQLAttributes,0,LEN(#SQLAttributes)-3)
set #sqlRowNum = #sqlRowNum + ' and ID in (Select VenueId from '
set #sqlRowNum = #sqlRowNum + ' CachedVenueAttributes WHERE ' + #SQLAttributes + ') '
END
IF #showAll <> 1
set #sqlRowNum = #sqlRowNum + ' and dbo.GeocodeDistanceMiles(Latitude,Longitude,' + #latitude + ',' + #longitude + ') <= ' + convert(varchar,#range )
set #withFunction = 'WITH LogEntries AS (' + #sqlRowNum + ')
SELECT * FROM LogEntries WHERE RowNumber between '+ Convert(varchar,#startIndex) +
' and ' + Convert(varchar,#endIndex) + ' ORDER BY ' + #OrderBy + ' ' + #SearchOrder
print(#withFunction)
exec(#withFunction)
As an aside, I would not use EXEC; rather I would use sp_executesql. See this superb article, The Curse and Blessings of Dynamic SQL, for the reason and other info on using dynamic sql.
See this answer.
Also, these:
Am I immune to SQL injections if I use stored procedures?
Avoiding SQL Injection in SQL query with Like Operator using parameters?
Can I protect against SQL Injection by escaping single-quote and surrounding user input with single-quotes?
Here's an optimized version of the query above that doesn't use dynamic SQL...
Declare #selectedFeature as varchar(MAX),
#searchStr as varchar(100),
#pageCount as int,
#startIndex as int,
#searchId as int,
#venueName as varchar(100),
#range int,
#latitude varchar(100),
#longitude varchar(100),
#showAll int,
#OrderBy varchar(50),
#SearchOrder varchar(10)
Set #startIndex = 1
Set #pageCount = 50
Set #searchStr = 'e'
Set #searchId = 4
Set #OrderBy = 'Address1'
Set #showAll = 1
--Select dbo.GeocodeDistanceMiles(Latitude,Longitude,#latitude,#longitude)
DECLARE #endIndex int
SET #endIndex = #startIndex + #pageCount -1
;
WITH LogEntries as (
SELECT
Row_Number()
OVER (ORDER BY
CASE #OrderBy
WHEN 'Distance' THEN Cast(dbo.GeocodeDistanceMiles(Latitude,Longitude,#latitude,#longitude) as varchar(10))
WHEN 'Name' THEN Name
WHEN 'Address1' THEN Address1
WHEN 'RecordId' THEN Cast(RecordId as varchar(10))
WHEN 'EliteStatus' THEN Cast(EliteStatus as varchar(10))
END) AS RowNumber,
RecordId,EliteStatus,Name,Description,
Address,TotalReviews,AverageFacilityRating,AverageServiceRating,Address1,Address2,Address3,Address4,Address5,Address6,PhoneNumber,
visitCount,referalCount,requestCount,imgUrl,Latitude,Longitude,
Convert(decimal(10,2),dbo.GeocodeDistanceMiles(Latitude,Longitude,#latitude,#longitude)) as distance
FROM VenueAllData
where Enabled=1 and EliteStatus <> 3
And
(
(Address5 like #searchStr + '%' And #searchId = 1) OR
(Address4 like #searchStr + '%' And #searchId = 2) OR
(Address6 like #searchStr + '%' And #searchId = 3) OR
(
(
#searchId = 4 And
(Name like '%' + #venueName + '%' OR Address like '%'+ #searchStr+'%')
)
)
)
And
ID in (
Select VenueID
From CachedVenueAttributes
--Extra Where Clause for the processing of VenueAttributes using #selectedFeature
)
And
(
(#showAll = 1) Or
(#showAll <> 1 and dbo.GeocodeDistanceMiles(Latitude,Longitude,#latitude,#longitude) <= convert(varchar,#range ))
)
)
SELECT * FROM LogEntries
WHERE RowNumber between #startIndex and #endIndex
ORDER BY CASE #OrderBy
WHEN 'Distance' THEN Cast(Distance as varchar(10))
WHEN 'Name' THEN Name
WHEN 'Address1' THEN Address1
WHEN 'RecordId' THEN Cast(RecordId as varchar(10))
WHEN 'EliteStatus' THEN Cast(EliteStatus as varchar(10))
END
The only thing I haven't fixed is the selection from CachedVenueAttributes that seems to build up a where statement in a loop. I think I might put this in a table valued function, and refactor it in isolation to the rest of the procedure.
I like dynamic SQL for search.
Where I have used it in the past I have used .Net prepared statements with any user generated string being passed in as a parameter NOT included as text in the SQL.
To run with the existing solution you can do a number of thing to mitigate risk.
White list input, validate input so that it can only contain a-zA-Z0-9\w (alpha numerics and white space) (bad if you need to support unicode chars)
Execute any dynamic sql as a restricted user. Set owner of stored proc to a user which has only read access to the tables concerned. deny write to all tables ect. Also when calling this stored proc you may need to do it with a user with similar restrictions on what they can do, as it appares MS-SQL executes dynamic sql within a storedproc as the calling user not the owner of the storedproc.
I've realized that this is a really old post, but when doing things like:
AND
(
(#showAll = 1)
OR (#showAll <> 1
AND dbo.GeocodeDistanceMiles(Latitude,Longitude,#latitude,#longitude) <= convert(varchar,#range))
)
... an OPTION(RECOMPILE) will usually help pick a more concise plan, as long as it's not going to be executed a thousand times per second or anything.

Resources