views:

125

answers:

4

I'm using the following query to return all records where at least 2 conditions match (provided by Quassnoi).

SELECT  *
FROM    (
        SELECT  ContentID
        FROM    (
                SELECT  ContentID
                FROM    VWTenantPropertiesResults
                WHERE   ContentStreet = 'Holderness Road'
                UNION ALL
                SELECT  ContentID
                FROM    VWTenantPropertiesResults
                WHERE   ContentTown = 'Hull'
                UNION ALL
                SELECT  ContentID
                FROM    VWTenantPropertiesResults
                WHERE   ContentPostCode = 'HU'
                ) qi
        GROUP BY
                ContentID
        HAVING  COUNT(*) >= 2
        ) q
JOIN    VWTenantPropertiesResults r
ON      r.ContentID = q.ContentID
WHERE   ContentBedrooms BETWEEN 1 AND 4
        AND ContentPrice BETWEEN 50 AND 500
ORDER BY
        ContentPrice

The problem is that it seems to work when searching for Street and Town (returns all matching properties with the requested street and town), but not when searching for Street and Postcode (returns no results). To get the search for Street and Postcode to work (returning results), I had to remove the following lines;

        UNION ALL
        SELECT  id
        FROM    VWTenantPropertiesResults
        WHERE   ContentTown = 'Hull'

But then obviously the Town and Postcode or Town and Street searches don't work because i had removed the above 4 lines to get the Street and Postcode search to work.

I wondered if anyone could offer some help with this?

Thank you.

A: 

The query approach looks right.

What do you mean by not working? It throws an error, or returns no results, or unexpected results?

Are you expecting to use full postal code matches? I see your example uses HU, not sure what that relates to. It does not look like Canadian postal code, what region are you searching?

Can you show us your data set?

RedFilter
It's not a Canadian postcode, it's a UK postcode!
PhilPursglove
I wondered which Hull was being referred to!
dnagirl
It is indeed Hull in the UK. I'm sure Hull in Canada is much more exciting though. ;) I'm actually changing the query slightly so that it uses LIKE to match anything before or after the postcode entered.
Neil Bradley
I didn't know there was a Hull in Canada!
PhilPursglove
A: 

If you are correct, you are saying that this query:

SELECT  ContentID
FROM    VWTenantPropertiesResults
WHERE   ContentStreet = 'Holderness Road'
UNION ALL
SELECT  ContentID
FROM    VWTenantPropertiesResults
WHERE   ContentTown = 'Hull'
UNION ALL
SELECT  ContentID
FROM    VWTenantPropertiesResults
WHERE   ContentPostCode = 'HU'

returns less than 2 rows for some ContentID, while this query:

SELECT  ContentID
FROM    VWTenantPropertiesResults
WHERE   ContentStreet = 'Holderness Road'
UNION ALL
SELECT  ContentID
FROM    VWTenantPropertiesResults
WHERE   ContentPostCode = 'HU'

returns 2 or more for the same ContentID.

That would appear to be logically impossible, unless your DBMS has a major bug! For some ContentID that exhibits the problem, what do the above queries return?

Tony Andrews
A: 

I'm not sure you should be enforcing the 'at least two conditions' criteria down in the database as you should probably never have knowledge of which two have been filled in. Perhaps instead this might work for you - it's a pattern I use quite often and should cope with any combination of criteria (I'm assuming this is inside a stored proc!):

DECLARE PROCEDURE PropertyList
@StreetName NVARCHAR(50) = NULL,
@Town NVARCHAR(50) = NULL,
@Postcode NVARCHAR(10) = NULL
AS

SET NOCOUNT ON

SELECT 
    *
FROM 
    VWTenantPropertiesResults
WHERE
    ContentBedrooms BETWEEN 1 AND 4
AND
    ContentPrice BETWEEN 50 AND 500
AND
    (@ContentStreet IS NULL OR ContentStreet = @ContentStreet)
AND 
    (@ContentTown IS NULL OR ContentTown = @ContentTown)
AND
    (@ContentPostcode IS NULL OR ContentTown = @ContentTown)
ORDER BY
    ContentPrice

To call this from your ASP page you'll want some code something like this (this may need a bit of debugging, my ADO & VBScript for ASP is pretty rusty!):

Dim cnn 'As ADODB.Connection
Dim cmd 'As ADODB.Command
Dim prmStreet 'As ADODB.Parameter
Dim prmTown 'As ADODB.Parameter
Dim prmPostcode 'As ADODB.Parameter
Dim rstProperty 'As ADODB.RecordSet
Dim i 'As Integer

Set cnn = Server.CreateObject("ADODB.Connection")

cnn.ConnectionString = MyConnectionString

Set cmd = Server.CreateObject("ADODB.Command")

Set cmd.ActiveConnection = cnn

'Set the CommandText property to the name of the stored proc we want to call
cmd.CommandText = "PropertyList"
cmd.CommandType = 4 'or adCmdStoredProc if you're using ADOVBS.inc

If Request.Form("StreetTextBox") = "" Then
    'No street entered so don't pass it to the stored proc
Else
    'A street has been entered so create a parameter...
    Set prmStreet = cmd.CreateParameter("@StreetName", 203, 1, 50, Request.Form("StreetTextBox"))

    ' and add it to the Parameters collection of the Command object
    cmd.Parameters.Add(prmStreet)
End If

If Request.Form("TownTextBox") = "" Then
    'No town entered so don't pass it to the stored proc
Else
    'A town has been entered so create a parameter...
    Set prmTown = cmd.CreateParameter("@Town", 203, 1, 50, Request.Form("TownTextBox"))

    ' and add it to the Parameters collection of the Command object
    cmd.Parameters.Add(prmTown)
End If

If Request.Form("PostcodeTextBox") = "" Then
    'No postcode entered so don't pass it to the stored proc
Else
    'A postcode has been entered so create a parameter...
    Set prmPostcode = cmd.CreateParameter("@Postcode", 203, 1, 10, Request.Form("PostcodeTextBox"))

    ' and add it to the Parameters collection of the Command object
    cmd.Parameters.Add(prmPostcode)
End If

cnn.Open

'This is the line that'll actually call the stored procedure
Set rstProperty = cmd.Execute()

cnn.Close

If rstProperty.BOF And rstProperty.EOF Then
    'If BOF And EOF are true then this is an empty recordset - we got no records back
    Response.Write "No records returned"
Else
    'We have records so write them out into a table
    Response.Write "<table><tr>"
    For i = 0 To rstProperty.Fields.Count - 1
        Response.Write "<td>"
        Response.Write rstProperty.Fields(i).Name
        Response.Write "</td>"
        Response.Write "<td>&nbsp;</td>"
    Next

    Response.Write "</tr>"

    Do While rstProperty.Eof = False
        Response.Write "<tr>"
        For i = 0 To rstProperty.Fields.Count - 1
            Response.Write "<td>"
            Response.Write rstProperty.Fields(i).Value
            Response.Write "</td>"
        Next
        Response.Write "<td>"
        Response.Write "<a href='ViewDetails.asp?id='" & rstProperty.Fields("PropertyId").Value & "'>View Details for this property</a>"
        Response.Write "</td>"
        Response.Write "</tr>"
        rstProperty.MoveNext
    Loop

    Response.Write "</table>"
End If

This ought to work for any combination of parameters, whether you enter none, some or all of them!

PhilPursglove
Thanks Phil. I'll give your solution a try. I'm new to ASP as I have inherited older sites in my new job. In your solution here, would the procedure PropertyList also contain some SQL query?I have been reading up on parametised queries, but haven't used stored procedures before.Thanks.
Neil Bradley
The stored procedure is the query! I can add some VBScript code demonstrating calling the stored proc if that would be useful to you?
PhilPursglove
Hey Phil. That would be great if it is not too much trouble?
Neil Bradley
A: 

You probably want this:

SELECT  *
FROM    (
        SELECT  ContentID
        FROM    (
                SELECT  ContentID
                FROM    VWTenantPropertiesResults
                WHERE   ContentStreet LIKE '%Holderness Road%' -- Take off the leading % if you can
                UNION ALL
                SELECT  ContentID
                FROM    VWTenantPropertiesResults
                WHERE   ContentTown LIKE '%Hull%' -- Take off the leading % if you can
                UNION ALL
                SELECT  ContentID
                FROM    VWTenantPropertiesResults
                WHERE   ContentPostCode LIKE '%HU%' -- Take off the leading % if you can
                ) qi
        GROUP BY
                ContentID
        HAVING  COUNT(*) >= 2
        ) q
JOIN    VWTenantPropertiesResults r
ON      r.ContentID = q.ContentID
WHERE   ContentBedrooms BETWEEN 1 AND 4
        AND ContentPrice BETWEEN 50 AND 500
ORDER BY
        ContentPrice

Because there is no postal code in Britain which is = 'HU' It will be of the form 'HU_ ___' so you need wildcards.

Typically we restrict searches to match from the beginning of the string (with helps with searching indexes), but sometimes users want arbitrary search.

Cade Roux