简体   繁体   English

如何使用SQL比较多个字段?

[英]How to compare multiple fields using SQL?

I am creating a recommendation system using Laravel php and sql. 我正在使用Laravel php和sql创建一个推荐系统。 The user enters values during registration and they are stored in the user table. 用户在注册期间输入值,并将它们存储在用户表中。

Here is my users table schema , any field that has the recommendation name format eg recommendationLocation is the location of the user the system should search for. 这是我的用户表模式,任何具有建议名称格式的字段,例如RecommendationLocation是系统应搜索的用户的位置。

TABLE `users` (
  `id` (int),
  `username` (varchar),
  `email` (varchar),
  `password` (varchar),
  `age` (varchar),
  `location` (varchar),
  `country` (varchar),
  `userType` (varchar),
  `religion` (varchar),
  `variable1` (varchar),
  `variable2` (varchar),
  `recommendationAge` (varchar),
  `recommendationLocation` (varchar),
  `recommendationReligion` (varchar),
  `recommendationVariable1` (varchar),
  `recommendationVariable2` (varchar),
  `recommendationUserType` (varchar),
  `recommendationHairColor` (varchar),
  `recommendationCountry` (varchar),
)

The below statement would be a perfect straight forward user match but not all users will enter the identical information. 下面的语句将是一次完美的直接用户匹配,但并非所有用户都会输入相同的信息。 My problem is that there are a lot of different variables that I have to consider as there are a high amount of combinations. 我的问题是,由于存在大量的组合,因此必须考虑许多不同的变量。 As I want to be able to still find a user even if the user doesn't have certain matching info. 因为我希望即使用户没有某些匹配信息也仍然可以找到用户。 For example, The statement could only have one matching field but the query still returns the username with this matching field. 例如,该语句只能有一个匹配字段,但查询仍返回带有该匹配字段的用户名。

$recommendationQuery = DB::table('users')
            ->select('username')
            ->where('id', '!=', Auth::id()) // Can't be the  current Auth User Details 
            ->where('age', '=' , Auth::user()->recommendationAge) // This is a required where clause for all statements 
            ->where('location', '=', Auth::user()->recommendationLocation) // This is a required where clause for all statements 
            ->where('religion', '=', Auth::user()->recommendationReligion)
            ->where('variable1', '=', Auth::user()->recommendationVariable1)
            ->where('variable2', '=', Auth::user()->recommendationVariable2)
            ->where('age', '=', Auth::user()->recommendationAge)
            ->where('location', '=', Auth::user()->recommendationLocation)
            ->where('country', '=', Auth::user()->recommendationCountry)
            ->where('hairColor', '=', Auth::user()->recommendationHairColor)
            ->get();

Are there any alternative and more efficient ways? 是否有其他更有效的方法? thanks 谢谢

As stated, the way to allow for matches on a single property is to use a logical disjunction ( OR ): 如前所述,允许对单个属性进行匹配的方法是使用逻辑析取( OR ):

DB::table('users')
  ->select('username')
  ->where('id', '!=', Auth::id())// Can't be the  current Auth User Details
  ->where('age', '=', Auth::user()->recommendationAge)// This is a required where clause for all statements 
  ->where('location', '=', Auth::user()->recommendationLocation)// This is a required where clause for all statements 
  ->where(function ($query) {
        $query->where('religion', '=', Auth::user()->recommendationReligion)
              ->orWhere('variable1', '=', Auth::user()->recommendationVariable1)
              ->orWhere('variable2', '=', Auth::user()->recommendationVariable2)
              ->orWhere('age', '=', Auth::user()->recommendationAge)
              ->orWhere('location', '=', Auth::user()->recommendationLocation)
              ->orWhere('country', '=', Auth::user()->recommendationCountry)
              ->orWhere('hairColor', '=', Auth::user()->recommendationHairColor)
    })
  ->get();

The ->where(function ($query) { ... }) notation is Eloquent's way to introduce parenthesis in the resulting SQL query. ->where(function ($query) { ... })表示法是Eloquent在结果SQL查询中引入括号的方法。 These parenthesis are required, since AND takes precedence over OR . 这些括号是必需的,因为AND优先于OR

As a more elegant approach, consider moving the properties used for matchmaking to a secondary table instead: 作为一种更优雅的方法,请考虑将用于配对的属性移至辅助表:

TABLE `user_properties` (
  `id` (int),
  `user_id` (int)
  `property` (varchar),
  `value` (varchar),
  `recommendation` (varchar),
  UNIQUE KEY(user_id, property)
)

Storing the properties in a column-oriented way will allow you to elegantly construct a query to find all cases where a property recommendation matches the value of another user: 以面向列的方式存储属性将使您能够优雅地构造查询以查找所有属性recommendation与另一个用户的值匹配的情况:

SELECT other_user.user_id, user.property, user.recommmendation, other_user.value
FROM user_properties user
JOIN user_properties other_user
  ON other_user.property = user.property
  AND other_user.value = user.recommendation
  AND other_user.user_id != user.other_id
WHERE user.user_id = ?

( ? here is a placeholder for the current user, for whom you wish to find matches.) ?这是您要查找其匹配项的当前用户的占位符。)

You could then count the number of matching properties for each other user, and use this tally as a score, and pick the N best matches: 然后,您可以计算每个其他用户的匹配属性的数量,并将此计数用作得分,并选择N个最佳匹配:

SELECT other_user.user_id, COUNT(*) AS score
FROM user_properties user
JOIN user_properties other_user
  ON other_user.property = user.property
  AND other_user.value = user.recommendation
  AND other_user.user_id != user.other_id
WHERE user.user_id = ?
GROUP BY other_user.id
ORDER BY score DESC
LIMIT 10

Currently you code produces something like the following SQL : 当前,您的代码产生类似于以下SQL的内容:

SELECT username
FROM users
WHERE 
    id =! ? AND age = ? AND location = ?
    AND religion = ? AND variable1 = ? AND ...

For a user to match, it needs to fufill all conditions in the WHERE clause. 为了使用户匹配,需要在WHERE子句中满足所有条件。

From my understanding you are trying to separate mandatory conditions from optional conditions, like : 据我了解,您正在尝试将强制性条件与可选性条件分开,例如:

SELECT username
FROM users
WHERE 
    id =! ? AND age = ? AND location = ?
    AND (
       religion = ? OR variable1 = ? OR ...
    )

With this query, a user needs to match the 3 mandatory conditions on id , age and location , and any of optional conditions ( religion , variablbe1 , ...). 有了这个查询,用户需要相匹配,对3个强制性条件idagelocation ,以及任何可选条件( religionvariablbe1 ,...)。

To achieve this, you can leverage Lavarel parameter grouping , like : 为此,您可以利用Lavarel参数分组 ,例如:

$recommendationQuery = DB::table('users')
    ->select('username')
    ->where('id', '!=', Auth::id()) // Can't be the  current Auth User Details 
    ->where('age', '=' , Auth::user()->recommendationAge) // This is a required where clause for all statements 
    ->where('location', '=', Auth::user()->recommendationLocation) // This is a required where clause for all statements 
    ->where(function ($query) {
        $query->where('religion', '=', Auth::user()->recommendationReligion)
            ->orWhere('variable1', '=', Auth::user()->recommendationVariable1)
            ->orWhere('variable2', '=', Auth::user()->recommendationVariable2)
            ->orWhere('age', '=', Auth::user()->recommendationAge)
            ->orWhere('location', '=', Auth::user()->recommendationLocation)
            ->orWhere('country', '=', Auth::user()->recommendationCountry)
            ->orWhere('hairColor', '=', Auth::user()->recommendationHairColor)
        })
        ->get();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM