Best way to search two queries and eliminate rows without a relationship
I am working on a property website and have record sets for property and for unit, unit has a one-to-many relationship with property. What I'm trying to figure out is how to best create a search function which will output results based on criteria from both. So if I search for a property with the location Manchester and a unit with a freehold tenure I'd like to eliminate all properties which don't have a unit with the tenure of freehold.
A potential solution I've considered is to create a record set for properties which match the property criteria and then create a unit record set for units which match the unit criteria and then finally loop through the property record set in server-side code and eliminate any properties which aren't related to any of the units in the unit record set. Really not sure if this is the best way to do things though so would be keen to hear any suggestions?
Thanks
EDIT (Added table structure and MySQL):
--
-- Table structure for table `property`
--
CREATE TABLE IF NOT EXISTS `property` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` text NOT NULL,
`street` text NOT NULL,
`town` text NOT NULL,
`postcode` text NOT NULL,
`description` longtext NOT NULL,
`team_member` varchar(255) NOT NULL DEFAULT '',
`pdf` text NOT NULL,
`default_image_id` int(11) DEFAULT NULL,
`virtual_tour_link` text NOT NULL,
`date` date NOT NULL DEFAULT '0000-00-00',
`archive` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='' AUTO_INCREMENT=13 ;
--
-- Table structure for table `unit`
--
CREATE TABLE IF NOT EXISTS `unit` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` text NOT NULL,
`description` text NOT NULL,
`size_sq_ft` int(11) DEFAULT NULL,
`size_acres` float DEFAULT NULL,
`price` float DEFAULT NULL,
`rental_price` float DEFAULT NULL,
`on_application` tinyint(1) DEFAULT NULL,
UNIQUE KEY `id` (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Stores data for property units' AUTO_INCREMENT=5;
--
-- Table structure for table `property_to_unit`
--
CREATE TABLE IF NOT EXISTS `property_to_unit` (
`property_id` int(11) NOT NULL,
`unit_id` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- MySQL which produces list of properties
--
SELECT
P.id AS id,
P.name AS name,
P.street AS street,
P.town AS town,
P.postcode AS postcode,
P.description AS description,
P.team_member AS team_member,
P.pdf AS pdf,
P.virtual_tour_link AS virtual_tour_link,
P.date AS date,
P.archive AS archive,
PI.name as image,
P2.image_ids as image_ids,
L2.location_ids as location_ids,
U2.unit_ids as unit_ids
FROM property P
-- Get default image and join using property id
LEFT JOIN property_image PI ON PI.id = P.default_image_id
-- Create a list of image_ids from property_image and
-- property_to_property_image tables then join using property_id
LEFT JOIN (
开发者_开发百科 SELECT
property_id,
GROUP_CONCAT(CAST(id AS CHAR)) as image_ids
FROM property_to_property_image PTPI
LEFT JOIN property_image PI ON PI.id = PTPI.property_image_id
GROUP BY property_id
) P2 ON P2.property_id = P.id
-- Create a list of locations from property_location table
-- and join using property_id
LEFT JOIN (
SELECT
property_id,
property_location_id,
GROUP_CONCAT(CAST(property_location.id AS CHAR)) AS location_ids
FROM property_to_property_location
INNER JOIN property_location ON property_location.id = property_to_property_location.property_location_id
GROUP BY property_id
) L2 ON L2.property_id = P.id
-- Create a list of units from unit table
-- and join using property_id
LEFT JOIN (
SELECT
property_id,
unit_id,
GROUP_CONCAT(CAST(unit_id AS CHAR)) AS unit_ids
FROM property_to_unit
INNER JOIN unit ON unit.id = property_to_unit.unit_id
GROUP BY property_id
) U2 ON U2.property_id = P.id
--
-- MySQL which produces list of units
--
SELECT
id,
name,
description,
size_sq_ft,
size_acres,
price,
rental_price,
on_application,
tenure_ids,
tenure_names,
type_ids,
type_names
FROM unit AS U
-- join tenure ids and names
LEFT JOIN (
SELECT
unit_id,
GROUP_CONCAT( CAST(UT.id AS CHAR) ) AS tenure_ids,
GROUP_CONCAT(UT.name) AS tenure_names
FROM unit_to_unit_tenure UTUT
INNER JOIN unit_tenure UT ON UT.id = UTUT.unit_tenure_id
GROUP BY unit_id
) UT ON UT.unit_id = U.id
-- join type ids and names
LEFT JOIN (
SELECT
unit_id,
GROUP_CONCAT( CAST(UTYPE.id AS CHAR) ) AS type_ids,
GROUP_CONCAT(UTYPE.name) AS type_names
FROM unit_to_unit_type UTUT
INNER JOIN unit_type UTYPE ON UTYPE.id = UTUT.unit_type_id
GROUP BY unit_id
) UTYPE ON UTYPE.unit_id = U.id
WHERE 0=0
I'm currently using a dynamically created WHERE statement appended to each MySQL query to filter the property and unit results.
You're making it a bit more complicated than it is. If I understand correctly, you can easily do this in a single query. This would search properties that have units with a particlar unit tenure id:
select *
from property p
where p.id in (
select pu.property_id
from property_to_unit pu
inner join unit u ON pu.unit_id = u.id
inner join unit_to_unit_tenure uut ON u.id = uut.unit_id
where uut.id = <cfqueryparam value="#uutid#">
)
Using two queries and then looping through to cross-check sounds like it could be dog slow.
Your situation requires a posted foreign key in the property table. Store the unit_id
in the property table and use a join in your query such as:
select * from property p, unit u
where p.unit_id = u.id
and p.town = ....
EDIT: So I just noticed the rest of your SQL. If you require to keep the many-to-many relationship table for the unit -> property relationship then you will need to join unit and property off of that table.
精彩评论