9

私は一種の旅行「デート」アプリを書いています。

  • ユーザーが自分で登録する
  • ユーザーが男性か女性かをアプリに伝える
  • ユーザーは、訪問したい国をアプリに伝えます
  • ユーザーは、男性 (pref_m=1) または女性 (pref_f=1) と旅行するかどうかをアプリに伝えます。

マイ テーブル

表 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

では、selectステートメントが何をしなければならないか

入力:現在のユーザーのユーザー 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;";
これにより、私と同じ国に旅行したいすべての人のリストが表示されます (そして、共通の国がいくつあるか)

最後の注意事項

私は明らかに性別選択の部分に苦労しています。
私が持っている方法でユーザーの選択を保存するために正しいことをしたかどうかはわかりません。
私もそこにいくつかのガイダンスが必要な場合があります。


明らかに - すべてに感謝します。

4

6 に答える 6

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

一番いいのは、サブクエリがなく、このクエリをさまざまな方法で簡単に使用できることです。(順序の変更、グループ化、集計関数の使用)

于 2013-01-27T23:04:17.510 に答える
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')
    )

SQL フィドル


並べ替えが必要な場合は、使用するのが最善の方法だと思います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

おそらくいくつかの厄介なサブクエリでもこれをやってのけることができますが、パフォーマンスが低下する気がします.

SQL フィドル

于 2013-01-27T23:37:53.557 に答える
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

ここの他のものと同様ですが、where 条件の代わりに適切な結合タイプと結合条件を使用します。

MySQL ドキュメントから:

ON で使用される conditional_expr は、WHERE 句で使用できる形式の条件式です。一般に、テーブルの結合方法を指定する条件には ON 句を使用し、結果セットに含める行を制限するには WHERE 句を使用する必要があります。

于 2013-01-28T00:03:09.760 に答える
1

推測された DDL :

create table users (id int, gender text, pref_m bool, pref_f bool);
create table countryselection (id int, userid int, countryid int);

質問のテーブルに.import(実行後にsqlite3を使用して)できるCSVは次のとおりです。.separator ,

ユーザー.csv:

1,male,1,0
2,male,1,1

国選択.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されます。

于 2013-01-27T23:22:00.330 に答える
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

結果をより簡単に確認できるように、意図的に group by を省略しました。

于 2013-01-27T23:43:12.740 に答える
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') )
        ;
于 2013-01-27T23:48:19.683 に答える