MySQL query based on 3 tables - php

I work with PHP. I have a table named books. In table books I have the book_name with the name of the book, book_publisher with the publisher's ID and book_author with the author's ID. Besides the books table I have the books_author table with the authors names and IDs and books_publisher with the books publishers names and IDs.
I give my users 3 input fields that represents author name, book name and publisher name and a search button. They can input an author name, a book name and publisher name in the same search and I have to query my database and return the books names that have author name LIKE (%..%) inputted author name, book name LIKE the inputted book name and publisher name LIKE the inputted publisher name.
The problem is that I have stored only the author's id and publisher's id in my books table and I need to search by all three fields and exclude duplicates (books that were match by name and also by publisher).
Can anybody help me building this query ?

Just join the query:
SELECT *
FROM books b
JOIN book_authors ba ON b.author_id = ba.id
JOIN book_publishers bp ON b.publisher_id = bp.id
WHERE b.book_name LIKE '...'
AND ba.author_name LIKE '...'
AND bp.publisher_name LIKE '...'
Usually in these situations the search boxes are optional so you'll need to dynamically construct the query, meaning the WHERE clause to only filter on, say, publisher name if the user actually entered a publisher name.
Also, searching on LIKE '%blah%' is not scalable beyond thousands or possibly tens of thousands of records. No indexing can cater for that. You may want to look into using something like the MySQL full-text searching instead.
Lastly, make sure you sanitize your input, meaning pass all your input fields through mysql_real_escape_string().

Related

sql query from many table in PHP

I have an mp3 downloading site and I want to create a search page, but I am in simple confusion. I have two tables, one that stores movie details like movie name and movie cast, and another one where all the songs of movies are stored. The structure of two tables is shown below:
Table 1: name categories
column :5
id,cat,cast,year,type
here id is primary key
cat is album/movie name
cast is all actores details of movie
year is releasing year and type is movie type
Table 2: files
column :4
id,cat,preview,file
Here my user can search with any keywords, and I can extract rows by sql query but here are two tables so I am confused what to do?
The user can input previews from table and can enter cat, type, cast, type and year from table 1.
Your table structure is not clear, But using join in query is not that complicated as you think, try and learn. Good luck
SELECT Category.cat, Category.cast, Category.year, Category.Type, File.preview, File.file
FROM category As Category
LEFT JOIN files As File On Category.cat = File.cat
WHERE Category.cat LIKE '%search%' OR Category.cast LIKE '%search%'
OR File.preview LIKE '%search%'
etc

Long string in SQL database alternative?

We are creating a website where users can create a certain profile. At the moment we already have about 662000 profiles (records in our database). The user can link certain keywords (divided into 5 categories) to their profile. They can link up to about 1250 keywords per category (no, this isn't nonsense, for certain profiles this would actually make sense). At the moment we save these keywords into an array and insert the serialized array in the profile's record in the database.
When a different user uses the search function and searches for one of the keywords, an SQL query is executed with 'WHERE keyword LIKE %keyword%'. This means that is has to go to a pretty big number of records and go through the entire serialized array for each record. Adding an index to the keyword columns is pretty tricky, since they don't have a defined max lenght (this could be 22000+ chars!).
Is there any other more sensible and practical way to go about this?
Thanks!
Never, never, never store multiple values in one column!
Use a mapping table
user_keywords TABLE
--------------------
user_id INT
keyword_id INT
users TABLE
---------------------
id INT
name VARCHAR
...
keywords TABLE
---------------------
id INT
name VARCHAR
...
You could then return all users having a specific keyword in their profile like this
select u.*
from users u
inner join user_keywords uk on uk.user_id = u.id
inner join keywords k on uk.keyword_id = k.id
where k.name = 'keyword_name'
Since you are dealing with a large data you should use NoSQL databases such as Hadoop/Hbase, Cassandra etc. You should also take a look at Lucene/Solr...
http://nosql-database.org/

MSQL query for displaying results based on the same keywords

I have a table called cakes that contains the columns: id, title, description, keywords. I also have a table called keywords, with cakes being the parent. The keywords table contains two columns: id and keyword
I need two queries: UPDATED
If a person types in ingredients such as chocolate, hazelnut, strawberry (could be anything separated by a comma) I need the query to search for cakes that contain all three keywords and display results. Display ONLY cakes that contain all three. If no cake matches, I need a message saying nothing found.
I have a label on the search box which says, Find similar cakes. If a person types in Vanilla Raspberry or example, the query needs to locate the cake in the database and match its keywords to the keywords of other cakes and display results. Display ONLY cakes that have the same keywords.
Not sure how to write these queries. Any help is appreciated. Thanks!
If the database must use a delimited long-string field for "keywords" rather than putting them in rows, then you will want to use the LIKE Operator
Assuming your [keywords] column is formatted like this:
'chocolate,ganache,strawberry'
You can search for "similar" cakes like this:
SELECT
columns
FROM
table t
WHERE
t.[keywords] LIKE '%chocolate%'
OR t.[keywords] LIKE '%cheesecake%'
Though, if you can change the schema, I would do so. Searching normalized keyword rows will be much more efficient and fast than having the DB parse through text using LIKE
If you could make a keywords table, which references the parent table by ID, you could do an equality search using a JOIN which would be superior, in my opinion.
It might have three columns: Id, ParentId, Keyword
EDIT: So based on your update, you have a cakewords table which can be searched.
This is untested, and there is likely a more efficient way using no IN clause. But the idea is that you know all the keyword id's for your specific cake. Then you are looking for other cakes having keywords in that collection.
SELECT
columns
FROM
cake AS cs
JOIN
cakewords AS csw
ON csw.[cakeid] = cs.[id]
WHERE
csw.[wordid] IN
(SELECT
cw.[wordid]
FROM
cakewords AS cw
JOIN
cakes AS c
ON c.[id] = cw.[cakeid]
WHERE
c.[id] = #pMyCurrenctCakeId
(
EDIT2: Here is a good related question:
What's the optimal solution for tag/keyword matching?
Based on an answer within, you might try this:
SELECT DISTINCT
c.[id]
FROM
cakewords AS cw1
INNER JOIN cakewords cw2
ON cw2.[wordid] = cw1.[wordid]
INNER JOIN cake AS c
ON c.[id] = cw.[cakeid]
WHERE
cw1.[cakeid] = #current_cake_id

Retrieving Data from a field with comma delimitation SQL

I have a publications database and I need to fetch some information regarding the author. The author field is such that the authors have been lumped together in one field e.g if a book has two authors called Robert Ludlum and John Grisham, in the database it is saved as Ludlum, R.;Grisham,J.;
My application needs to spool information and retrieve data on books authored by a particular author if they click on their name. I am using this statement to retrieve the data
$select = "SELECT tblPublications.Title, tblPublications.Year FROM tblPublications WHERE tblPublications.Authors LIKE '%$sname%'";
$sname is a variable referring to the surname of the author. The problem arises if two authors share the same surname. however a workaround I am trying to implement is to get the applicationtake the surname, insert a comma, take the first name of a user and get the first letter then combine the result to a stringe and match them to each comma delimited value in the author field e.g if it is Grisham's books I am looking for I use *Grisham, J.* in my query.
Any Idea how to do this in PHP,MYSQL?
If it is possible to redesign the database, you should probably have an authors table and a book_authors table that relates books to authors, so that multiple authors can be associated with each book. Where is the Last Name coming from that the user clicks? Is it possible to have the link generated be LastName, First letter of first name? If so then you can probably change the link so it will include the first letter. But it is still possible to have two authors with the same last name and first letter of first name. So I think the best solution is to have an authors table and a Book_authors table and just store the author id as a hidden field and use that to retrieve the books by the selected author.
Your database design is incorrect, you have not normalized the data.
If you use like to search with leading wildcards, you will kill any chance of using an index.
Your only option to fix (if you want to keep the mistaken CSV data) is to convert the table to MyISAM format and put a FULLTEXT index on the authors field.
You can then search for an author using
SELECT fielda, b,c FROM table1 WHERE MATCH(authors) against ('$lastname')
See: http://dev.mysql.com/doc/refman/5.0/en/fulltext-search.html
Of course a better option would be to normalize the database and create a separate table for authors with a link table.
TABLE books
------------
id primary key
other book data
TABLE authors
--------------
id primary key
lastname varchar (indexed)
other author data
TABLE author_book_link
----------------------
author_id
book_id
PRIMARY KEY ab (author_id, book_id)
Now you can query using very fast indexes using something like:
SELECT b.name, b.ISBN, a.name
FROM books b
INNER JOIN author_book_link ab ON (ab.book_id = b.id)
INNER JOIN author a ON (a.id = ab.author_id)
WHERE a.lastname = '$lastname'
It would entirely depend on what input you are getting from the user.
If the user just types a name, then there isn't much you can do (as there is no guarantee that they will enter it in a correct format for you to parse).
If you are getting them to type in a firstname and lastname however, something like this could be done:
<?php
$firstname = trim(mysql_real_escape_string($_GET['fname']));
$surname = trim(mysql_real_escape_string($_GET['sname']));
$firstletter = substr($_GET['fname'],0,1);
$sname = $surname.', '.$firstletter;
$select = "SELECT tblPublications.Title,
tblPublications.Year
FROM tblPublications
WHERE tblPublications.Authors LIKE '%$sname%'";

First part of postcode

I want user to search by postcode, they could enter full postcode (eg: UB100PE / UB10 0PE) or first part of postcode (UB10)
I will use getUKPostcodeFirstPart("UB100PE") to get first part of postcode
Ref: getUKPostcodeFirstPart() by LazyOne
I want to display a list of record from first part of postcode, is this how it should be done?
Should I add stripped_postcode field in the table?
Something like::
SELECT records.company, records.full_postcode, area.* FROM records
LEFT JOIN area on area.stripped_postcode = records.stripped_postcode AND records.id= area.record_id
WHERE records.stripped_postcode = "UB10"
It is linked with two tables, records and area
record table:
id (PK)
company
postcode
stripped_postcode
area table:
id (PK)
record_id (FK)
stripped_postcode
field1
field2
I'm a bit confused: seems like a lot of work for avoiding the use of a like in SQL.
SELECT records.company, records.full_postcode, area.[insert your fields here]
FROM records
LEFT JOIN area
ON area.stripped_postcode = records.stripped_postcode
AND records.id= area.record_id
WHERE records.postcode like strSearch+'%'

Categories