我正在使用的软件使管理员能够创建自定义字段来存储有关用户的任意信息。字段存储在名为meta
的表中,其每个用户的值存储在表meta_value
中,该表通过外键引用user
和meta
表。
表的简化定义:
CREATE TABLE `user` (
`id` INT NOT NULL AUTO_INCREMENT,
`username` VARCHAR(120) NOT NULL,
PRIMARY KEY (`id`),
INDEX `idx_username` (`username`)
);
CREATE TABLE `meta` (
`id` INT NOT NULL AUTO_INCREMENT,
`key` VARCHAR(120) NOT NULL UNIQUE,
PRIMARY KEY (`id`),
INDEX `idx_key` (`key`)
);
CREATE TABLE `meta_value` (
`id` INT NOT NULL AUTO_INCREMENT,
`value` VARCHAR(120) NOT NULL,
`meta_id` INT NOT NULL,
`user_id` INT NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`meta_id`) REFERENCES `meta` (`id`),
FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
INDEX `idx_value` (`value`)
);
我正在实现一种搜索功能,该用户可以通过自定义字段的值搜索其他用户。搜索查询(简体)如下所示:?firstName[eq]=Andrew&lastName[eq]=Johnson&cityName[like]=Minneapol
。
此查询字符串应选择所有具有字段firstName = 'Andrew'
ANDlastName = 'Johnson'
AND city LIKE 'Minneapol%'
的用户。
我已经尝试了明显的解决方案:
SELECT `user`.*
FROM `user`
INNER JOIN `meta_value` ON `meta_value`.`user_id` = `user`.`id`
INNER JOIN `meta` ON `meta`.`id` = `meta_value`.`meta_id`
WHERE (
CASE `meta`.`key`
WHEN 'firstName' THEN `meta_value`.`value` = 'Andrew'
WHEN 'lastName' THEN `meta_value`.`value` = 'Johnson'
WHEN 'cityName' THEN `meta_value`.`value` LIKE 'Minneapol%'
END
);
此查询返回结果,其中firstName = 'Andrew'
或lastName = 'Johnson'
或cityName LIKE 'Minneapol%'
不是所需的结果。
我很感谢所有有关如何解决此问题的建议,或在我做错情况时采取正确方法的所有建议。
请注意,表结构不是一成不变的,可以更改,因此,如果您知道用于此目的的更合适的数据结构方法,也请提出建议。
您可以使用聚合和having
:
SELECT u.*
FROM user u JOIN
meta_value mv
ON mv.user_id = u.id JOIN
meta m
ON m.id = mv.meta_id
WHERE ( (m.key = 'firstname' AND mv.value = 'Andrew') OR
(m.key = 'lastName' AND mv.value = 'Johnson') OR
(m.key = 'cityName' AND mv.value LIKE 'Minneapol%')
)
GROUP BY u.id
HAVING COUNT(DISTINCT m.key) = 3;
在循环中添加其他条件:
$query = 'SELECT * FROM `user`
WHERE 1=1';
...
foreach($request as $fieldKey => $data) {
$query .= (' AND user.id IN (
SELECT `user_id`
FROM meta_value
JOIN `meta` ON meta.`id` = meta_value.`meta_id`
WHERE meta_value.`value` ' . $data['operand'] . ' "' . $data['value'] . '"
AND meta.`key` = "' . $fieldKey . '")');
}
...