SQL Server stored procedure dynamic Where condition in temporary table - asp.net

I am using ASP.NET and a SQL Server database.
I have options to search values from gridview base on some criteria. The criteria are mostly independent from each other.
For example:
"where ProductType = " + Convert.ToInt32(recordType.persoRecord) +
" and AccountNumber like '%" + SearchValue + "%'";
or
"where fileid=" + File_ID + ShowSearch +
" and lower(j.CardHolderName) like '%" + SearchValue.Trim().ToLower() + "%'
There are a lot of options to search by user. I have millions of rows of data in this table, in order to fetch the data and bind it fast to gridview, I have created a stored procedure.
It works fine for fetching and binding but for searching, it's hard to manage. Due to I don't have much time, i want to configure the stored procedure to 'if there's a searching' fetch the searched data only.
Here is my stored procedure:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[SetJobsGrid]
(#FileID varchar(50),
#PageIndex int = 1,
#PageSize int = 1,
#DynamicQuery NVARCHAR(MAX),
#SearchFlag bit,
#RecordCount int output)
AS
BEGIN
DECLARE #SearchQuerySQL as nvarchar(MAX)
SET NOCOUNT ON;
select ROW_NUMBER()OVER (
order by j.creationdate asc,
j.id desc
) AS RowNumber
, j.id as 'Serial #', j.jobname as 'File Record Name',
j.RecordNumber as 'Record Number',i.issuername as'Issuer Name',p.ProductName as 'Product Name',p.productNumber as 'Product Number',
(j.AccountNumber) as AccountNumber ,j.CardHolderName as 'Card Holder Name', j.CardholdersBranchName as 'Card Holder Branch Name',
j.ShipmentBranchName as 'Shipment Branch Name', j.EmbossingCardholderName as 'Embossing Card Holder Name', j.MaskedPAN as 'PAN',
j.creationdate as 'File Record Creation Date', j.status as 'File Record Status', j.chipdatastatuserror as 'ChipDataStatus Erro',
j.chipdatastatuserrormessage as 'Error Message', j.chipdatastatus as 'Data Prepared',j.isduplicaterecord as 'isduplicate',j.isduplicatefromsamefile as 'IsDuplicateFromSameFile',
j.validationerrors , j.isworkordercreatedForCard,j.isworkordercreatedForPin,j.isworkordercreatedForCarrier,j.PersoMachineId,j.PinMachineId,j.CarrierMachineId
INTO #Results
FROM jobs j join issuer i on j.issuerid=i.id join Product p on p.id=j.productid WHERE fileid = #FileID
IF(#SearchFlag = 1)
begin
select #SearchQuerySQL = 'SELECT ' + #RecordCount + ' = COUNT(*) FROM #Results ' + #DynamicQuery
EXEC(#SearchQuerySQL)
select #SearchQuerySQL = 'SELECT * FROM #Results ' + #DynamicQuery + ' and RowNumber BETWEEN(#PageIndex -1) * #PageSize + 1 AND(((#PageIndex -1) * #PageSize + 1) + #PageSize) - 1'
EXEC(#SearchQuerySQL)
end
ELSE
begin
SELECT #RecordCount = COUNT(*)
FROM #Results
SELECT * FROM #Results
WHERE RowNumber BETWEEN(#PageIndex -1) * #PageSize + 1 AND(((#PageIndex -1) * #PageSize + 1) + #PageSize) - 1
end
DROP TABLE #Results
END
When the SearchFlag is set to true from ASP.NET, I want to fetch only searched value. #DynamicQuery set from asp for example:
WHERE AccountNumber LIKE '%" + SearchValue + "%'"
or with many different case.
When I run this stored procedure as in the above, I get an exception:
Conversion failed when converting the varchar value 'select ' to data type int
Regards

To make the stored procedure use your dynamic where statement, it is better to use sp_executesql
example:
EXEC sp_executesql
N'select * from Employees where Id = #param1',
N'#param1 int'
,#param1 = 1
for more information about dynamic query refer to the following site
SQL Server Dynamic SQL

Too long for a comment but you need to understand why the error occurs to fix it. You have this snippet in your code
select #SearchQuerySQL = 'SELECT ' + #RecordCount +
First, if you intend to assign a scalar value (e.g., #SearchQuerySQL) then you should use SET, not SELECT. That's another topic you can research at your leisure. The assignment expression that follows is where you intended to do string concatenation. Unfortunately, the interpretation of the plus sign operator varies according to datatypes involved.
What happens when the database engine encounters an operator that involves 2 different datatypes. Like any other language, one (or both) must be converted to the same type in order to perform the expressed operation. How does the engine do that? There are rules for datatype precedence. In this case, your int parameter has higher precedence and so those strings in that expression are converted to int. That fails with the error that you encountered.
If you want to write dynamic sql, you need to have an advanced understanding of tsql. You should also consider searching the internet first before trying to reinvent the wheel. Maybe Aaron's article on dynamic pagination might help - but it might be a bit much for you at this point.
And while you're mucking about with things, add some comments within the procedure declaration about the usage you intend to support. No one should have to read your code to understand what it does and how it should be used.

Related

Use a SQL Server stored procedure with unlimited number of multiple criteria instead of a view

I have a page where users select multiple search criteria to retrieve data from a SQL Server 2014 view. The view is grabbing data from a table on a linked server (I am not able to put the view directly on that server, and the table I am reading from has over 800 million rows so copying that data onto the local server isn't going to happen).
Of course, I can't index the view either (on linked server) so I'm trying to find a way to stop the timeouts from happening when the query is run. Is it possible to do something like this in a stored procedure?
SELECT
cast(trees as varchar(3)) as Trees
, MIN(fruitnumber) AS FN_Start
, MAX(fruitnumber) AS FN_End
, COUNT(CASE WHEN fruitType = 'apple' THEN 1 ELSE NULL END) AS apple
, COUNT(CASE WHEN fruitType = 'banana' THEN 1 ELSE NULL END) AS banana
FROM
view_fruitReport
WHERE
(orchard = #orchard) and
and here's where it gets wonky. Users select the orchard from a dropdown (not a combobox because we use IE11 and ajaxtoolkit combo box still doesn't work there) so only one selection possible but.
They are able to add criteria to listboxes. Unlimited criteria. And they don't need to select any of the criteria, they can just search by orchard.
So the rest of the WHERE clause is built based on what they have added to the listboxes.
Like this:
' check if items selected in both listboxes'
If trees_Listbox.Items.Count > 0 Then
If fruitminListBox.Items.Count > 0 Then
'cycle through items in fruitnum listbox to create an "in" clause for sql query'
For Each item As ListItem In trees_Listbox.Items
whereString += String.Join(",", item) + ", "
Next
whereString = Left(whereString, Len(whereString) - 2) + ")"
selectQry += "(" + wherecls + whereString + ")"
whereFNcls = "(fruitNumber between "
For Each itemFNmin As ListItem In fruitminListBox.Items
'create a "between" clause for the min and max FN values entered by user.'
whereOEcls += itemFNmin.Value + " and " + fruitmaxListBox.Items(i).ToString + ") or (fruitNumber between " '(fruitnumber between number and number) or '
i += 1
Next
'trim off the last text portion of the whereOEcls'
whereOEcls = Left(whereOEcls, Len(whereFNcls) - 25)
selectQry += " and (" + whereFNcls + ") GROUP BY trees ORDER BY trees"
fruityData.SelectCommand = selectQry
WeeklyGridView.Visible = True
Else
'see if FN is empty but trees is selected'
For Each item As ListItem In trees_Listbox.Items
whereString += String.Join(",", item) + ", "
Next
whereString = Left(whereString, Len(whereString) - 2)
selectQry += wherecls + whereString + ") GROUP BY trees ORDER BY trees"
fruityData.SelectCommand = selectQry
WeeklyGridView.Visible = True
End If
Else
Essentially ending up with a where clause that could look like this:
WHERE (orchard = #orchard)
and trees in (100,200,300,400)
and fruitnumber between (itemFNmin.Value and itemFNmax.Value)
or fruitnumber between (itemFNmin.Value and itemFNmax.Value)
etc etc etc
Which works except it makes things very ugly and I am certain is a poor way of doing this.
I have no clue if/how I can make these lists of variables pass to a stored procedure as multiple arrays or tables etc.
Probably anything is better than having them tied to a view, whose linked server table isn't even an indexed table (not my fault haha)
For your first question: You can return the Count of each fruit type, but it will have performance implications as it requires a subquery for each one. This also requires that you hard code each possible fruit type in the query. I assume that the fruit types can change or have other types added to them, so this isn't the most desirable in terms of maintenance either. You can't dynamically add columns to a query unless you build SQL in your proc and make use of sp_executesql, which is more convoluted than doing in line SQL in your .Net code.
SELECT
cast(trees as varchar(3)) as Trees
, MIN(fruitnumber) AS FN_Start
, MAX(fruitnumber) AS FN_End
, CASE
WHEN fruitType = 'apple' THEN (SELECT COUNT(fruitType) FROM view_fruitReport WHERE fruitType = 'apple') ELSE NULL
END AS [apple]
, CASE
WHEN fruitType = 'banana' THEN (SELECT COUNT(fruitType) FROM view_fruitReport WHERE fruitType = 'banana') ELSE NULL
END AS [banana]
FROM
view_fruitReport
WHERE
(orchard = #orchard)
For your second question, you can pass in lists/tables into a stored procedure. One method is to pass some sort of delimited string and parse it using T-SQL. I recommend a different approach, however, which is
Table Value Parameters. This is a parameter that acts as a table that you can join with in your stored procedure.
Here is an example for implementing a Table Value Parameter for the Trees column.
You will first need to declare a SQL Type:
CREATE TYPE [dbo].[Trees] AS TABLE (Trees INT)
Then you can reference it in your stored procedure as a parameter that acts as a table. Note that you can't use WITH(NOLOCK) with these and must specify READONLY in the paramter:
CREATE PROCEDURE [dbo].[up_getOrchardInfo]
(
#Trees As [dbo].[Trees] READONLY
, #Orchard INT
)
AS
BEGIN
SELECT
cast(trees as varchar(3)) as Trees
, MIN(fruitnumber) AS FN_Start
, MAX(fruitnumber) AS FN_End
, COUNT(CASE WHEN fruitType = 'apple' THEN 1 ELSE NULL END) AS apple
, COUNT(CASE WHEN fruitType = 'banana' THEN 1 ELSE NULL END) AS banana
FROM
view_fruitReport AS F
INNER JOIN #Trees AS T
ON F.Trees = T.Trees
WHERE
(orchard = #orchard)
END
GO
The above example will filter by the Trees passed in. Note that if want to return everything for the Orchard if #Trees is Null or the count is 0 you will need to include that conditional logic in your stored procedure.
IF (#Trees IS NULL OR (SELECT COUNT(1) FROM #Trees = 0))
BEGIN
--No Join to #Trees
END
ELSE
BEGIN
--Query from above.
END
Finally, on the .Net side you will need to pass in a DataTable object as a Parameter on the SqlCommand with the Type of Structured:
Dim sqlCommand As New SqlCommand("up_getOrchardInfo", sqlConnection.SqlConnection)
sqlCommand.CommandType = CommandType.StoredProcedure
Dim sqlTreesParameter As New SqlParameter("#Trees", SqlDbType.Structured)
sqlOrchardParameter.Direction = ParameterDirection.Input
Dim tblExample As New DataTable
tblExample.Columns.Add("Trees", New Integer().GetType())
Dim drExample As DataRow = tblExample.NewRow()
drExample.Item("Trees") = 100
tblExample.Rows.Add(drExample)
'Adjust if Orchard is a VarChar/String'
Dim sqlOrchardParameter As New SqlParameter("#Orchard", SqlDbType.Int)
sqlOrchardParameter.Direction = ParameterDirection.Input
sqlOrchardParameter.Value = intYourOrchardValue
sqlCommand.Parameters.Add(sqlTreesParameter)
sqlCommand.Parameters.Add(sqlOrchardParameter)
'Execute Dataset
All of this said, you may want to consider making multiple stored procedures. One stored procedure could be optimized for returning everything when only an Orchard is passed and another for when Trees are also passed. This depends on how many parameters you're dealing with.
Maybe there's a UX answer. When you know it's not going to come back for a long time give the user a heads up and confirm if they want to wait or not. (With check box on the confirmation that says, "don't show me this again.")
But don't re-event the godawful estimated file transfer time from Windows Explorer.

SQL use comma-separated values with IN clause

I am developing an ASP.NET application and passing a string value like "1,2,3,4" into a procedure to select those values which are IN (1,2,3,4) but its saying "Conversion failed when converting the varchar value '1,2,3,4' to data type int."
Here is the aspx code:
private void fillRoles()
{
/*Read in User Profile Data from database */
Database db = DatabaseFactory.CreateDatabase();
DbCommand cmd = db.GetStoredProcCommand("sp_getUserRoles");
db.AddInParameter(cmd, "#pGroupIDs", System.Data.DbType.String);
db.SetParameterValue(cmd, "#pGroupIDs", "1,2,3,4");
IDataReader reader = db.ExecuteReader(cmd);
DropDownListRole.DataTextField = "Group";
DropDownListRole.DataValueField = "ID";
while (reader.Read())
{
DropDownListRole.Items.Add((new ListItem(reader[1].ToString(), reader[0].ToString())));
}
reader.Close();
}
Here is my procedure:
CREATE Procedure [dbo].[sp_getUserRoles](#pGroupIDs varchar(50))
AS BEGIN
SELECT * FROM CheckList_Groups Where id in (#pGroupIDs)
END
Here is a workaround I found to do what you are trying to achieve
CREATE Procedure [dbo].[sp_getUserRoles](
#pGroupIDs varchar(50)
)
As
BEGIN
SELECT * FROM CheckList_Groups Where (',' + #pGroupIDs +',' LIKE '%,' + CONVERT(VARCHAR, id) + ',%')
End
This gets your comma delimited list and compares it to the id's(which are represented like so ',1,', ',2,' etc) in the table using LIKE
If you dont want to use dynamic sql, the best way ive found is to create a function which turns a delimited string into a table, something like this works for an Integer list:
CREATE FUNCTION [dbo].[StringToIntList]
(#str VARCHAR (MAX), #delimeter CHAR (1))
RETURNS
#result TABLE (
[ID] INT NULL)
AS
BEGIN
DECLARE #x XML
SET #x = '<t>' + REPLACE(#str, #delimeter, '</t><t>') + '</t>'
INSERT INTO #result
SELECT DISTINCT x.i.value('.', 'int') AS token
FROM #x.nodes('//t') x(i)
ORDER BY 1
RETURN
END
Then use that in your sp:
CREATE Procedure [dbo].[sp_getUserRoles](
#pGroupIDs varchar(50)
)
As
BEGIN
SELECT * FROM CheckList_Groups Where id in (
SELECT ID FROM dbo.StringToIntList(#pGroupIds,',')
)
End
Sure it can't do that,
The generated query would be sth like this
SELECT * FROM CheckList_Groups Where id in ('1,2,3,4')
and sure it can't be executed.
you can build the query in your stored procedure then execute it with exec
'SELECT * FROM CheckList_Groups Where id in (' + #pGroupIDs + ')'
or
SELECT * FROM CheckList_Groups Where charindex(','+id+',' , #pGroupIDs)>0
but you first must add the ',' to start and end of your parameter in your c# code
It is not possible to put those values (the comma separated string) in a parameter-value.
What you'll have to do, is to create the SQL Statement in your stored procedure dynamically, by string concatenation. You'll have to execute it with the sp_executesql stored procedure then.
CREATE PROCEDURE [dbo].[getUserRoles]( #groupIds NVARCHAR(50) )
AS BEGIN
DECLARE #statement NVARCHAR(255)
SELECT #statement = N'SELECT * FROM CheckList_Groups Where id in ( ' + #pGroupIDs + N')'
execute sp_executesql #statement
END
Also, not that I named the SP getUserRoles instead of sp_getUserRoles.
The reason is very simple: when you execute a stored procedure whose name starts with sp_, then SQL Server will first query the master database to find that stored procedure, which causes a performance hit offcourse.
The way you are trying to do this is slightly wrong. You will need to use EXECUTE in order to achieve this.
CREATE PROCEDURE [dbo].[sp_getUserRoles](#pGroupIDs nvarchar(50))
As
BEGIN
EXECUTE (N'SELECT * FROM CheckList_Groups Where id in (' + #pGroupIDs + ')';
END
DECLARE #TagId NVARCHAR(100) = '43,81'
SELECT * FROM TABLE WHERE TagId IN (SELECT TRIM(VALUE) FROM STRING_SPLIT( #TagId , ',' ) )
USE STRING_SPLIT FUNCTION FOR THIS
You need to use SP_executesql to achieve this functionllity
CREATE Procedure [dbo].[sp_getUserRoles](
#pGroupIDs varchar(50)
)
As
BEGIN
EXECUTE sp_executesql
N'SELECT * FROM CheckList_Groups Where id in (#pGroupIDs)',
N'#level varchar(50)',
#level = #pGroupIDs;
End
The IN clause can't take a bound parameter like that. What it's being given when the query is actually created is SELECT * FROM CheckList_Groups Where id in ('1,2,3,4'). Essentially the IN clause is being passed a single string.
First create function -
Just run this code
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[CSVToTable] (#InStr VARCHAR(MAX))
RETURNS #TempTab TABLE
(id int not null)
AS
BEGIN
;-- Ensure input ends with comma
SET #InStr = REPLACE(#InStr + ',', ',,', ',')
DECLARE #SP INT
DECLARE #VALUE VARCHAR(1000)
WHILE PATINDEX('%,%', #INSTR ) <> 0
BEGIN
SELECT #SP = PATINDEX('%,%',#INSTR)
SELECT #VALUE = LEFT(#INSTR , #SP - 1)
SELECT #INSTR = STUFF(#INSTR, 1, #SP, '')
INSERT INTO #TempTab(id) VALUES (#VALUE)
END
RETURN
END
GO
Then -
Use function in bracket with select statment -
DECLARE #LIST VARCHAR(200)
SET #LIST = '1,3'
SELECT Id, Descr FROM CSVDemo WHERE Id IN (SELECT * FROM dbo.CSVToTable(#LIST))

Looking for a utility that converts a SQL statement into a dynamic SQL statement

I am looking for a utility that will convert Oracle SQL to a string that can executed dynamically.
Edit:
Yes, consider this simple SQL
SELECT * FROM TABLE
WHERE COLUMN_NAME = 'VALUE'
I have a utility which for T-SQL which converts the above SQL to a synamic SQL as follows:
BEGIN
DECLARE #Exe_String VarChar(2000)
DECLARE #Qt Char(1)
DECLARE #Cr Char(1)
SET #Qt = Char(39)
SET #Cr = Char(10)
SET #Exe_String = 'SELECT * FROM TABLE ' + #Cr
SET #Exe_String = #Exe_String + 'WHERE COLUMN_NAME = ' + #Qt + 'VALUE' + #Qt + '' + #Cr
PRINT #Exe_String
--Execute (#Exe_String)
END
Granted that the code generated good probably be better, yo get the idea, I hope.
I'm looking for the same type of conversion for Oracle SQL.
Here is a tool that I have used a couple of times. You will have to change the output a little to get it to run but it sure beats having to figure out how to escape all the single ticks.
Sql Tuning
After you click on the link it will take you right to the site and a page with sample SQL. Click the "Static SQL to Dynamic SQL" button and you can see how it works. Then input your own sql you want converted and click the button again. Remove the extra tick (') marks in the end and beginning of each line with the exception of the first and last line and pipes (|) don't need to be there either. Hope this helps.
As a raw translation of your T-SQL to PL/SQL
DECLARE
Exe_String VarChar(2000);
Qt CONSTANT Char(1) := CHR(39);
Cr CONSTANT Char(1) := CHR(10);
BEGIN
exe_string := 'SELECT * FROM TABLE '||Cr;
exe_string := exe_string ||
'WHERE COLUMN_NAME = ' || Qt || 'VALUE' ||Qt || '' ||Cr;
dbms_output.put_line(exe_string);
--
EXECUTE IMMEDIATE exe_string;
END;
The obvious difference is that in Oracle the concatenation operator for strings is || rather than +.
Personally, I have a little string manipluation package (let's call it pstring) that I'd use in a case like this - includes functions like enquote(string), standard constants for newline,tab,etc and the ability to do C-style text replacement.
exe_string :=
pstring.substitute_text('SELECT * FROM %s \n WHERE %s = %s',
table_name,column_name,pstring.enquote(value));
Have you considered using bind variables - i.e. :value - rather than dealing with escaping all the internal quotes? It's a good defence against SQL injection.
Obviously there's some difficulty if you have varying numbers of variables (you need to use DBMS_SQL to link them to the statement rather than a simple EXECUTE IMMEDIATE) but for your simple case it would look like this.
PROCEDURE (table_name IN VARCHAR2, column_name IN VARCHAR2)
IS
Exe_String VarChar(2000);
BEGIN
exe_string :=
pstring.substitute_text('SELECT * FROM %s \n WHERE %s = :value',
table_name,column_name);
dbms_output.put_line(exe_string);
--
EXECUTE IMMEDIATE exe_string USING pstring.enquote(value);
END;
Although of course you have to do something with the results of your SQL.
EXECUTE IMMEDIATE exe_string INTO lresult USING pstring.enquote(value);
Which is difficult when the shape of the table may differ - again, you have to look at Type 4 dynamic SQL (DBMS_SQL).

SQL server conditional select statement

Ok, this might be an easy one, but I just can't get it.
I am creating a page which will query a table with many columns and most items are not unique. I need to be able to get a list of records that match as many of the (up to 4) search criteria as possible.
Example:
I am user searching for the following items, I enter at least one and up to 4 of the items below in a text box:
Name, age, gender, weight (user may or may not fill in all of them).
If he just enters "F" for gender, then he will get a list of thousands of females with their name, age, gender and weight.
However if he enters "F" for gender and "300" for weight, he will get a much smaller list of returned records.
I need to be able to create a sql statement that can perform that search with that functionality.
advTHANKSance
I've used similar to the one below to do what you are trying:
DECLARE #Gender varchar(1)
DECLARE #Age int
DECLARE #Weight int
DECLARE #Name varchar(64)
SELECT * FROM MyTable
WHERE
(#Gender is null OR Gender = #gender)
AND (#weight is null OR Weight = #weight)
AND (#Age is null OR age = #Age)
and (#Name is null OR Name = #Name)
if you were to create a stored procedure (which i would recommend) it would look like this:
CREATE PROCEDURE SelectRecords
#Gender varchar(1),
#Age int,
#Weight int,
#Name varchar(64)
AS
SELECT * FROM MyTable
WHERE
(#Gender is null OR Gender = #gender)
AND (#weight is null OR Weight = #weight)
AND (#Age is null OR age = #Age)
and (#Name is null OR Name = #Name)
What this stored procedure is doing is checking to see if you passed a value in for the specific parameter. If you DID NOT then it will be null and the condition will be true. if you DID then it will not be null and the second condition must evaluate to true for the record to be returned.
I've often seen this done with the following SQL statement (where #gender, #weight, #age, and #name are filled in with data from the user, and gender, weight, age, and name are table fields):
SELECT * FROM MyTable
WHERE
gender = COALESCE(#gender, gender)
AND weight = COALESCE(#weight, weight)
AND age = COALESCE(#age, age)
and name= COALESCE(#name, name)
(Edit: I just wanted to add a short explanation of why this works for anyone not familiar with coalesce. The coalesce function takes the first not-null value of the 2 passed to it. So if there is something in the # parameter, which means the user entered data for that field, it will check if the field equals that user-entered value; if the user didn't enter anything and the # parameter is null, it will test against the second value, which is the field itself - and as the field is always equal to itself, this will return all records - it won't filter based on this field at all.)
I did stuff like this by combining a null check with the parameter. If it was null then everything got included, if not then the other part actually mattered
CREATE myFunnyProc ( #p1 nvarchar(10), #p2 nvarchar(10) ) AS BEGIN
SELECT * FROM dbo.myReallyLongTable table
WHERE
(#p1 is null or table.name LIKE #p1)
AND (#p2 is null or table.age = #p2)
END
#Abe - Your solution will work IF Age and Weight are not between Name and Gender in the SELECT statement. I know cuz I tried it :) on SQL Server 2008. I made a table with many records with NULLs scattered throughout. I also made a proc, and running it as you wrote it would not return rows when age or weight were entered as parameters. I moved the int based params in the SELECT statement to the top or bottom of list, both worked. But nesting the int params between the string params in the select statement and then specifying the int params made it fail...
So why did the original not work?? :) Anyone?
BTW, the COALESCE will not work at all (yes, I tested it and reordered the SELECT statement in the proc). It LOOKED like it should work...
Seeing as you are using ASP.NET, you could take a look at LINQ-to-SQL which solves this in a very elegant way:
var query = db.T_Persons;
if (txtGender.Text != string.Empty)
query = query.Where(x => x.Gender == txtGender.Text);
if (txtWeigth.Text != string.Empty)
query = query.Where(x => x.Weight == int.Parse(txtWeight.Text));
...
Of course, you'll need to be using .NET 3.5 or newer.
The answer is something most people try to stay away from it is dynamic sql.
I would suggest creating a stored procedure you can call for this, but here is the code. You need to put the name of your table in place of tablename.
Declare
#gender varchar(1),
#weight int,
#age int,
#name varchar(100),
#sql varchar(200),
#variableCount int
set #variableCount = 0
set #sql = 'select * from tablename'
if(#gender is not null)
Begin
#sql += ' where gender = #gender'
#vaiableCount = #VariableCount+1
End
if(#weight is not null)
Begin
if(#variableCount = 0)
Begin
#sql += ' Where '
End
else
#sql += ' And '
#sql += 'weight = #weight'
End
if(#age is not null)
Begin
if(#VariableCount = 0)
Begin
#sql += ' where '
End
else
#sql += ' And '
#sql += 'age = #age'
End
if(#name is not null)
Begin
if(#VariableCount = 0)
Begin
#sql += ' where '
End
else
#sql += ' And '
#sql += 'name = #name'
End
execute #sql

Preferred Method to Catch Specific OleDB Error

Ok - I have a situation in which I must execute a dynamically built stored procedure against tables that may, or may not be in the database. The data retrieved is then shunted to a VB.Net backed ASP based report page. By design, if the tables are not present in the database, the relevant data is automatically hidden on the report page. Currently, I'm doing this by checking for the inevitable error, and hiding the div in the catch block. A bit kludgy, but it worked.
I can't include the VB code-behind, but the relevant stored procedure is included below.
However, a problem with this method was recently brought to my attention when, for no apparent reason, the div was being hidden even though the proper data was available. As it turned out, the user trying to select the table in the dynamic SQL call didn't have the proper select permissions, an easy enough fix once I could track it down.
So, two fold question. First and foremost - is there a better way to check for a missing table than through catching the error in the VB.Net codebehind? All things considered, I'd rather save the error checking for an actual error. Secondly, is there a preferred method to squirrel out a particular OLE DB error out of the general object caught by the try->catch block other than just checking the actual stack trace string?
SQL Query - The main gist of the code is that, due to the design of the database, I have to determine the name of the actual table being targeted manually. The database records jobs in a single table, but each job also gets its own table for processing data on the items processed in that job, and it's data from those tables I have to retrieve. Absolutely nothing I can do about this setup, unfortunately.
DECLARE #sql NVarChar(Max),
#params NVarChar(Max),
#where NVarChar(Max)
-- Retained for live testing of stored procedure.
-- DECLARE #Table NvarChar(255) SET #Table = N'tblMSGExportMessage_10000'
-- DECLARE #AcctID Integer SET #AcctID = 10000
-- DECLARE #Type Integer SET #Type = 0 -- 0 = Errors only, 1 = All Messages
-- DECLARE #Count Integer
-- Sets our parameters for our two dynamic SQL calls.
SELECT #params = N'#MsgExportAccount INT, #cnt INT OUTPUT'
-- Sets our where clause dependent upon whether we want all results or just errors.
IF #Type = 0
BEGIN
SELECT #where =
N' AND ( mem.[MSGExportStatus_OPT_CD] IN ( 11100, 11102 ) ' +
N' OR mem.[IngestionStatus_OPT_CD] IN ( 11800, 11802, 11803 ) ' +
N' OR mem.[ShortcutStatus_OPT_CD] IN ( 11500, 11502 ) ) '
END
ELSE
BEGIN
SELECT #where = N' '
END
-- Retrieves a count of messages.
SELECT #sql =
N'SELECT #cnt = Count( * ) FROM dbo.' + QuoteName( #Table ) + N' AS mem ' +
N'WHERE mem.[MSGExportAccount_ID] = #MsgExportAccount ' + #where
EXEC sp_executesql #sql, #params, #AcctID, #cnt = #Count OUTPUT
To avoid an error you could query the sysobjects table to find out if the table exists. Here's the SQL (replace YourTableNameHere). If it returns > 0 then the table exists. Create a stores procedure on the server that runs this query.
select count(*)
from sysobjects a with(nolock)
where a.xtype = 'U'
and a.name = 'YourTableNameHere'

Resources