我一直在思考这个问题,并且我认为最好先问一下并倾听其他人的想法。
我建立了一个在Mysql上存储位置的系统。每个位置都有一个类型,一些位置有多个地址。
表格看起来像这样
location
- location_id (autoincrement)
- location_name
- location_type_id
location_types
- type_id
- type_name (For example "Laundry")
location_information
- location_id (Reference to the location table)
- location_address
- location_phone
因此,如果我想查询最近添加的10个数据库,我会选择以下内容:
SELECT l.location_id, l.location_name,
t.type_id, t.type_name,
i.location_address, i.location_phone
FROM location AS l
LEFT JOIN location_information AS i ON (l.location_id = i.location_id)
LEFT JOIN location_types AS t ON (l.location_type_id = t.type_id)
ORDER BY l.location_id DESC
LIMIT 10
右?但问题是,如果一个位置有超过1个地址,限制/分页将不会被激活,除非我“GROUP BY l.location_id”,但这将只显示每个地方的一个地址..会发生什么有多个地址的地方?
所以我认为解决这个问题的唯一方法是在循环内部进行查询..这样的事情(伪代码):
$db->query('SELECT l.location_id, l.location_name,
t.type_id, t.type_name
FROM location AS l
LEFT JOIN location_types AS t ON (l.location_type_id = t.type_id)
ORDER BY l.location_id DESC
LIMIT 10');
$locations = array();
while ($row = $db->fetchRow())
{
$db->query('SELECT i.location_address, i.location_phone
FROM location_information AS i
WHERE i.location_id = ?', $row['location_id']);
$locationInfo = $db->fetchAll();
$locations[$row['location_id']] = array('location_name' => $row['location_name'],
'location_type' => $row['location_type'],
'location_info' => $locationInfo);
}
现在我获得了最后10个位置,但通过这样做我最多得到10个查询,我认为这不会有助于应用程序的性能。
有没有更好的方法来实现我正在寻找的东西? (准确的分页)。
答案 0 :(得分:18)
这是您的原始查询
SELECT l.location_id, l.location_name,
t.type_id, t.type_name,
i.location_address, i.location_phone
FROM location AS l
LEFT JOIN location_information AS i ON (l.location_id = i.location_id)
LEFT JOIN location_types AS t ON (l.location_type_id = t.type_id)
ORDER BY l.location_id DESC
LIMIT 10
你最后执行分页。如果您重构此查询,则可以更早地执行分页。
SELECT l.location_id, l.location_name,
t.type_id, t.type_name,
i.location_address, i.location_phone
FROM
(SELECT location_id,location_type_id FROM location
ORDER BY location_id LIMIT 10) AS k
LEFT JOIN location AS l ON (k.location_id = l.location_id)
LEFT JOIN location_information AS i ON (k.location_id = i.location_id)
LEFT JOIN location_types AS t ON (l.location_type_id = t.type_id)
;
注意我创建了一个名为k
的子查询。 10个钥匙被拿起并命令FIRST !!!
然后JOIN可以从那里开始,希望只使用10个location_ids。
什么有助于子查询k
是一个带有location_id和location_type_id的索引
ALTER TABLE location ADD INDEX id_type_ndx (location_id,location_type_id);
以下是您对此方法的其他看法
您如何查询下一个10个ID(ID 11 - 20)?像这样:
SELECT l.location_id, l.location_name,
t.type_id, t.type_name,
i.location_address, i.location_phone
FROM
(SELECT location_id,location_type_id FROM location
ORDER BY location_id LIMIT 10,10) AS k
LEFT JOIN location AS l ON (k.location_id = l.location_id)
LEFT JOIN location_information AS i ON (k.location_id = i.location_id)
LEFT JOIN location_types AS t ON (l.location_type_id = t.type_id)
;
您需要做的就是在每个新页面中更改子查询LIMIT
中的k
子句。
LIMIT 20,10
LIMIT 30,10
我可以通过删除位置表来改进重构,并让子查询k携带所需的字段,如下所示:
SELECT k.location_id, k.location_name,
t.type_id, t.type_name,
i.location_address, i.location_phone
FROM
(SELECT location_id,location_type_id,location_name
FROM location ORDER BY location_id LIMIT 10,10) AS k
LEFT JOIN location_information AS i ON (k.location_id = i.location_id)
LEFT JOIN location_types AS t ON (k.location_type_id = t.type_id)
;
此版本不需要额外索引。
试一试!!!
答案 1 :(得分:3)
比循环和10个查询更好,您可以查询分页的location.location_id限制10,将其连接成逗号分隔的字符串,然后查询完整查询以获取WHERE location.location_id IN (1,2,3...{list of ids})
答案 2 :(得分:3)
您可以按照您对location_id进行分组的原始想法,然后使用group_concat函数将该位置的所有地址显示为1个字段。
SELECT l.location_id, l.location_name,
t.type_id, t.type_name,
group_concat(concat("Address: ",i.location_address, " Phone: ", i.location_phone)) as addresses
FROM location AS l
LEFT JOIN location_information AS i ON (l.location_id = i.location_id)
LEFT JOIN location_types AS t ON (l.location_type_id = t.type_id)
GROUP BY l.location_id
ORDER BY l.location_id DESC
LIMIT 10
答案 3 :(得分:2)
有几种方法可以解决这个问题:
IsPrimary
表中添加location_information
位列,并添加触发器以确保每个位置始终只有一个location_information
记录,并将此值设置为1。location_information
或location_id
列,则可以使用DateCreated
列选择最早或最新的DateModified
记录(MIN / MAX)。