views:

332

answers:

4

Hi there,

I'm building a website for property agents and tenants. Tenants can sign up and fill in their desired locations for properties, including Street, Town and Postcode. Once they sign up, this automatically emails agents who have properties that match those search criteria.

At present I have the query set up as follows so that it matches on either the Street, Town or Postcode.

<%
Dim rspropertyresults
Dim rspropertyresults_numRows

Set rspropertyresults = Server.CreateObject("ADODB.Recordset")
rspropertyresults.ActiveConnection = MM_dbconn_STRING
rspropertyresults.Source = "SELECT * FROM VWTenantPropertiesResults "

'WHERE     (ContentStreet = 'Holderness Road') OR (ContentTown = 'Hull') OR (ContentPostCode = 'HU')

rspropertyresults.Source = rspropertyresults.Source& "WHERE (ContentStreet = '" & Replace(rspropertyresults__varReqStreet, "'", "''") & "'"

rspropertyresults.Source = rspropertyresults.Source& "OR ContentTown = '" & Replace(rspropertyresults__varReqTown, "'", "''") & "' "
rspropertyresults.Source = rspropertyresults.Source& "OR ContentTrimmedPostCode = '" & Replace(varPostcode, "'", "''") & "' ) "

rspropertyresults.Source = rspropertyresults.Source& "AND (( ContentBedRooms >= " & Replace(rspropertyresults__varBedroomsNoMin, "'", "''") & " "
rspropertyresults.Source = rspropertyresults.Source& "AND ContentBedRooms <= " & Replace(rspropertyresults__varBedroomsNoMax, "'", "''") & " ) "

rspropertyresults.Source = rspropertyresults.Source& "AND ( ContentPrice > = " & Replace(rspropertyresults__varPriceMin, "'", "''") & " "
rspropertyresults.Source = rspropertyresults.Source& "AND ContentPrice <= " & Replace(rspropertyresults__varPriceMax, "'", "''") & " )) " & varSQL & " "

rspropertyresults.Source = rspropertyresults.Source& "ORDER BY ContentPrice " & Replace(rspropertyresults__varSortWay, "'", "''") & " "

rspropertyresults.CursorType = 0
rspropertyresults.CursorLocation = 2
rspropertyresults.LockType = 1
rspropertyresults.Open()

rspropertyresults_numRows = 0
%>

However, the client has asked that instead of just matching on one of the values, it needs to work in such a way that if say Street and Town match, then email that property agent or if Town and Postcode match, then email that property agent.

As you can imagine, I think the query would become quite complex, but i'm unsure how to best design a query like this.

I wondered if anyone might be able to help or point me in the right direction?

Thank you.

+2  A: 

Including SQL in your web site is bad practice IMO. But I am not familiar with asp-classic. Also the way you do this you are in danger from SQL injection. Don't mix UI and data access logic.

Look at http://en.wikipedia.org/wiki/SQL_injection

Petar Repac
There is nothing in ASP classic that prevents using best practices like separate model layer, parameterized queries, disconnected recordsets, connection pooling, etc.
RedFilter
+1  A: 

It would be a good idea to create a SQL Stored Procedure to handle the logic you've described. In ASP code, you can call into this procedure with the user-supplied parameters. This avoids issues with dynamically creating SQL and also make this problem a bit easier to tackle.

See http://authors.aspalliance.com/stevesmith/articles/sprocs.asp for a few examples of using ADODB with stored procedures.

David Andres
Note, it is not necessary to use a stored procedure in order to implement parameterized queries.
RedFilter
Absolutely true. I'm advocating SPs here as a cleaner, more focused way to affect the goals the OP wants to achieve.
David Andres
+4  A: 
SELECT  *
FROM    (
        SELECT  id
        FROM    (
                SELECT  id
                FROM    VWTenantPropertiesResults
                WHERE   ContentStreet = 'Holderness Road'
                UNION ALL
                SELECT  id
                FROM    VWTenantPropertiesResults
                WHERE   ContentTown = 'Hull'
                UNION ALL
                SELECT  id
                FROM    VWTenantPropertiesResults
                WHERE   ContentPostCode = 'HU'
                ) qi
        GROUP BY
                id
        HAVING  COUNT(*) >= 2
        ) q
JOIN    VWTenantPropertiesResults r
ON      r.id = q.id
WHERE   ContentBedrooms BETWEEN 1 AND 4
        AND ContentPrice BETWEEN 50 AND 500
ORDER BY
        ContentPrice

This will return you all records where at least 2 conditions match.

This solution is index friendly: unlike OR clauses, it will use indexes on ContentStreet, ContentTown and ContentPostCode.

See this entry in my blog for performance detail:

For best performance and security, replace substituted parameter values with bound parameters.

This will save you time on query parsing and will protect you against SQL injection.

Quassnoi
+1: Good solution - perhaps UNION ALL would be better in this case, since you are grouping by id anyway?
RedFilter
Hey Quassnoi,Thank you for your solution. I have tried this just in a view first to see if it executes correctly. (I also changed the id's to ContentID). It executes, but doesn't return any results.This could be that I have something else that is interfering with the results. I know that there is definitely some data that would match 2 or 3 of the conditions.
Neil Bradley
`@OrbMan`: right, thanks. Not only it's better, but it's requried for the query to work properly.
Quassnoi
`@Neil Bradley`: try now, I've fixed the query (thanks to `OrbMan`)
Quassnoi
I missed that it was required, was only thinking of performance, but of course you need it!
RedFilter
Thank you very much guys. It does seem to be working now. Really appreciate the fast response.
Neil Bradley
@Quassnoi: If I need to append the select to check if any 2 of those 3 conditions match as well as a check for the following;AND ContentBedrooms >= '1' AND ContentBedrooms <= '4'AND ContentPrice >= '50' AND ContentPrice <= '500'ORDER BY ContentPriceWhat would be the best way to do that?Thank you.
Neil Bradley
`@Neil Bradley`: add a WHERE clause with these conditions to the end of the query.
Quassnoi
`@Neil Bradley`: since your conditions don't seem to be very selective, this seems to be the best way. If they were selective I'd suggest to create composite indexes on (`ContentTown, ContentPrice`) etc. and add the condition into each of the three queries above.
Quassnoi
Would the WHERE clause go just before JOIN?
Neil Bradley
`@Neil`: just after `JOIN`. See the post update.
Quassnoi
@Quassnoi: Man, you are awesome. Thank you so much.
Neil Bradley
A: 

An approach I've used in a similar situation is if you make use of LIKE, rather than column = value, then you can make use of any values in any combination of fields. For instance:

WHERE town LIKE ('%' + @town + '%') and zip LIKE ('%' + @zip '%') AND street LIKE ('%' + @street '%') AND etc

Then it wouldn't matter if they only filled out some of the fields, it would still return valid results. The main catches to this approach, is all the fields would need to be string values as LIKE doesn't work with numeric type columns. So you would have to do some casting on numeric fields, which could bog things down some depending on how much conversion needs to be done, so it's a give and take kind of situation.

I would also agree that this really should be done in a stored procedure passing in parameters for the fields to search on.

BBlake