我正在编写一种旅行“约会”应用程序。
表1:用户
id (key) | gender | pref_m | pref_f
------------------------------------
1 male 1 0
2 male 1 1
表2:乡村选择
id (key) | userid | countryid
------------------------------------
1 1 123
2 1 111
3 1 100
4 1 110
5 2 123
6 2 111
7 2 202
8 2 210
输入:当前用户的用户ID
输出(逻辑上):选择想要和我一起旅行到同一国家的所有人的用户ID和匹配国家,并希望与有我性别的人一起旅行
(加入)在那个选择中,我显然只需要我正在寻找性别的人。
由拥有最多匹配国家/地区的人订购DESC。
$sql = "SELECT userid,count(*) AS matches from countryselection";
这给了我一份列表,列出了所有想和我一起旅行到同一个国家的人(以及我们有多少个国家)
$sql .= " WHERE countryid IN (SELECT countryid FROM countryselection WHERE userid = :userid) GROUP BY userid ORDER BY matches DESC;";
我显然正在努力选择性别选择部分
不确定我是否做了正确的事情以我的方式存储用户选择。
我也可能需要一些指导。
显然 - 谢谢大家。
答案 0 :(得分:8)
SELECT
us2.id, -- etc.
COUNT(cs2.countryid) as countries_in_common
FROM
countryselection cs1 -- let's gather user countries he want to visit
LEFT JOIN -- now let's find other users!
countryselection cs2 ON
(
cs2.userid <> :userid AND -- which are not him
cs2.countryid = cs1.countryid -- and want to visit same countries
)
INNER JOIN -- let's grab our user_data
users us1 ON
(
us1.id = cs1.userid
)
INNER JOIN -- and let's grab other user data!
users us2 ON
(
us2.id = cs2.userid
)
WHERE
cs1.userid = :userid AND -- finding our user countries he want to visit
-- final checks
(
(us1.pref_m = 1 AND us2.gender = 'male')
-- he is looking for male and second user is male
OR
(us1.pref_f = 1 AND us2.gender = 'female')
-- he is looking for female and second user is female
) AND
(
(us2.pref_m = 1 AND us1.gender = 'male')
OR
(us2.pref_f = 1 AND us1.gender = 'female')
)
GROUP BY
cs2.userid -- finally group by user_id
最好的事情是没有子查询,您可以通过多种方式轻松使用此查询。 (更改顺序,分组和使用聚合函数)
答案 1 :(得分:2)
如果您不进行大多数国家/地区的排序,这很容易(如果结果集不会太大,您可以稍后在代码中执行此操作):
SELECT
o.id userid, u_cs.countryid
FROM users u
JOIN countryselection u_cs ON (u.id = u_cs.userid)
JOIN countryselection o_cs ON (u_cs.countryid = o_cs.countryid)
JOIN users o ON (o_cs.userid = o.id)
WHERE
u.id = :userid AND -- The user we want
u.id <> o.id AND -- Exclude ourselves
( -- Check whether the other person is
-- compatible with us
(u.pref_m = 1 AND o.gender = 'male') OR
(u.pref_f = 1 AND o.gender = 'female')
) AND
( -- Check whether we're compatible with the
-- other person
(o.pref_m = 1 AND u.gender = 'male') OR
(o.pref_f = 1 AND u.gender = 'female')
)
如果你做想要排序,我认为最好的选择是使用GROUP_CONCAT
(因为MySQL糟透了,不支持窗口/分析功能)。
SELECT
o.id userid, GROUP_CONCAT(u_cs.countryid) countries
FROM users u
JOIN countryselection u_cs ON (u.id = u_cs.userid)
JOIN countryselection o_cs ON (u_cs.countryid = o_cs.countryid)
JOIN users o ON (o_cs.userid = o.id)
WHERE
u.id = :userid AND -- The user we want
u.id <> o.id AND -- Exclude ourselves
( -- Check whether the other person is
-- compatible with us
(u.pref_m = 1 AND o.gender = 'male') OR
(u.pref_f = 1 AND o.gender = 'female')
) AND
( -- Check whether we're compatible with the
-- other person
(o.pref_m = 1 AND u.gender = 'male') OR
(o.pref_f = 1 AND u.gender = 'female')
)
GROUP BY o.id
ORDER BY COUNT(u_cs.countryid) DESC
你也可以用一些讨厌的子查询来解决这个问题,但我觉得它会扼杀性能。
答案 2 :(得分:2)
SELECT t4.id, COUNT(t4.id) AS frequency
FROM users t1
LEFT JOIN countryselection t2
ON t1.id = t2.userid
INNER JOIN countryselection t3
ON t2.userid != t3.userid AND t2.countryid = t3.countryid
INNER JOIN users t4
ON t3.userid = t4.id
AND ((t4.pref_m = 1 AND t1.gender = 'male' OR t4.pref_f = 1 AND t1.gender = 'female')
AND (t1.pref_m = 1 AND t4.gender = 'male' OR t1.pref_f = 1 AND t4.gender = 'female'))
WHERE t1.id = ?
GROUP BY t4.id
ORDER BY frequency DESC
与其他人类似,但使用适当的连接类型和连接条件而不是条件。
来自MySQL docs:
与ON一起使用的conditional_expr是的任何条件表达式 可以在WHERE子句中使用的表单。一般来说,你应该使用 指定如何连接表的条件的ON子句,以及 WHERE子句,用于限制结果集中所需的行。
答案 3 :(得分:1)
推断DDL:
create table users (id int, gender text, pref_m bool, pref_f bool);
create table countryselection (id int, userid int, countryid int);
以下是CSV,您可以.import
(在执行.separator ,
之后使用sqlite3)进入问题中的表格:
users.csv:
1,male,1,0
2,male,1,1
countryselection.csv
1,1,123
2,1,111
3,1,100
4,1,110
5,2,123
6,2,111
7,2,202
8,2,210
彼得的sql,编辑使用来自问题的字段名称:
SELECT
us2.id,
COUNT(cs2.*) as countries_in_common
FROM
countryselection cs1
LEFT JOIN
countryselection cs2 ON
(
cs2.userid <> $userid AND
cs2.countryid = cs1.countryid
)
LEFT JOIN
users us1 ON
(
us1.id = cs1.userid
)
LEFT JOIN
users us2 ON
(
us2.id = cs2.userid
)
WHERE
cs1.userid = $userid AND
cs2.userid IS NOT NULL AND
(
(us1.pref_m = 1 AND us2.gender = 'male')
OR
(us1.pref_f = 1 AND us2.gender = 'female')
) AND
(
(us2.pref_m = 1 AND us1.gender = 'male')
OR
(us2.pref_f = 1 AND us1.gender = 'female')
)
GROUP BY
cs2.userid
;
你可以像这样执行:
sqlite3 myDBname < peters_sql.sql
将$userid
设置为1
,我将2
作为输出。
答案 4 :(得分:1)
我认为这有效
select me.id meid
, them.id themid
, me.gender mygender
, them.gender themgender
, me.pref_m mepref_m
, me.pref_f mepref_f
, them.pref_m thempref_m
, them.pref_f thempref_f
, csme.countryid
from users me
cross
join users them
inner
join countryselection csme
on me.id = csme.userid
inner
join countryselection csthem
on them.id = csthem.userid
where csme.countryid = csthem.countryid
and ((me.gender = 'male' and them.pref_m) or (me.gender = 'female' and them.pref_f))
and ((them.gender = 'male' and me.pref_m) or (them.gender = 'female' and me.pref_f))
and me.id != them.id
and me.id = 2
http://sqlfiddle.com/#!2/06351/25/0
我故意将小组排除在外,以便更容易验证结果。
答案 5 :(得分:1)
更新:(添加国家/地区)
SELECT u1.id AS uid1
, u2.id AS uid2
, cs.countryid
FROM users u1
, users u2
JOIN countryselection cs ON cs.userid = u2.id
-- WHERE u1.id < u2.id -- tiebreaker
WHERE u1.id = 12345
AND EXISTS (
SELECT * FROM countryselection cs1
JOIN countryselection cs2 ON cs1.countryid = cs2.countryid
WHERE cs1.userid = u1.id
AND cs2.userid = u2.id
)
AND ((u1.pref_m = True AND u2.gender = 'male')
OR (u1.pref_f = True AND u2.gender = 'female') )
-- the love must be mutual ...
AND ((u2.pref_m = True AND u1.gender = 'male')
OR (u2.pref_f = True AND u1.gender = 'female') )
;