I'm attempting to create a pure t-sql representation of the default SHA-1 password hashing in the ASP.Net Membership system. Ideally, what I would get would be this:
UserName Password GeneratedPassword
cbehrens 34098kw4D+FKJ== 34098kw4D+FKJ==
Note: that's bogus base-64 text there. I've got base64_encode and decode functions that round-trip correctly. Here's my attempt, which doesn't work:
SELECT UserName, Password, dbo.base64_encode(HASHBYTES('SHA1', dbo.base64_decode(PasswordSalt) + 'test')) As TestPassword FROM aspnet_Users U JOIN aspnet_membership M ON U.UserID = M.UserID
I've tried a number of variations on the theme, to no avail. I need to do this in pure T-Sql; involving a console app or something like that will double the work.
So if anyone can supply what precisely the syntax should be to duplicate that password from the ASP.Net membership stuff, I would greatly appreciate it.
I wrote a hashing stored proc by reverse enginering the C# code from here ASP.NET Identity default Password Hasher, how does it work and is it secure? and some fantastic PBKDF2 SQL functions from here Is there a SQL implementation of PBKDF2?
First create these two functions taken from Is there a SQL implementation of PBKDF2?
create FUNCTION [dbo].[fn_HMAC]
(
#hash_algorithm varchar(25),
#key VARCHAR(MAX),
#message VARCHAR(MAX)
)
RETURNS VARCHAR(MAX)
AS
BEGIN
--HASH key if longer than 16 characters
IF(LEN(#key) >64)
SET #key = HASHBYTES(#hash_algorithm,#key)
DECLARE #i_key_pad VARCHAR(MAX), #o_key_pad VARCHAR(MAX), #position INT
SET #position = 1
SET #i_key_pad = ''
SET #o_key_pad = ''
--splice ipad & opod with key
WHILE #position <= LEN(#key)
BEGIN
SET #i_key_pad = #i_key_pad + CHAR(ASCII(SUBSTRING(#key, #position, 1)) ^ 54)
SET #o_key_pad = #o_key_pad + CHAR(ASCII(SUBSTRING(#key, #position, 1)) ^ 92)
SET #position = #position + 1
END
--pad i_key_pad & o_key_pad
SET #i_key_pad = LEFT(#i_key_pad + REPLICATE('6',64),64)
SET #o_key_pad = LEFT(#o_key_pad + REPLICATE('\',64),64)
RETURN HASHBYTES(#hash_algorithm,CONVERT(VARBINARY(MAX),#o_key_pad) + HASHBYTES(#hash_algorithm,#i_key_pad + #message))
END
GO
and
CREATE function [dbo].[fn_PBKDF2]
(
#hash_algorithm varchar(25),
#password varchar(max),
#salt varchar(max),
#rounds int,
#outputbytes int
)
returns varchar(max)
as
begin
declare #hlen int
select #hlen = len(HASHBYTES(#hash_algorithm, 'test'))
declare #l int
SET #l = (#outputbytes +#hLen -1)/#hLen
declare #r int
SET #r = #outputbytes - (#l - 1) * #hLen
declare #t varchar(max), #u varchar(max), #block1 varchar(max)
declare #output varchar(max)
SET #output = ''
declare #i int
SET #i = 1
while #i <= #l
begin
set #block1 = #salt +cast(cast(#i as varbinary(4)) as varchar(4))
set #u = dbo.fn_HMAC(#hash_algorithm,#password,#block1)
set #t = #u
declare #j int
SET #j = 1
while #j < #rounds
begin
set #u = dbo.fn_HMAC(#hash_algorithm,#password,#u)
declare #k int
SET #k = 0
DECLARE #workstring varchar(max)
SET #workstring = ''
while #k < #hLen
begin
set #workstring = #workstring + char(ascii(substring(#u,#k+1,1))^ascii(substring(#t,#k+1,1)))
set #k = #k + 1
end
set #t = #workstring
set #j = #j + 1
end
select #output = #output + case when #i = #l then left(#t,#r) else #t end
set #i = #i + 1
end
return master.dbo.fn_varbintohexstr(convert(varbinary(max), #output ))
end
GO
then create the stored proc to generate the hash password
CREATE PROCEDURE [dbo].[EncryptPassword2]
#passwordIn AS VARCHAR(MAX),
#passwordOut VARCHAR(max) OUTPUT
AS
-- Generate 16 byte salt
DECLARE #saltVarBin VARBINARY(max)
SET #saltVarBin = (SELECT CAST(newid() AS binary(16)))
-- Base64 encode the salt
DECLARE #saltOut VARCHAR(max)
SET #saltOut = cast('' as xml).value('xs:base64Binary(sql:variable("#saltVarBin"))', 'varchar(max)')
-- Decode salt to pass to function fn_PBKDF2
DECLARE #decodedsalt varchar(max)
SET #decodedsalt = convert(varchar(max),(SELECT CAST('' as xml).value('xs:base64Binary(sql:variable("#saltOut"))', 'varbinary(max)')))
-- Build the password binary string from 00 + salt binary string + password binary string created by 32 byte 1000 iteration ORC_PBKDF2 hashing
DECLARE #passwordVarBinStr VARCHAR(max)
-- Identity V1.0 and V2.0 Format: { 0x00, salt, subkey }
SET #passwordVarBinStr = '0x00' + REPLACE(master.dbo.fn_varbintohexstr(#saltVarBin) + (SELECT dbo.fn_PBKDF2('sha1', #passwordIn, #decodedsalt, 1000, 32)),'0x','')
-- Identity V3.0 Format: { 0x01, prf (UInt32), iter count (UInt32), salt length (UInt32), salt, subkey } (comment out above line and uncomment below line)
--SET #passwordVarBinStr = '0x01000000010000271000000010' + REPLACE(master.dbo.fn_varbintohexstr(#saltVarBin) + (SELECT dbo.fn_PBKDF2('SHA2_256', #passwordIn, #decodedsalt,10000, 32)),'0x','')
-- Convert the password binary string to base 64
DECLARE #passwordVarBin VARBINARY(max)
SET #passwordVarBin = (select cast('' as xml).value('xs:hexBinary( substring(sql:variable("#passwordVarBinStr"), sql:column("t.pos")) )', 'varbinary(max)') from (select case substring(#passwordVarBinStr, 1, 2) when '0x' then 3 else 0 end) as t(pos))
SET #passwordOut = cast(''as xml).value('xs:base64Binary(sql:variable("#passwordVarBin"))', 'varchar(max)')
RETURN
Finally execute the stored proc using
DECLARE #NewPassword varchar(100)
DECLARE #EncryptPassword VARCHAR(max)
select #NewPassword = 'password12344'
EXECUTE EncryptPassword2 #NewPassword, #PasswordOut = #EncryptPassword OUTPUT;
PRINT #EncryptPassword
Please note that the stored proc may need to be changed for later versions of SQL server as this was written specifically for 2005 and I belive conversion to base64 is different in later versions.
if you are running 2005 or higher, you can create a CLR (.NET) UDF:
[SqlFunction(
IsDeterministic = true, IsPrecise = true,
DataAccess = DataAccessKind.None,
SystemDataAccess = SystemDataAccessKind.None
)]
public static string EncodePassword(string pass, string salt) {
byte[] bytes = Encoding.Unicode.GetBytes(pass);
byte[] src = Convert.FromBase64String(salt);
byte[] dst = new byte[src.Length + bytes.Length];
Buffer.BlockCopy(src, 0, dst, 0, src.Length);
Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider()) {
return Convert.ToBase64String(sha1.ComputeHash(dst));
}
}
you need to include the following namespaces in your class:
using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;
the class must be public.
build the .dll then run the following (per database you want to call the UDF) SQL statement:
sp_configure 'clr enabled', 1
GO
RECONFIGURE
GO
IF OBJECT_ID (N'dbo.EncodePassword', N'FS') IS NOT NULL
DROP FUNCTION dbo.EncodePassword;
IF EXISTS (SELECT name FROM sys.assemblies WHERE name='UDF')
DROP ASSEMBLY UDF
CREATE ASSEMBLY UDF FROM 'FULL_PATH_TO.dll' WITH PERMISSION_SET=SAFE
GO
CREATE FUNCTION EncodePassword(
#pass NVARCHAR(4000),
#salt NVARCHAR(4000)
)
RETURNS NVARCHAR(4000)
-- return NULL if any input parameter(s) are NULL
WITH RETURNS NULL ON NULL INPUT
AS
EXTERNAL NAME UDF.[NAMESPACE.CLASSNAME].EncodePassword
GO
obviously, replace 'NAMESPACE.CLASSNAME' with the namespace (if any) and name of your class. and you might want to mess with the input parameter and return value sizes.
then call the UDF with T-SQL:
SELECT UserName,Password
,dbo.EncodePassword('PASSWORD', PasswordSalt) As TestPassword
FROM aspnet_Users U
JOIN aspnet_membership M ON U.UserID = M.UserID
works for me :)
Instead of using CLR you can create this function in SQL. On this page you will find very nice example:
http://svakodnevnica.com.ba/index.php?option=com_kunena&func=view&catid=4&id=4&Itemid=5&lang=en#7
P.S. byte[] src = Convert.FromBase64String(salt); is correct way...
Fox
OP requested "pure" sql - I think using CLR is cheating ;) I was stubborn and had to figure it out for myself so here's what I did.
NOTE: Make a backup first!!
Select * into dbo.aspnet_Membership_BACKUP from [dbo].[aspnet_Membership]
Function to calculate the hashes:
/*
Create compatible hashes for the older style ASP.Net Membership
Credit for Base64 encode/decode: http://stackoverflow.com/questions/5082345/base64-encoding-in-sql-server-2005-t-sql
*/
Create Function dbo.AspNetHashCreate (#clearPass nvarchar(64), #encodedSalt nvarchar(64))
Returns nvarchar(128)
as
begin
declare #binSalt varbinary(128)
declare #binPass varbinary(128)
declare #result nvarchar(64)
Select #binPass = CONVERT(VARBINARY(128), #clearPass)
-- Passed salt is Base64 so decode to bin, then we'll combine/append it with password
Select #binSalt = CAST(N'' as XML).value('xs:base64Binary(sql:column("bin"))','VARBINARY(128)')
from (Select #encodedSalt as bin) as temp;
-- Hash the salt + pass, then convert to Base64 for the output
Select #result = CAST(N'' as XML).value('xs:base64Binary(xs:hexBinary(sql:column("bin")))', 'NVARCHAR(64)')
from (Select HASHBYTES('SHA1', #binSalt + #binPass) as bin) as temp2;
-- Debug, check sizes
--Select DATALENGTH(#binSalt), DATALENGTH(#binPass), DATALENGTH(#binSalt + #binPass)
return #result
end
I was changing a Membership database from "clear" passwords to the more secure hashed format - call it like this:
Update [dbo].[aspnet_Membership] set PasswordFormat = 1, Password = dbo.AspNetHashCreate(password, PasswordSalt) where PasswordFormat = 0
Even with my database originally set to "clear" passwords, the salt values were created with each record, however, if for some reason you don't have salt values you can create them with this:
/*
Create compatible salts for the older style ASP.Net Membership (just a 16 byte random number in Base64)
Note: Can't use newId() inside function so just call it like so: dbo.AspNetSaltCreate(newId())
Credit for Base64 encode: http://stackoverflow.com/questions/5082345/base64-encoding-in-sql-server-2005-t-sql
*/
Create Function dbo.AspNetSaltCreate (#RndId uniqueidentifier)
Returns nvarchar(24)
as
begin
return
(Select CAST(N'' as XML).value('xs:base64Binary(xs:hexBinary(sql:column("bin")))', 'NVARCHAR(64)')
from (select cast(#RndId as varbinary(16)) as bin) as temp)
end
Then use it like this:
Update [dbo].[aspnet_Membership] set PasswordSalt = dbo.AspNetSaltCreate(newId()) where PasswordSalt = ''
Enjoy!
According to this SO post, this is the process they use to encode/hash your password/salt.
public string EncodePassword(string pass, string salt)
{
byte[] bytes = Encoding.Unicode.GetBytes(pass); //HERE
byte[] src = Encoding.Unicode.GetBytes(salt); //and HERE
byte[] dst = new byte[src.Length + bytes.Length];
Buffer.BlockCopy(src, 0, dst, 0, src.Length);
Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
HashAlgorithm algorithm = HashAlgorithm.Create("SHA1");
byte[] inArray = algorithm.ComputeHash(dst); //then they has the bytes not the string...
return Convert.ToBase64String(inArray);
}
I could be wrong but it looks like you are missing the step where you get the bytes for the password and salt. Can you try adding that and see if it works?
Related
Hi can you help me please?, That way I can return the result of a query string in a variable inside a function with "execute"?.
for example
create function my _funcion(in # num_celular char (20), in # str_comando char (120))
returns char (120)
- on exception resume
as
begin
set # exec_qry = Select 1 from dummy
execute (# exec_qry)
return (# the return value of the query "execute")
end
go
thanks
Try this way:
create function my_funcion(#num_celular char (20), #str_comando char (120))
returns char(120)
as
begin
DECLARE #Result char(120)
execute('select #Result = ''Aaa'' ' )
RETURN #Result
end
go
Result of below query
select my_funcion('AA','BB')
is
Aaa
Ok, thanks for answering my question, but look at the issue is much more complex because we are running a Dynamic Sql, formed in while loop to the query is then stored in one string variables and running it, the problem are the function promptly made as follows and is attached below.
create function ms_trafico.fn_mov_cambia_tag( in #num_celular char(20), in #str_comando char(120) )
returns char(120)
--on exception resume
as
begin
set nocount on
set temporary option string_rtruncation='OFF'
--aplican para isql
--set option isql_print_result_set=ALL
--set option isql_show_multiple_result_sets=On
declare #comando varchar(100)
declare #exec_qry varchar(500)
declare #cadena varchar(60)
declare #tag char(15)
declare #columna char(20)
declare #pos int
declare #strCadFinal char(150)
declare #strFono char(20)
set #comando = trim(#str_comando)
set #strFono = trim(#num_celular)
set #exec_qry='select XXX '
set #pos = 1
print "comando " + #comando
while(#pos <= locate(#comando,'<',-1))
begin
set #tag = substr(#comando,locate(#comando,'<',#pos),CHARINDEX('>', SUBSTR(#comando,locate(#comando,'<',#pos),length(#comando))))
set #pos = locate(#comando,'<',#pos) + 1
select atributo_campo into #columna from TB_MOV_MAPEO_COMANDOS where parametro = #tag
set #cadena="replace(XXX,'"||trim(#tag)||"',"||trim(#columna)||")"
set #exec_qry=replace(#exec_qry,'XXX',#cadena)
end
set #comando="'"||trim(#comando)||"'"
set #exec_qry=replace(#exec_qry,'XXX',#comando)
set #exec_qry=#exec_qry||' as comando INTO #strCadFinal FROM VM_ALL_SERVICES_MOVIL where num_celular=#strFono'
execute (#exec_qry)
return (#strCadFinal) "The result of my query with execute method"!!!!!!!!!!
end
go
Thanks for you attention.
Is this sql vulnerability in my code?Because I have Parameterized the SQL ,so no sql Injection? anyone give some suggests will appreciate!If yes, How to repair?
ASP.NET CODE:
public DataTable CompanySearchUser(int pageSize, int currentPage, string whereCondition)
{
DbParameter[] parms = {
DbHelper.MakeInParam("#PageSize",(DbType)SqlDbType.Int,4,pageSize),
DbHelper.MakeInParam("#PageNumber",(DbType)SqlDbType.Int,4,currentPage),
DbHelper.MakeInParam("#where",(DbType)SqlDbType.NVarChar,500,whereCondition)
};
DataTable userlist = DbHelper.ExecuteDataset(CommandType.StoredProcedure, "spCompanySearchUser", parms).Tables[0];
return userlist;
}
SQL CODE:
ALTER PROC [dbo].[spCompanySearchUser]
#PageSize INT
#PageNumber INT,
#where nvarchar(550)--like 'and a=1 '
AS
DECLARE #RowStart INT
DECLARE #RowEnd INT
DECLARE #SQL NVARCHAR(4000)
IF #PageNumber > 0
BEGIN
SET #PageNumber = #PageNumber - 1
SET #RowStart = #PageSize * #PageNumber + 1;
SET #RowEnd = #RowStart + #PageSize - 1;
SET #SQL='
WITH AllUsers
AS (SELECT
UB.UserBaicInfoID,
UB.UserName,
UB.HighestEducation,
UB.Age,
UB.Sex,
UB.WorkExperience,
UB.PositionDesired,
UB.UpdateTime,
Row_number() OVER (ORDER BY UB.UpdateTime DESC) AS RowNumber
From UserBasicInfo UB
WHERE ResumeState=1 '+#where+')
SELECT * FROM AllUsers WHERE RowNumber >=' + Str(#RowStart) + ' AND RowNumber <= ' + Str(#RowEnd) + ''
EXEC sp_executesql #SQL
END
Is this sql vulnerability in my code?Because I have Parameterized the SQL ,so no sql Injection? anyone give some suggests will appreciate!If yes, How to repair?
cannot test it right now but i would say yes it is.
you pass in #where and you do a concatenation, your #where is doing actual sql code not a real "where" clause
then you do a exec on the final sql code
this can be exploited
not sure what your whereCondition look like but try this
public DataTable CompanySearchUser(int pageSize, int currentPage, string whereCondition)
{
/**** add this to test ****/
whereCondition += " or 1=1";
/**** add this to test ****/
DbParameter[] parms = {
DbHelper.MakeInParam("#PageSize",(DbType)SqlDbType.Int,4,pageSize),
DbHelper.MakeInParam("#PageNumber",(DbType)SqlDbType.Int,4,currentPage),
DbHelper.MakeInParam("#where",(DbType)SqlDbType.NVarChar,500,whereCondition)
};
DataTable userlist = DbHelper.ExecuteDataset(CommandType.StoredProcedure, "spCompanySearchUser", parms).Tables[0];
return userlist;
}
It's still insecure and non-performant.
You should just do the SQL dynamically and add the parameters there rather than relying on sp_executeSQL.
Building the string with the # placeholder items cmd.Parameters.AddWithValue("#placeholder", value) values, then adding those parameters, will generally be better, more secure, more manageable, and won't waste SQL's time since it won't be able to optimize your statements.
You may actually get better performance doing it dynamically in code rather that in a stored proc since SQL will cache every one of those statements and re-execute them if an identical statement comes along.
I have a stored procedure and it has where condition using 'IN' key word. So I want to send more than one value to stored procedure .
This is my stored procedure:
ALTER PROCEDURE [dbo].[xxx]
#COM_KEY varchar(max) = NULL
AS
BEGIN
SELECT
UserName, UserId
FROM
company
WHERE
(COM_KEY IN (#COM_KEY))
END
So I pass the value in here
string companyID = "";
for (int i = 0; i < lbCompanies.Items.Count; i++)
{
if (i == 0)
{
companyID += Convert.ToInt32(lbCompanies.Items[i].Value);
}
else
{
companyID += "," + Convert.ToInt32(lbCompanies.Items[i].Value);
}
}
DataSet ApproveList = DataRepository.TUsersProvider.xxx(companyID);
but there is an error
Conversion failed when converting the varchar value '3087,4058' to data type int
How can I solve it?
You cannot pass a comma-delimited string into #COM_KEY. You should pass an array.
One way to accomplish this task would be using Table-Valued Parameters.
Have a look at this article, under Solution #3: TSQL: Passing array/list/set to stored procedure (MS SQL Server).
In essence, you treat your series of ints as a table you apply JOIN upon, and not query it via WHERE ... IN () phrase.
You can create a UserDefinedFunction which parses the string and puts each integer extracted into a table to solve this issue
CREATE Function fnSplitter (#IDs Varchar(100) )
Returns #Tbl_IDs Table (ID Int) As
Begin
-- Append comma
Set #IDs = #IDs + ','
-- Indexes to keep the position of searching
Declare #Pos1 Int
Declare #pos2 Int
-- Start from first character
Set #Pos1=1
Set #Pos2=1
While #Pos1<Len(#IDs)
Begin
Set #Pos1 = CharIndex(',',#IDs,#Pos1)
Insert #Tbl_IDs Select Cast(Substring(#IDs,#Pos2,#Pos1-#Pos2) As Int)
-- Go to next non comma character
Set #Pos2=#Pos1+1
-- Search from the next charcater
Set #Pos1 = #Pos1+1
End
Return
End
Now alter your stored procedure like this
ALTER PROCEDURE [dbo].[xxx]
#COM_KEY varchar(max) = NULL
AS
BEGIN
SELECT UserName, UserId
From company
WHERE COM_KEY IN (Select ID From dbo.fnSplitter(#COM_KEY))
END
Check this link for detailed Implemetation
The most trivial solution to your problem is to vale SQL string(for dynamic Query) and then execute the statement
ALTER PROCEDURE [dbo].[xxx]
#COM_KEY varchar(max) = NULL
AS
BEGIN
DECLARE #SQL nvarchar(MAX)
SET #SQL=N' SELECT
UserName, UserId
FROM
company
WHERE
(COM_KEY IN ('+#COM_KEY+'))'
EXCE #SQL
END
Solution mentioned by #ssilas777 is the sophisticated version for the same.
Also note that using IN is not always advisable. I have run into
query time out errors
when IN has lot of IDs
Change your code from:
if (i == 0)
{
companyID += Convert.ToInt32(lbCompanies.Items[i].Value);
}
else
{
companyID += "," + Convert.ToInt32(lbCompanies.Items[i].Value);
}
to:
if (i == 0)
{
companyID +="'" + Convert.ToInt32(lbCompanies.Items[i].Value) + "'";
}
else
{
companyID += ",'" + Convert.ToInt32(lbCompanies.Items[i].Value) + "'";
}
I want to create a procedure that takes in a string, searches the table and the specified column for that string, and returns a 1 if it finds it and a zero if it does not. I am relatively new to SQL and do not know the syntax or commands very well. I want something like this:
CREATE PROCEDURE dbo.GetUsername
(
#Username NCHAR(10)
)
AS
#boolVariable
SELECT #Username FROM Accounts
RETURN #boolVariable
You don't return a value, but instead supply that in a result set.
CREATE PROCEDURE GetUsername
(
#Username NCHAR(10)
)
AS
SELECT Username FROM Accounts WHERE Username = #UserName;
In your calling code, simply check for the existence of a row in the result set.
I'm not sure if you're looking for mysql or mssql solution.
delimiter //
drop procedure if exists search_string //
create procedure search_string (in str varchar(100))
begin
declare b,r bool;
select count(*) into r from your_table where your_field = str;
if r > 0 then
set b = 1;
else
set b = 0;
end if;
select b;
end; //
delimiter //
call search_string('searched_string');
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