简体   繁体   中英

MySQL - Optimizing my query and returning the more specific match

I posted a while back a question asking how to implement a search with the following criteria:

On the "Searching" page, the customer can search for movies by any of the following attributes or their combination (logical "AND" operation):
title;
year;
director;
star's first name and/or last name. This means you need to do both: a) first name or last name if only one of the two names is provided; b) first name and last name, if both names are provided.

I then have to spit back out information about that movie from my database given the search input. People suggested I use FullText Search, and I did. This is the solution I have come up with.

  String searchInput = request.getParameter("searchInput");
  String query = "";

  // First see if there is a number in their search
  // Note: doesn't consider more specific searches, because it only searches
  // based on the given number
  if (searchInput.matches("\\d+")) {
      Pattern p = Pattern.compile("\\d+");
      Matcher m = p.matcher(searchInput); 
      if (m.find()) {
          String number = m.group();
          int n = Integer.parseInt(number);
          query = "SELECT title, year, director, id " +
          "FROM movies " + 
          "WHERE year = " + n + " " +
          "OR title LIKE '%" + n + "%'";
      }
  } else {
      query = "SELECT title, year, director, r.id " +
      "FROM movies AS r, stars AS s " + 
      "WHERE MATCH(title, director) AGAINST('" + searchInput + "*') " + 
      "OR MATCH(first_name, last_name) AGAINST('" + searchInput + "*') " +
      "AND r.id IN (SELECT sim.movie_id " + 
                    "FROM stars_in_movies AS sim " + 
                    "WHERE sim.star_id = s.id " + 
                    "AND sim.movie_id = r.id) " +
      "GROUP BY title";
  }

  ResultSet rs = statement.executeQuery(query);

  // Query again, this time ANDing the matches to see if
  // there is a more specific search result
  Statement statement2 = dbcon.createStatement();
  query = "SELECT title, year, director, r.id " +
  "FROM movies AS r, stars AS s " + 
  "WHERE MATCH(title, director) AGAINST('" + searchInput + "*') " + 
  "AND MATCH(first_name, last_name) AGAINST('" + searchInput + "*') " +
  "AND r.id IN (SELECT sim.movie_id " + 
                "FROM stars_in_movies AS sim " + 
                "WHERE sim.star_id = s.id " + 
                "AND sim.movie_id = r.id) " +
  "GROUP BY title";

  ResultSet rs2 = statement2.executeQuery(query);
  if (rs2.next()) {
      // If there is a more specific match (ANDing the matches), use that instead
      rs2.beforeFirst();
      rs = rs2;
  }

Now, I know this is REALLY bad code, since I'm executing almost the same query twice, with the only difference being that I AND the matches in the second query. I can't seem to figure out a way to combine the two queries or also reduce the amount of matching done to speed up the query. I also don't have a good way to deal with the year of the movie that the user may or may not input.

Here is what I have tried:

  1. I have tried searching up the FullText Search documentation for some functions that can be included in my search to make my SQL query return the more specific result if it matches more things, but the only thing that comes closest to such a thing is the Boolean Full-Text Search. However, I don't think I can use such a function because I have to distinguish between the different user's keyword inputs.

  2. I have also tried Googling AND/OR operations in MySQL that allows AND when possible, and if not possible, then ORs the query. However, it doesn't seem possible to do this.

UPDATE: I have nested subqueries now and the search is MUCH faster. However, I still need help with improving my code to return the more specific match and only the more specific match, if it exists. Else, return the more general match (if anything at all matches).

Run each sub query separately. You can AND and OR results in code as required, assuming the size of the results are reasonable. Bit more coding, but you only run the necessary queries once.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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