Sorting MySQL results for diversity - mysql

I have a table called Classes which stores information on College classes. It has the columns Department, Course, and Section. I currently have an autocomplete where users can enter classes. For example if they type "ECON" it shows ECON-E 201 12345, etc. The autocomplete query is LIMIT'ed to 10.
Now, the problem with my autocomplete is that it doesn't give diversity in its responses. If they type "E" it will show 10 ECON classes, but not ENG (English) classes. Is there a way to sort the response to give as many departments, courses, and sections (in that order) as possible?

It depends also on the setup of your database. If you have a separate field for the names (such as "ECON"), then you could do:
SELECT DISTINCT course FROM classes WHERE UPPER(course) LIKE 'E'
Looking at your comment, how does this work for you? (I'm only checking against Department_Code, but in place of that, you could have your REPLACE line...)
SELECT Class_ID, Department_Code,
Course_Code, Class_Code FROM Classes
GROUP BY CASE WHEN (SELECT
COUNT(DISTINCT Department_Code)=1 FROM
Classes WHERE Department_Code LIKE '%"
. $q ."%') THEN Class_ID ELSE
Department_Code END HAVING
Department_Code LIKE '%" . $q ."%' LIMIT 10
This will return only a single record for each of the Departments that begin with 'E', but if the user enters 'ENG', then all of the courses for 'ENG' will be pulled.

There is a way to do this although it depends on where you want to do it at. For example do you want MySQL to do the delimiting or do you want to push this to the server side scripting to delimit/diversify your data?
Personally I see as a possibility, querying the database one or two times with different queries to diversify your data. Such as "SELECT * FROM classes WHERE couse like e" and then potentially take the first result such as "ECON..." and on next query do perhaps "SELECT * FROM classes WHERE !(course like "ECON")"
If you want server side scripting diversification it will require querying a much larger set of data instead of the 10. Lets say 50. From this you could return 0,1,2,3,4,20,30,40,49,50 of the result set. This might not be most intelligent way and it also requires you to pull a larger data set from database.
Original SELECT:
SELECT Class_ID, Department_Code, Course_Code, Class_Code, FROM Classes WHERE REPLACE(REPLACE(REPLACE(CONCAT(Department_Code, Course_Code, Class_Code), '-', ''), '(', ''), ')', '') LIKE '%". $q ."%' LIMIT 10
Diversify Query: (I will use to replace the above original to keep it shorter)
<originalQuery> UNION SELECT * FROM Classes WHERE SUBSTRING(<originalQuery>,1,1) LIKE '%".$q."%'
This might not be exactly it, but basic idea is you want your original result set (left side of the union) along with the second query where I take the result and call a substring on it so in your example probably "E" is like the user query. Hopefully this would give make a mixed set, I feel its not quite right but it should give you an idea of what you can do.

Related

SELECT odd syntaxes in database

Basically I have a large MySQL database table with a lot of city names, 90% of them are valid entries, but some of them are written in a ... not valid way.
For example the valid way is juste "CITYNAME" but some of them are like "(CITY NAME)(COUNTRY)" or just "(CITY NAME)" so I just wanna SELECT all the entries that are not written the valid way.
I don't know if that's specific enough don't hesitate to ask me for some precise elements.
And please help I have no idea how to build my SQL query.
CREATE TABLE cities(
name VARCHAR(50) NOT NULL PRIMARY KEY
);
INSERT INTO cities(name) VALUES ('ORLANDO');
INSERT INTO cities(name) VALUES ('(CHARLOTTE)');
INSERT INTO cities(name) VALUES ('(PHOENIX)(USA)');
INSERT INTO cities(name) VALUES ('AUSTIN(USA)');
INSERT INTO cities(name) VALUES ('TENNESSEE NASHVILLE');
So here are some examples of the different kinds of entries that I have to deal with.
I don't know how to describe the desired output, that'd just be a list of odd syntaxes, with or without the brackets.
The whole point is to delete those odd entries, but I have to SELECT them before doing so. And I also won't be the one deleting them, just gotta SELECT.
Possible solutions with output:
select * from cities where instr(name,')') or instr(name,'(');
(CHARLOTTE)
(PHOENIX)(USA)
This looks for anything which contains "(" or ")" anywhere.
Why wouldn't you try something like
Select * from cities where name like '(%';
If the problem is the parenthesis.. then everything that starts with a parenthesis will be selected
EDIT:
Select * from cities where name like '%(%' or name like '%)%';
It gives every line that contains a ( OR a )
EDIT2: some explanation
The character '%' replaces any string. So if you put like '%randomthing%', it will look for everything that look like anystring_randomthing_anystring. I hope i made this clear
You are using MySql, so you can do:
select *
from cities
where name not regexp '^[A-Z-]+$';
The not regexp '^[A-Z-]+$' means all the city names that don't respect the format "AAAA" or "AAAA-AAAAA" (where A represent an uppercase letter) is considered as invalid.

Show Fields but with content

i'm trying to show fields names on a combobox, but I need only those that are not null or have blank spaces.
I all ready have the field names with this query:
SHOW FIELDS FROM model WHERE type LIKE 'varchar(15)'
Any idea about how can i do this?
Update:
I'm working with an old database who is poorly designed. I attached an image:Database Screenshot This is a tire sizes database, so i need to get the years by model who has the size captured to show them in a combo box.
You can use your current query to get the "candidate" fields, but (short of some very complicated dynamic sql) you'll need to build a separate query to determine which candidates are pertinent. Generically, something like:
SELECT SUM(IF(field1 REGEXP '[a-zA-Z0-9.]+', 1, 0) > 0 AS showField1
, SUM(IF(field2 REGEXP '[a-zA-Z0-9.]+', 1, 0) > 0 AS showField2
, ...
FROM theTable
;
Depending on what you consider "has values" the regexp string may need adjusted; learn more here: http://dev.mysql.com/doc/refman/5.7/en/regexp.html
If the table is huge (large number of rows), you may be better off querying on each field separately like this (the one above will be looking at the whole table without some sort of WHERE to reduce the rows examined):
SELECT 1 FROM theTable WHERE fieldX REGEXP '[a-zA-Z0-9.]+' LIMIT 1;
Treating having a query result as "yes" and no result as "no content".

Composing dynamic SELECT Statement

Some advise on this issue:
select
concat(MySourceTable,',',sid,'X',gid,'X',qid) as MySourceFieldname
from
MySchemaTable
where
SomeCriteria;
Using the upper Statement I get a list (one column only) of fieldnames.
How can I transform this to be shown in horizontal (Fields next to each other) position, separated by comma
I want to produce a 'normal" SELECT-Statement for further use.
#amixdon
Input is :
select concat('shape_survey_990113',sid,'X',gid,'X',qid) as lsfield -- <=MySourceFieldname
from shape_questions -- <=MySchemaTable i.e. kind of dictionary
where question='result' -- <=SomeCriteria
and sid=990113
and language='en'
order by lsfield;
Result of Input looks like this (e.g.)
lsfields
---------
shape_survey_990113.990113X468X729,
shape_survey_990113.990113X469X733,
shape_survey_990113.990113X470X737,
....,
Explanation: sid, gid, qid are numeric contents taken from a table that is comparable to a dictionary. This is the source system which I want to select information from (cannot be changed). It is an opensource survey tool. Within this table all information for each survey is handled (numeric value 990113 is identifiying one special survey out of many. The concat above ist the final field name that holds the answers to the questions.
The 'shape_survey_990113' is the table to select the firlds from:
This select result should look like this an can be written to some variable
(e.g. SET #MyFields =...)
shape_survey_990113.990113X235X476, shape_survey_990113.990113X235X484, shape_survey_990113.990113X235X496
..to be used in the next step to make up a real select statement like:
concat('SELECT ', #MyFields, ' FROM shape_survey_991103;')
Unfortunately i cannot upload a screenshot fort this, not enough reputation....
There's nothing sacred, and quite a bit profane, about that CONCAT(). Lose it. And lose the quoted commas. Like so.
select
MySourceTable, sid, gid, qid
from
MySchemaTable
where
SomeCriteria;
Simple as can be.

MySQL search within the last 5 characters in a column?

My user table has a column "name" which contains information like this:
Joe Lee
Angela White
I want to search for either first name or last name efficiently. First name is easy, I can do
SELECT * FROM user WHERE name LIKE "ABC%"
But for last name, if I do
SELECT * FROM user WHERE name LIKE "%ABC"
That would be extremely slow.
So I am thinking about counting the characters of the input, for example, "ABC" has 3 characters, and if I can search only the last three characters in name column, that would be great. So I want something like
SELECT * FROM user WHERE substring(name, end-3, end) LIKE "ABC%"
Is there anything in MySQL that can do this?
Thanks so much!
PS. I cannot do fulltext because our search engine doesn't support that.
The reason that
WHERE name LIKE '%ith'
is a slow way to look for 'John Smith' by last name is the same reason that
WHERE Right(name, InStr(name, ' ' )) LIKE 'smi%'
or any other expression on the column is slow. It defeats the use of the index for quick lookup and leaves the MySQL server doing a full table scan or full index scan.
If you were using Oracle (that is, if you worked for a formerly wealthy employer) you could use function indexes. As it is you have to add some extra columns or some other helping data to accelerate your search.
Your smartest move is to split your first and last names into separate columns. Several other people have pointed out good reasons for doing that.
If you can't do that you could try creating an extra column which contains the name string reversed, and create an index on that column. That column will have, for example, 'John Smith' stored as 'htimS nhoJ'. Then you can search as follows.
WHERE nameReversed LIKE CONCAT(REVERSE('ith'),'%')
This search will use the index and be decently fast. I've had good success with it.
You're close. In MySQL you should be able to use InStr(str, substr) and Right(str, index) to do the following:
SELECT * FROM user WHERE Right(name, InStr(name, " ")) LIKE "ABC%"
InStr(name, " ") returns the index of the Space character (you may have to play with the " " syntax). This index is then used in the Right() function to search for only the last name (basically; problems arise when you have multiple names, multiple spaces etc). LIKE "ABC%" would then search for a last name starting with ABC.
You cannot use a fixed index as names that are more than 3 or less than 3 characters long would not return properly as you suggest.
However, as Zane said, it's a much better practise to use seperate fields.
If it is a MyIsam table, you may use Free text search to do the same.
You can use the REGEXP operator:
SELECT * FROM user WHERE name REGEXP "ABC$"
http://dev.mysql.com/doc/refman/5.1/en/regexp.html

MySQL UNION query correct handling for 3 or more words

I've to ask your help to solve this problem.
My website has a search field, let's say user writes in "Korg X 50"
In my database in table "products" i have a filed "name" that holds "X50" and a field "brand" that hold "Korg". Is there a way to use the UNION option to get the correct record ?
And if the user enters "Korg X-50" ?
Thank you very much !
Matteo
May be you should use full-text search
SELECT brand, name, MATCH (brand,name) AGAINST ('Korg X 50') AS score
FROM products WHERE MATCH (brand,name) AGAINST ('Korg X 50')
As far as I understand you don't need UNION but something like
SELECT * FROM table1
WHERE CONCAT(field1, field2) LIKE '%your_string%'
On client side you get rid of all characters (like space, hyphen, etc) in your_string that appears in user input and cannot be in field1 or field2.
So, user input Korg X 50 as well as Korg X-50 becomes KorgX50.
you will need to get some form of searchable text.
either parse out the input for multiple key words and match each separately, or perhaps try to append them all together and match to the columns appended in the same way.
you will also need either a regex, or maybe a simpler search and replace to get rid of spaces and dashes after the append before the comparison.
in general, allowing users to search for open ended text strings is more complicated than 'what union do i use'... you will ideally also be worried about slight misspellings and capitalization, and keyword order.
you may consider pulling all keywords out from your normal record into a separate keyword list associated with each product, then use that list to perform your searches.
If you do not want to parse user input and use as it is, then you will need to use a query like this
select * from products where concat_ws(' ',brand,name) = user_input -- or
select * from products where concat_ws(' ',brand,name) like %user_input%
However, this query won't return result if user enters name "Korg X-50" and your table contains "Korg" and "X50", then you need to do some other thing to achive this. You may look at http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_soundex however it won't be a complete solution. Look for text indexing libraries for that ex: lucene