I am coding a tiny search engine for my practice. I want to add up search functionality in it. I am trying to select all rows of questions table upon matching title, description and keywords.
I created the following 3 tables:
questions(id(PK), title, description)
keywords(id(PK), label);
questions_keywords(id(PK), question_id(FK), keyword_id(FK));
So far my SQL query looks like this:
SELECT q.* FROM question_keywords qk
JOIN keywords k ON qk.keyword_id=k.id
JOIN questions q ON qk.question_id=q.id
WHERE q.description LIKE '%javascript%'
OR
k.keyword_label LIKE '%java%'
In this query, i am selecting all the rows from questions table containing the substring java or javascript
Am I doing it right or there is a better way to do it??
Thanks in advance.
AS others mentioned I would add distinct. I would also reorder the tables. Functionally I don't think it matters it just bugged me... ha ha
SELECT DISTINCT
q.*
FROM
questions AS q
JOIN
question_keywords AS qk ON q.id = qk.question_id
JOIN
keywords AS k ON qk.keyword_id = k.id
WHERE
q.description LIKE '%javascript%'
OR
k.label LIKE '%java%';
As you can see in this DBfiddle
https://www.db-fiddle.com/f/pcVqcMm1yUoU6NdSHitCVr/2
The reason you get duplicates is basically called a Cartesian product
https://en.wikipedia.org/wiki/Cartesian_product
In simple terms is just a consequence of having a "Many to Many" relationship.
If you see in the fiddle I intentionally created this situation by what I added to the Bridge ( or Junction ) table question_keywords in the last 2 Inserts
INSERT INTO question_keywords (question_id,keyword_id)VALUES(4,1);
INSERT INTO question_keywords (question_id,keyword_id)VALUES(4,2);
The duplicate row, is simply because there are 2 entries for this table with the matching value of 4 for question_id. So these are only Duplicates in the sense that we are only selecting the fields form the questions table. If we included fields from the keywords table. Then one row would have a keyword or Java #1 while the other would have Javascript #2 as the keyword.
Hope that helps explain it.
A few other things to note:
You have a syntax error in the query you posted k.keyword_label LIKE '%java%' should be k.label LIKE '%java%' according to your table definition in the question.
Typically the Junction table should be a combination of both tables it joins ( which you almost did ) but the pluralization is wrong question_keywords should be questions_keywords it's a small thing but it could cause confusion when writing queries.
There is really not a need for a separate primary key for the Junction table.
If you notice how I created the table in the fiddle.
CREATE TABLE question_keywords(
question_id INT(10) UNSIGNED NOT NULL,
keyword_id INT(10) UNSIGNED NOT NULL,
PRIMARY KEY(question_id,keyword_id)
);
The primary key is a compound of the 2 foreign keys. This has the added benefit of preventing real duplicate rows from being made. For example if you tried this
INSERT INTO question_keywords (question_id,keyword_id)VALUES(4,1);
INSERT INTO question_keywords (question_id,keyword_id)VALUES(4,1);
With the setup I have it would be impossible to create the duplicate. You can still have a separate primary key (surrogate key), but you should create a compound unique index on those 2 keys in place of it.
Related
I have been looking around for answers for countless hrs and could not find much.I am making a food project and I was wondering if there is a way to prevent 2 items with the same name from colliding each other with their 2nd table.To make things clear I have 1 table which is
CREATE TABLE cuisine (id int,dish_name varchar(32));
//2nd table CREATE TABLE ingrediant (id int,dish_name varchar(32),ingrediant);
I was wondering if lets say 2 people posted dish_name = pizza with different ingrediants,when i left join how would the tables know which one to join because the only thing that are matching is the dish_name.
I know i could add the ingrediants to the first table but lets say i am adding up to 50 ingrediants and other items it is too much stuff.
An example i can use is Ebay.
If 2 people post items with the exact same name,when the item is clicked how does it know which info it is for.Hope it is clear
If you would model your relations correctly, you don't have that problem.
You would have to define a foreign key in the 2nd table, and the foreign key would have to refer to the primary key (or at least some other uniquely indexed field or combination of fields) in the 1st table. If you plan on using dish_name as a foreign key in the 2nd table, that would necessarily mean that dish_name would have to be unique in the 1st table.
This is probably a bad idea, so it's better to create a dish_id foreign key column in your 2nd table and get rid of the dish_name column.
I can't figure out what is the better solution.
A table : 35 of fields and 2 of them are longtext type. (Already built-in in the CMS that I'm about to use)
B table : similar amounts of fields and 3 of them are text type.(Newly required data for my project)
At first, I decided to use already existing 'A' table and add new 'B' table. It's far easy way 'cause the CMS already provides the classes and methods (etc - search, crud...) that is needed to my project.
But, my concern is the performance. Since table 'B' is sort of add-on to 'A', most of the DB actions, like showing list, searching, getting contents..., must refer at least those two tables at once each time.
JOIN and multiple queries are absolutely needed. And this site needs to show many lists and contents to online users at same time.
What would be the best solution? Is combining two tables more efficient, or re-using the already-existing codes as much as I can and preserving compatibility to the CMS?
ps. If I combine two tables, I can reduce some of the unnecessary varchar fields in table 'A'.
You only duplicate columns that you will use to relate tableB with tableA. Reasons why you shouldn't duplicate non-primary key columns:
a. table size grows and it's a waste of space if there are duplicate fields between tables.
b. regardless of the column size to be duplicated, it's very laborious maintaining tables just to make sure all duplicate columns are updated correctly. Think for example there's a duplicate column named detail. If you update this column in tableA for a particular id, then you need to make another effort to update the corresponding record in tableB.
c. Will consume lots of effort, resources, time, and money to maintain duplicate non primary key columns between tables.
Combining 2 tables is too expensive and not wise. If those two tables contains duplicate fields, you will waste lots of database space which will impact the CRUD operations. Best approach is to create table B but add only the primary key[s] fields from table A that you can use to link the two tables. In this way it will be easy to retrieve to both tables for data you specifically need. You also don't need to retrieve all data from both tables.
Example:
Table A:
id
fname
lname
.
.
rest of the 35 fields
Table B:
tableb_id
id (from table A)
.
.
rest of table B fields
Your Query:
Select column[s]
From TableA, TableB
Where TableA.id = TableB.id
And (all other conditions like TableA.lname = "Santos");
or use Inner Join
SELECT column_name(s)
FROM tableA INNER JOIN tableB ON tableA.id = tableB.ID
WHERE (all other conditions like TableA.lname = "Santos");
I'm in the midsts of constructing some database tables, but a possible search issue has just come to mind.
The two tables in question are Genres, a 2 column table holding a list of music genres identified by an ID field, i.e. 1 = Dance, 2 = Rock, and so on. And a Music table, a multi column table with Title, Artist, and Genre_ID fields. And yes you've guest it, Genre_ID refers to the ID of the Genre table.
My question is, if I have a search box on the site powered by PHP, and that search box queries the key fields, so Title, Artist, and Genre to yeld the best result, how can I get that to function correctly in a search, when the Genre name itself is in a separate table, and not in the Music table.
An example search would be, "rock music by ACDC".
To connect multiple tables in a query, you should look at using "join" statements. Rather than reinventing the wheel, the first answer to this post does a good job of explaining them... When to use a left outer join
Create a view where you join both of the tables. Then use SELECT with LIKE in WHERE clause or better use a fulltext search to do the searching job.
The view
create view ViewMusicWithGenre as
select "*"
from Music as m
left join Genre as g on m.genre_id = g.id;
Search option with like
select "*"
from ViewMusicWithGenre
where Title like '%<what_you_search>%'
or Artist like '%<what_you_search>%'
or Genre like '%<what_you_search>%';
I wrote the asterisk in "" because I KNOW that you WILL NOT use an asterisk.
Left join is there because you want the row even without specified genre (very likely).
The fulltext search
This usually depends on the database you use. This is for instance Microsoft SQL Server 2014:
Fulltext search - http://technet.microsoft.com/en-us/library/ms142571.aspx
Fulltext index - http://technet.microsoft.com/en-us/library/ms187317.aspx
Querying fulltext search - http://technet.microsoft.com/en-us/library/ms142583.aspx
EDIT: for MySQL database
MySQL does not support fulltext indeces on views. So you are left with couple of choices:
use the LIKE statement - could be ineffective, also more work later on
create the fulltext index on Music table and omit the genre - not good enough
create a new table that resembles the join and fill it on say daily basis with a job (or something like that) a do the fulltext search on that table - best solution in long terms, but more work to begin with and includes data duplicity
You also have to bear in mind that fulltext indeces only work on MyISAM storage engine.
The create statement for the joint table
create table fulltextSearchTable (
Music_ID int not null primary key,
Music_Title varchar(1024) not null,
Music_Artist varchar(1024) not null,
Genre_ID int not null,
Genre_Title varchar(1024) not null,
fulltext(Music_Title, Music_Artist, Genre_Title)
) engine=MyISAM;
The select with fulltext search
select "*"
from fulltextSearchTable
where match(Music_Title, Music_Artist, Genre_Title) against ('your_keyword');
You can try INNER JOIN like this:
$result=mysqli_query($YourConnection,"SELECT music.title, music.artist FROM music
INNER JOIN genres ON music.genre_id=genres.genre_id
WHERE music.title LIKE '$searchword'
OR music.artist LIKE '$searchword'
OR genres.genre LIKE '$searchword'");
And then print the results like this:
while($row=mysqli_fetch_array($result)){
echo $row['title']." - ".$row['artist']."<br>";
}
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
I'm pretty new to PHP and databases and I'm having some problems creating the database I need.
I'm hoping that someone here can help me! Probably a very stupid question...
What I'm trying to do is create a very simple movie library and creating tables from within phpmyadmin. I have not started writing any PHP code for this yet, I just want to make sure I created the database tables correctly.
I´m doing one table (MOVIES) with the fields:
ID, TITLE, DIRECTOR, YEAR and CATEGORY_ID.
This table should have a relation to another table for categories. So the CATEGORY_ID field in the first table should be connected to a table that stores the categories.
I have problems with the CATEGORY_ID field. Since the first ID field is set as primary and also Auto indent I can´t have that on the CATEGORY_ID field so when I add movies as a test from within phpmyadmin I just get the Id number 0 on category id.
Just to show the table layout that I've done:
MOVIE TABLE - ID (primary key, auto indent), TITLE, DIRECTOR, YEAR, CATEGORY_ID
CATEGORY TABLE - CATEGORY_ID, CATEGORY
You'd need to make a little more study to get complete help. I'd suggest you do some googling and read articles like this one:
http://www.sitepoint.com/mysql-foreign-keys-quicker-database-development/
[EDIT]
Thanks for taking the time to edit your question and making necessary changes:
Sample Database Schema
CATEGORY TABLE
CATEGORY_ID int auto_increment (11) (primary key)
CATEGORY
MOVIES TABLE:
ID int auto_increment (Primary Key)
TITLE,
DIRECTOR,
MOVIE_YEAR
CATEGORY_ID int(11) (Foreign Key)
Now you see what I mean by a foreign key; The above schema is based on your question and sample. I've only renamed the year column. I'm careful words or names that might conflict with reserved words. I just called it MOVIE_YEAR. TITLE field is not unique. There isn't a need for that. To prevent duplicates if you want, you could query the database for a match. But seeing two movies could have same names, their ids should set them apart.
Mysql
You don't need two auto increment columns in a table. Begin by creating category table and loading the values for categories. Id column could be auto increment or not. If it isn't, you'd have to manually provide the ids. Then create the movies table. You are doing a one to many relationship. Meaning, one category could match many movies in the movie table. So having 0 there isn't an error. That issue would be solved by your PHP script. The column is possibly set to not null, so a default value is provided.
The PHP Scripting
In PHP, create your code to pull values from category table into something like a dropdown select or clickable links and pass them to a hidden form element. Using a select could be quicker (see example below). The select and other form elements needed to add a new movie would be in one html form. So when you submit the form, it adds the data into the movies table. That data would carry with it the category_id that the user is inserting. This would replace the 0 you now have.
Creating the Select Dropdown
0 would happier if you had set the category_id column to not null. Nothing strange. During updates, the same process is repeated. Please note that the select would have a special format as shown below:
<?php while($row = mysqli_fetch_assoc($connection, $recordset)){ ?>
<select name='category_id' id='categories_id'>
<options value='<?php echo $row['category_id'] ?>'><?php echo $row['category_name'] ?></option>
</select>
<?php } ?>
These value should come from your db and populates the select menu. This should get you started. So try working on the PHP side now with the database you currently have. If you have a PHP code, you could show it so we all tweak it.
Forget the up and down votes, we're not in a competition in here. It's called reputation. We seem have a lot of it. :)
Displaying Records
Displaying is the tricky part. There you would need a join. Something in the line of:
$sql='SELECT * FROM movies LEFT JOIN category ON movies.category_id = category.id';
$recordset_all = mysqli_query($connection, $sql);
Something like that would give you all the records on movies and matching records on category. That way, you'd have the category name to work with all movies. And since you don't seem to have column name conflicts, everything should be fine. If you want only matching rows, take out the word LEFT, so movies that don't have category_id matching the foreign key in movies would not be selected.
Sub Headings
Your case would get complex if you intend to load rows in subheadings of category names. In which case, you would have to do two database selects and create a function to call and return corresponding movies. There could be a better way. We'd fix that when you come to that part if necessary.
Here's is an example of a very common database design of a one-to-many relationship. Notice CATEGORIES has a primary key that is used to relate MOVIES, which employs the FOREIGN KEY statement. Notice also that TITLE must be unique (I am presuming).
create table CATEGORIES (
ID int not null auto_increment,
Description varchar(100),
PRIMARY KEY (ID)
);
create table MOVIES(
ID int not null auto_increment,
TITLE varchar(30),
DIRECTOR varchar(30),
YEAR datetime,
CATEGORY_ID int,
PRIMARY KEY (ID),
UNIQUE KEY (TITLE),
FOREIGN KEY (CATEGORY_ID) REFERENCES CATEGORIES(ID)
)
you may be looking for INNER JOIN
SELECT ... FROM table1 t1
INNER JOIN table2 t2 ON t1.key = t2.key
WHERE ...
more information on table joining can be found Here
Remember to prefix your column names with the alias of the table (in this case the aliases are t1 and t2
Hope this helps.
Edit: here is a sample update and select query that you can use to update categoryID and later select using inner join
UPDATE MovieTable
SET categoryID = (number here)
WHERE (use unique identifier like ID or Title if it is unique) = (value)
SELECT my.Title FROM MovieTable mt
INNER JOIN CategoryTable ct ON mt.categoryID = ct.categoryID
WHERE ct.categoryID = (value)
Here is the scenario.
I'm developing a timeclock system; I have these tables:
-punch (id_punch,date)
-in1 (id_in1,time,id_punch,...)
-in2 (id_in2,time,id_punch,...)
.
-in6 (id_in6,time,id_punch,...)
-out1 (id_out1,time,id_punch,...)
-out2 (id_out2,time,id_punch,...)
.
-out6 (id_out6,time,id_punch,...)
My question is, how can I with only one query in PHP to get all values from in and out table, from a list of id_punch values, for example:
Get all punchs of September, or
Get all punchs of July to December,
I mean... from a list of id_punch between two dates, get all the results from the in, out table.
The only way I think is to do a query with each id_punch variable, but in a month its about 20-25 queries... to much?
To get all the data from the tables you'll need to join them with JOIN MySQL JOIN
But from what I can gather by looking at you tables, you probably should be thinking about making this into one table rather than the multiple tables you have here.
You really need to store all the in/out data in one table that is a child of punch:
CREATE TABLE punch (
id_punch SERIAL PRIMARY KEY,
punch_date DATE NOT NULL,
ip_address INT UNSIGNED NOT NULL
-- plus other attributes
) ENGINE=InnoDB;
CREATE TABLE inout (
id_punch BIGINT UNSIGNED,
in_time TIME NOT NULL,
out_time TIME NULL,
PRIMARY KEY (id_punch, in_time),
FOREIGN KEY (id_punch) REFERENCES punch (id_punch)
) ENGINE=InnoDB;
Now you can query very easily for all punches in September:
SELECT *
FROM punch LEFT OUTER JOIN inout USING (id_punch)
WHERE EXTRACT(YEAR_MONTH FROM punch_date) = '200909';
Your database schema is a little unclear, but if you're asking how to get the results corresponding to a list of ids you already have this should work (assuming your ids are 1,3,5,7,9)
SELECT * FROM table1, table2, table 3
WHERE table1.punch_id = table2.punch_id AND table2.punch_id = table3.punch_id AND table3.punch_id IN (1,3,5,7,9)
you'll probably need to modify it just make sure every table's punch_id is joined to that IN constraint
I can't use one table cause i have some informations in each punch, as ipaddress, and other information.
Neil, the answer was in my nose, i already saw a solution like yours, but my doubt is how to put the list in the query, answer for my own question = use a foreach() in php to "populate" this list...
Something like:
> SELECT * FROM table1, table2, table 3 WHERE table1.punch_id = table2.punch_id AND table2.punch_id = table3.punch_id AND table3.punch_id IN (<? foreach($query->results() as $row) echo $row->id_punch;?>)
im using codeigniter