I want store some article into database. I use php and mysql.
Whether have a database classification retrieval table?
like:
directory | keyword1 | keyword2 | keyword3 | keyword4|
sport | football | f1 | nba | tennis |
so that:
$query = mysql_query("SELECT * FROM keywordtable WHERE keyword1='$word' OR keyword2='$word' OR keyword3='$word' OR keyword4='$word' ");
if some words in the article match one of the keywords, the article will insert into directory - sport.
Thanks.
I need a table like my example, it should be have many words which can let me reference: if my article appears these words, I can put it into the directory it should in. I know there have more and more words which can be defined into sport.
If you are using fields like something1 something2 then you should probably use a different table for them, basically that's what relational databases are for. (Of course there are some legitimate reasons to use something like this, for example caching purposes.)
If I understand what you're asking, you'd like to have a keyword table against which you can check content. If the content matches any of the keywords, then the content is tagged with the associated directory. If that's correct then you need 4 tables:
keywords
kword
dir_id, int not null FK directories
directories
dir_id, int not null primary key
dir_name, varchar
article_directories
art_id, int not null FK articles
dir_id, int not null FK directories
articles, MyISAM
art_id, int not null primary key
art_title, varchar
content, text FULLTEXT index
INSERT INTO article_directories(art_id,dir_id)
SELECT DISTINCT a.art_id, k.dir_id
FROM articles a, keywords k
WHERE MATCH (a.content) AGAINST (k.kword)
The above query will identify all possible directories that an article could belong to. This means that an article could belong to both 'sports' and 'entertainment', for example.
Related
I have a small problem with a php mysql query, I am looking for help.
I have a family tree table, where I am storing for each person his/her ancestors id separated by a comma. like so
id ancestors
10 1,3,4,5
So the person of id 10 is fathered by id 5 who is fathered by id 4 who is fathered by 3 etc...
Now I wish to select all the people who have id x in their ancestors, so the query will be something like:
select * from people where ancestors like '%x%'
Now this would work fine except, if id x is lets say 2, and a record has an ancestor id 32, this like query will retrieve 32 because 32 contains 2. And if I use '%,x,%' (include commas) the query will ignore the records whose ancestor x is on either edge(left or right) of the column. It will also ignore the records whose x is the only ancestor since no commas are present.
So in short, I need a like query that looks up an expression that either is surrounded by commas or not surrounded by anything. Or a query that gets the regular expression provided that no numbers are around. And I need it as efficient as possible (I suck at writing regular expressions)
Thank you.
Edit: Okay guys, help me come up with a better schema.
You are not storing your data in a proper way. Anyway, if you still want to use this schema you should use FIND_IN_SET instead of LIKE to avoid undesired results.
SELECT *
FROM mytable
WHERE FIND_IN_SET(2, ancestors) <> 0
You should consider redesigning your database structure. Add new table "ancestors" to database with columns:
id id_person ancestor
1 10 1
2 10 3
3 10 4
After -- use JOIN query with "WHERE IN" to choose right rows.
You're having this issue because of wrong design of database.First DBMS based db's aren't meant for this kind of data,graph based db's are more likely to fit for this kind of solution.
if it contain small amount of data you could use mysql but still the design is still wrong,if you only care about their 'father' then just add a column to person (or what ever you call it) table. if its null - has no father/unknown otherwise - contains (int) of his parent.
In case you need more then just 'father' relationship you could use a pivot table to contain two persons relationship but thats not a simple task to do.
There are a few established ways of storing hierarchical data in RDBMS. I've found this slideshow to be very helpful in the past:
Models for Hierarchical Design
Since the data deals with ancestry - and therefore you wouldn't expect it to change that often - a closure table could fit the bill.
Whatever model you choose, be sure to look around and see if someone else has already implemented it.
You could store your values as a JSON Array
id | ancestors
10 | {"1","3","4","5"}
and then query as follows:
$query = 'select * from people where ancestors like \'%"x"%\'';
Better is of course using a mapping table for your many-to-many relation
You can do this with regexp:
SELECT * FROM mytable WHERE name REGEXP ',?(x),?'
where x is your searched value
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,ancestors VARCHAR(250) NOT NULL
);
INSERT INTO my_table VALUES(10,',1,3,4,5');
SELECT *
FROM my_table
WHERE CONCAT(ancestors,',') LIKE '%,5,%';
+----+-----------+
| id | ancestors |
+----+-----------+
| 10 | ,1,3,4,5 |
+----+-----------+
SELECT *
FROM my_table
WHERE CONCAT(ancestors,',') LIKE '%,4,%';
+----+-----------+
| id | ancestors |
+----+-----------+
| 10 | ,1,3,4,5 |
+----+-----------+
I know it makes little sense... and i'm new to using MySQL...
What i'm trying to do here is, link one tables row to another tables row...
for an example there are two tables..
one table is for user registration and same table is used for login as well...
and the next table is for user posts.. like status updates and all...
here is how i want it...
user_log_info:-
id ( primary )
firstname
lastname
username
email
password
posts:-
id ( primary )
userposts
posted_by
date_post
so as you can see, i want the user_log_info tables username to be automatically copied to posts posted_by row... And i have no idea how i can archive this...
You haven't given nearly enough information to give a full answer, but I'll do my best with what you've given.
Tables
+-----------------+ +-----------------+
| users_log_info | | posts |
+-----------------+ +-----------------+
| int ID (primary)| | int ID (primary)|
+-----------------+ | int posted_by |
+-----------------+
(I left off fields that are irrelevant to what you seem to want to do, I'm just simplifying it)
posted_by is an unofficial foreign key, or referencing the primary key of another table.
To insert, what you can do is along the lines of this:
INSERT INTO posts(...., posted_by) VALUES (...., user.ID)
Where .... is referencing all of your other information to insert
Then, to find information on someone who posted something:
SELECT * FROM users_log_info WHERE ID = Post.posted_by
Or if you want to find all posts by a user:
SELECT * FROM posts WHERE posted_by = user.ID
So, if Bob, who is User ID 3 wants to post "Hi", you might be able to do:
INSERT INTO posts(content, posted_by) VALUES('Hi', bob.ID)
And then when you are outputting the post you might do this:
post = (however you choose the post to put on the page)
userPosted = SELECT * FROM users_log_info WHERE ID = post.posted_by
print post.content + " posted by: " userPosted.Name
Essentially, the field "posted_by" is, to "posts" an arbitrary number, but you know that it links to, or references, a user. It does that by referencing "ID", which is the primary key of users_log_info, so that when you want to get information from users_log_info, is all you need to do is select the entry which has the ID that corresponds to "posted_by". I do recommend naming it something like posterID, however, for easier identification.
my MySQL table is in this structure:
|id|title|duration|thumb|videoid|tags|category|views
|1||Video Name|300|thumb1.jpg|134|tag1|tag2|tag3|category|15
|2||Video Name2|300|thumb2.jpg|1135|tag2|tag3|tag4|category|10
Table contains about 317k rows.
Query is:
SELECT id,title,thumb FROM videos WHERE tags LIKE '%$keyword%' or title LIKE '%$keyword%' order by id desc limit 20
And this is taking 0.8s to 3s to load results.
Im new in php/mysql, how can I speed up these queries, suggestions please, thank you.
The only other suggestion I can throw in is to have a multi-part index of
( tags, title, id )
This way, it can utilize the index to qualify the WHERE clause criteria for both tags and title, and have the ID for the order by clause without having to go back to the raw data pages. Then, when records ARE found, only for those entries does it need to actually retrieve the raw data pages for the other columns associated with the row.
You are using this search construct:
column LIKE '%$keyword%'
The leading % wildcard character definitely defeats the use of indexes to do these searches. How to cure this terrible performance problem? You could use FULLTEXT search, about which you can read. Or, you could try to organize your tables so
column LIKE 'keyword%'
will find what you need, and then index the columns being searched. To do this, you would create a tag table, with a name and id for each distinct tag. This table will have a primary key on the id, and a unique key on the tag. E.g.
tag_id | tag
1 | drama
2 | comedy
3 | horror
4 | historical
The you would create another table, known in the trade as a join table, with two ids in it. The primary key of this table is a composite of the two columns. You also need a non-unique index on the tag_id field.
video_id | tag_id
1 | 1
1 | 4
This sample data gives video with id = 1 the tags "drama" and "historical."
Then to match tags you need
SELECT v.id, v.title, v.thumb
FROM video AS v
JOIN tag_video AS tv ON v.id = tv.video_id
JOIN tag AS t ON tv.tag_id = t.tag_id
WHERE t.tag IN ('drama', 'comedy')
This will look up your tags very fast, and let you look up multiple ones in a single query if you wish.
It won't help with your requirement for full text search on your titles, however.
EDITED:
define indexes on title and keyword fields.
try this:
ALTER TABLE `videos` ADD INDEX (`title`);
ALTER TABLE `videos` ADD INDEX (`keyword`);
how I can use array in cell related with another table in database like ?
Users table:
Name | languages_id
Anas | 1,2,3
Languages table:
id | language
1 | English
2 | Arabic
it’s work or not ?! and do you know what can I use in yii to do this ?
Don't do this.
Don't store mutiple items as comma separated column, it is really bad.
You should keep your tables normalized, by creating a new table UsersLanguages as a many to many table between the USERS and Languages table. Somrthing like this:
Users:
UserId,
UserName,
... other details.
Languages:
LanguageId,
LanguageName,
...
UserLanguages:
UserId foreign key references Users(UserId),
LanguageId foreign kery references Languages(LanguageId).
You can use FIND_IN_SET.
SELECT
name,
language
FROM
users
INNER JOIN
languages ON FIND_IN_SET(languages.id, languages_id) != 0
GROUP BY
name
Although Mahmoud Gamal's comment would perhaps be the better way to go about it.
I have two tables, images and image_data and here is an example of my image_data table.
image_id | slide_id | language_id | type |
101 | 1 | 1 | CQ |
101 | 2 | NULL | NULL |
56 | 5 | 1 | TN |
56 | NULL | 2 | NULL |
So basically, each image will have different options and I am wondering the best way to implement this.. because I have a feeling I am doing this the wrong way.
With this, I can run a query to use GROUP_CONCAT() to turn values in multiple rows into a single concatenated string.
image_id | slide_id | language_id | type |
101 | 1,2 | 1 | CQ |
56 | 5 | 1,2 | TN |
Which is fine, but the problem with the way I am doing it right now is..it seems like it will be really difficult to update the rows with my backend system.
So with my query, I can determine which ones to check based on the database since I have it all in one row since I concatenated it. But now it's like.. when I go to click "Save" and update the rows, which one do I update? there can be more than 1 row of the same image id, so how would I update the right one, and so on.
If I checked off another slide for image #101 then I would need to create a new row for it. If after that I wanted to add another language_id to it, then I would need to make sure to not add a new row since one exists with a NULL value, and to just replace the NULL value with the new language id.
It just seems really complicated and there's so many factors, that using this method is really hard to program.
What would be the best way to do this? Any suggestions are really appreciated.
Thanks!
What you need to do is implement N:M (many-to-many) relationships between your images and slides / languages / types tables so that your design is more normalized (one fact in one place).
Think of it this way: one image can have multiple slides, and one slide may be an option of multiple images. -- this is a N:M relationship. Same goes for languages and types.
What you need to do is get rid of your image_data table which houses the options between ALL entities and have three separate cross-reference tables instead. Here's how you would model it:
Base tables:
images(image_id [PK], ...)
slides(slide_id [PK], slide_name, ...)
languages(language_id [PK], language_name, ...)
types(type_name [PK], ...)
Cross-Reference tables:
images_has_slides(image_id [PK], slide_id [PK])
images_has_languages(image_id [PK], language_id [PK])
images_has_types(image_id [PK], type_name [PK])
How it would look in ER:
With this type of design, you wouldn't have to deal with NULL values or figuring out which row to update because you now have just one fact in one place. To get all options, you would still have to do GROUP_CONCAT() like so:
SELECT
a.*,
GROUP_CONCAT(c.slide_name) AS slides,
GROUP_CONCAT(e.language_name) AS languages,
GROUP_CONCAT(f.type_name) AS types
FROM
images a
LEFT JOIN
images_has_slides b ON a.image_id = b.image_id
LEFT JOIN
slides c ON b.slide_id = c.slide_id
LEFT JOIN
images_has_languages d ON a.image_id = d.image_id
LEFT JOIN
languages e ON d.language_id = e.language_id
LEFT JOIN
images_has_types f ON a.image_id = f.image_id
GROUP BY
a.image_id
Then to update image options, you would use INSERT and DELETE on the cross-reference tables:
Let's say you wanted to add two languages to an image, you would do
INSERT INTO images_has_languages (image_id, language_id)
VALUES (101, 4), (101, 5);
The above query adds languages with id's of 4 and 5 to the image that has an id of 101.
To remove options (unchecking on the form) - let's say you wanted to remove 2 slides from an image
DELETE FROM images_has_slides WHERE image_id = 101 AND slide_id IN (3,6)
This removes slides with id's of 3 and 6 from the image that has an id of 101.
So in your application, you could figure out if you need to do insert/delete queries based on if the user unchecked or checked values in the form for the image.
Have you tried splitting the tables? If you make a separate table for the slide and language and kept the type in the same table as the image ID you could then use that to make your lists. You could then optimize your database with foreign keys so you don't take as big a performance hit.
Here what I mean:
Image data table: two columns, image_id and image_type (type is a reserved word). Imageid is the primary key so there are no duplicates (assuming you only want one type for each image)
Image-language table: two columns, image id and image_language. Both are primary keys so you don't duplicate languages on the same image id, but an image id can have multiple languages. Primary key from image id links to the primary key in the image data table
Image-slide table: two columns, image id and slide number. Same as above (two primary keys, relationship, etc)
This way you could get get all the data like so:
SELECT d.image_id, d.image_type, l.image_language, s.slide_number FROM image_data d LEFT JOIN image_language l ON d.image_id = l.image_id LEFT JOIN image_slide s ON s.image_id = s.image_id
The left joins make sure all the item id always shows up no matter what even if there isn't enough languages or slides to go around. It will create a "matrix" of sorts for you with a row for each image and each language and each slide it applies to. For example, if you had an image that had spanish and english as its language and 4 slides, you would get 8 entries: one for each slide in each language.
I don't know if that will necessarily solve the problem, but it would make it a little easier to control exactly what is in the database while still having the database do a bit of the work for you.
You need to normalize your schema.
You have images table:
CREATE TABLE images (
image_id integer,
image_name varchar(100),
PRIMARY KEY(image_id)
);
Each image can have several slides:
CREATE TABLE slides (
slide_id integer,
image_id integer,
slide_name varchar(100),
PRIMARY KEY(slide_id)
);
The same goes for the image_types and image_languages. I hope you understand the logic. And make sure to add proper FOREIGN KEY constraints. Also, it is a good idea to CREATE INDEX on the image_id columns of the subordinate tables.
Now, you have 1 row per each parameter in the related tables. Managing the contents should be easy: INSERT new records when some features are selected and DELETE them when those are deselected. The query (based on the outlined 2 tables) should be:
SELECT i.image_id, i.image_name,
group_concat(s.slide_id) AS slides
FROM images i
LEFT JOIN slides s USING (image_id)
GROUP BY i.image_id;
Some notes:
It is safe to do GROUP BY only by image_id, as it is a PRIMARY KEY of the iamges and thus it will guarantee single-row groupping;
If you'd like to have slide_id (also language_id, type_id and others) starting from 1 for each of the images, you might go for a 2-field primary keys in the subordinate table, like PRIMARY KEY (image_id, slide_id).
EDIT:
A note on the many-to-many relations. If you happen to have 2 sets of related data, like images can have many slides and slide_id can be shared by many image_id, then you need an extra table:
CREATE TABLE images (
image_id integer,
image_name varchar(100),
PRIMARY KEY(image_id)
);
CREATE TABLE slides (
slide_id integer,
slide_name varchar(100),
PRIMARY KEY(slide_id)
);
CREATE TABLE image_slides (
image_id integer,
slide_id integer,
create_dt timestamp,
PRIMRY KEY (image_id, slide_id)
);