Searching rows in mysql - php

I'm just learning to set up searchable tables, I apologize for any obtuse questions in advance. I've set up a table that will allow me to post messages to, seems to be working fine. I need to be able to search a particular column in the table in order to determine if a message is supposed to show up in a particular user's feed. This is my show create;
CREATE TABLE `feed` (
`messageid` int(11) unsigned NOT NULL AUTO_INCREMENT,
`userid` text,
`contactid` text,
`subject` text,
`message` text,
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`flag` int(2) NOT NULL,
`state` int(2) NOT NULL,
`trash` int(2) NOT NULL,
PRIMARY KEY (`messageid`),
FULLTEXT KEY `contactid` (`contactid`),
FULLTEXT KEY `userid` (`userid`),
FULLTEXT KEY `message` (`message`),
FULLTEXT KEY `subject` (`subject`)
) ENGINE=MyISAM AUTO_INCREMENT=41 DEFAULT CHARSET=latin1
I believe the table type is set properly (MyISAM) and that any fields that I would want to be searchable have been set appropriately to text. Here is the full content of the table;
+-----------+--------+-----------+-----------------------------------------+--------------------------------------------+
| messageid | userid | contactid | subject | message |
+-----------+--------+-----------+-----------------------------------------+--------------------------------------------+
| 40 | 67 | 63 66 65 | Another test with apostraphes '''''' | ''' '''' ,,,, ''' ,,,' '''' test test test |
| 39 | 67 | 63 | Here's a test (with apostraphes '''''') | '''''' test test test ''''' |
+-----------+--------+-----------+-----------------------------------------+--------------------------------------------+
So, my thinking is to search the contactid column for a user's userid. If it shows up, the message will show up in the user's feed. But, when I do a search, nothing shows up;
mysql> select * from feed where match(contactid) against(63);
Empty set (0.00 sec)
Can someone help me figure out where I'm going wrong?

It looks like userid represents the message creator, and the collection of contactid's respresents the set of users who will be able to see the message.
I would suggest splitting feed into two tables, one with messages and another with a many-to-many relationship of messageid to contactid rather than including the column containing a list of contactids. This way you will be able to join the two tables to create a query to retrieve messages viewable by a particular user using a simple = rather than a full text search.
Here's an example:
messages
+-----------+--------+------------------+----------------+
| messageid | userid | subject | message |
+-----------+--------+------------------+----------------+
| 40 | 67 | Another test | test test test |
| 39 | 67 | Here's a test | test test test |
+-----------+--------+------------------+----------------+
message_contacts
+-----------+-----------+
| messageid | contactid |
+-----------+-----------+
| 40 | 63 |
| 40 | 66 |
| 40 | 65 |
| 39 | 63 |
+-----------+-----------+
SELECT messages.* FROM messages
INNER JOIN message_contacts ON messages.messageid = message_contacts.messageid
WHERE message_contacts.contactid = 63
Storing your data this way can help you avoid other problems as well. You can read more about that in one of my favorite answers: Is storing a delimited list in a database column really that bad?

MATCH() AGAINST() is used for what is called a "full text search", google it for more information, there's already enough information out there.
If you are matching column (aka field) against value, you would generally use an operator to tell mysql, what field to match against your value, and how to match it.
In your example, you should use the equals operator like so:
mysql> select * from feed where contactid=2
Lots of operators exist, which will tell mysql to do different lookups (i.e. the > greater than operator, would tell mysql to get all records with a contactid greater than 2, in your example).
Edit: MySQL doesn't provide string splitting functions, since you would normally be expected to split this data up over multiple tables and use relationships. The best you can get via a "non-hacky" approach is to use FIND_IN_SET, but that would require your contactids to be stored as CSV.
Try this:
mysql > select * from feed WHERE TRIM(contactid) = '2' OR contactid LIKE '% 2 %' OR contactid LIKE '2 %' OR contactid LIKE '% 2';

Related

mySql join Tables with key

I have a table that looks like this
+----+----------+------------
| id | restaurant| ... |
+----+----------+-----------+
| 1 | one | ... |
| 2 | tow | ... |
And now for the restaurants I want to add in hours of operation for each. Now from what I have read it would be bad to have a new column and call it hours and then have a varchar and have something like
"9:00-22:00,10:00-20:00,10:00-21:00"
And then when I pull this data into my app later split it at the commas to make an array. Not 100% sure why this is bad, but I know that I am not supposed to do that right?
So I was thinking making a new table called "Restaurant_Hours" and have it look like this
+----+----------+------------+------------+
| id | restaurant| mon |tue | ect...
+----+----------+------------+------------+
| 1 | one | 9:00-22:00|10:00-22:00 |
| 2 | tow | ect. | ect. |
Is this strategy of making the new table and having it like the way I showed best? And is this also not the correct way of doing things. And then restaurant would be my unique each in each so I could get the hours that way?
The base of what I'm thinking is something like this:
CREATE TABLE `restaurant_hours` (
`restaurant_hour_id` INT NOT NULL AUTO_INCREMENT,
`restaurant_id` INT NOT NULL,
`day_of_week` TINYINT NULL,
`opens_at` TIME NOT NULL,
`closes_at` TIME NOT NULL,
`hours_desc` CHAR(16) NOT NULL DEFAULT ''
);
Of course, restaurant_id should be a FOREIGN KEY to restaurants.id, and you might want a UNIQUE constraint on (restaurant_id, day_of_week, hours_desc). If they have special hours for holidays, you might want to use day_of_week == 0 as a "flag".
... or if you're feeling really ambitious, have it also reference some sort of "day_descriptions" table, where 1-7 correspond to Sunday-Saturday, and >=8 can be used to signal things that may need calculated by year (specific holidays).
Edit: hours_desc is intended as things like "Breakfast", "Lunch", "Dinner", etc...
Even without that, a query to find out "what's open when" would go something like this:
SELECT r.restaurant
FROM restaurant_hours AS rh
INNER JOIN restaurants AS r rh.restaurant_id = r.id
WHERE rh.day_of_week = DAYOFWEEK(#theWhen)
AND rh.opens_at < TIME(#theWhen)
AND rh.ends_at > TIME(#theWhen)
;

Do the fields (structure) of MySQL tables get unique ids?

I'm not talking about unique keys or auto_increments, suppose I have this structure:
mysql> describe email_notifications;
+---------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+------------------+------+-----+---------+----------------+
| email_id | int(11) unsigned | NO | PRI | NULL | auto_increment |
| email_address | varchar(100) | NO | | | |
| course_id | int(11) unsigned | NO | MUL | NULL | |
+---------------+------------------+------+-----+---------+----------------+
I'm building (for fun, practice, and hopefully some practical use) a tool in PHP that will analyze the structure of each table in a database and then compare it to a newer one (to assist in Dev -> Live updates), and then spit out some MySQL queries (Such as ALTER TABLE...) that I can run on the live database in order to bring it up to speed.
The question - does each field get a unique id of some sort?
If I change email_address from varchar(100) to text (for example) or the name course_id to cr_id, is there any way for me to tell that it's still technically the same dataset? I don't want to run a Delete and Add, but instead rename it give it a new type.
Or if there's a better way to do it without some sort of MySQL ID, that would be great :)
Thanks!
I think you can use information_schema.columns. The following are both unique keys in this table (even if they are not so defined):
TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME
TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, ORDINAL_POSITION
When you change the name or type of a column, I do not believe that ORDINAL_POSITION is affected. So, the second version may be what you are looking for.
This may then lead to the question "what if I change the name of a table?" The information_schema tables can't help there, unfortunately.

Configuring a mysql table or query to meet these needs

I have gotten the gist of most of mysql query and how to set-up my table however I am a little lost on how I can do this.
I currently have a table that has this format:
tag_target_id | tag_id | sub_tag_id | tag_target_name | tag_type_id
int(11) | int(11) | int(11) | varchar(255) | int(11
<PK> | <FK> | <FK> | | <FK>
A single tag_target_id corresponds to one piece of content ie. a video, article etc. now in the table there should be multiple entries with the same tag_target_id and tag_target_name and different tag_id , sub_tag_id , tag_type_id simple enough
So my problem is when I am inserting data into the table I have a PDO query that looked like this:
"INSERT INTO phpro_tag_targets (tag_id, sub_tag_id, tag_target_name, tag_type_id)
VALUES (:tag_id, :sub_tag_id,:tag_target_name,:tag_type_id)"
... I used to have tag_target_id as auto increment however each new query obviously incremented the tag_target_id so a single piece of content would have tag-target_id = 5, 6, 7, 8, 9 for eg. where I would like to have each piece of new content have one single tag_target_id
Now I would like to somehow have each new piece of content have a new tag_target_id but not each entry into the table for a single piece of content have it's own unique idea
so for eg. for one piece of content in the table all data for that content could look like this:
tag_target_id | tag_id | sub_tag_id | tag_target_name | tag_type_id
int(11) | int(11) | int(11) | varchar(255) | int(11
54 | 22 | 64 | url_to_content | 16
54 | 66 | 82 | url_to_content | 24
54 | 22 | 77 | url_to_content | 18
54 | 87 | 55 | url_to_content | 16
54 | 66 | 92 | url_to_content | 20
So how can I change the structure of the table or write a query set to achieve this desired output?
Assuming that You have separate table with info about content itself You may create a composite primary key on fields tag_target_id, tag_id and sub_tag_id.
And make tag_target_id as FK for this content table.
For existing table the query will be
ALTER TABLE phpro_tag_targets ADD PRIMARY KEY uniq_target_tag_sub (tag_target_id,tag_id,sub_tag_id)
Well you still need a unique id to distinguish the records. I would create a new column called something like "tag_content_id" and use that to store the grouping id (in your example 54). This way your table will have a unique id to use for the key (tag_target_id) and also a way to distinguish between different content entries and group items together (tag_content_id). When all else fails, add a column!

Database design: Multiple tables per user

I'm attempting to build a database that stores messages for multiple users. Each user will be able to send/receive 5 different message "types" (strictly a label, actual data types will be the same). My initial thought was to create multiple tables for each user, representing the 5 different message types. I quickly learned this is not such a good idea. My next thought was to create 1 table per message type with a users column, but I'm not sure that's the best method either from a performance perspective. What happens if user 1 sends 100 message type 1's, while user 3 only sends 10? The remaining fields would be null values, and I'm really not sure if that makes a difference or not. Thoughts? Suggestions and/or suggested reading? Thank you in advance!
No, that (the idea given in the subject of this question) will be tremendously inefficient. You'll need to introduce a new table each time a new user is created, and querying all them at once would be a nightmare.
It's far easier to be done with a single table for storing information about message. Each row in this table will correspond to one - and only - message.
Besides, this table should probably have three 'referential' columns: two for linking a specific message to its sender and receiver, and one for storing its type, that can be assigned only a limited set of values.
For example:
MSG_ID | SENDER_ID | RECEIVER_ID | MSG_TYPE | MSG_TEXT
------------------------------------------------------
1 | 1 | 2 | 1 | .......
2 | 2 | 1 | 1 | #######
3 | 1 | 3 | 2 | $$$$$$$
4 | 3 | 1 | 2 | %%%%%%%
...
It'll be quite easy to get both all the messages sent by someone (with WHERE sender_id = %someone_id% clause), sent to someone (WHERE receiver_id = %someone_id%), of some specific type (WHERE msg_type = %some_type%). But what's best of it, one can easily combine these clauses to set up more sophisticated filters.
What you initially thought of, it seems, looks like this:
IS_MSG_TYPE1 | IS_MSG_TYPE2 | IS_MSG_TYPE3 | IS_MSG_TYPE4
---------------------------------------------------------
1 | 0 | 0 | 0
0 | 1 | 0 | 0
0 | 0 | 1 | 0
It can be NULLs instead of 0, the core is still the same. And it's broken. Yes, you can still get all the messages of a single type with WHERE is_msg_type_1 = 1 clause. But even such an easy task as getting a type of specific message becomes, well, not so easy: you'll have to check each of these 5 columns until you find the one that has truthy value.
The similar difficulties expect the one who tries to count the number of messages of each types (which is almost trivial with the structure given above: COUNT(msg_id)... GROUP BY msg_type.
So please, don't do this. ) Unless you have a very strong reason not to, try to structure your tables so that with the time passing by they will grow in height - not in width.
The remaining fields would be null values
Except if you're designing your database vertically, there will be no remaining fields.
user int
msgid int
msg text
create table `tv_ge_main`.`Users`(
`USER_ID` bigint NOT NULL AUTO_INCREMENT ,
`USER_NAME` varchar(128),
PRIMARY KEY (`ID`)
)
create table `tv_ge_main`.`Message_Types`(
`MESSAGE_TYPE_ID` bigint NOT NULL AUTO_INCREMENT ,
`MESSAGE_TYPE` varchar(128),
PRIMARY KEY (`ID`)
)
create table `tv_ge_main`.`Messages`(
`MESSAGE_ID` bigint NOT NULL AUTO_INCREMENT ,
`USER_ID` bigint ,
`MESSAGE_TYPE_ID` bigint ,
`MESSAGE_TEXT` varchar(255) ,
PRIMARY KEY (`ID`)
)

How do I track changes and store calculated content in Nermalization?

I'm trying to create a table like this:
lives_with_owner_no from until under_the_name
1 1998 2002 1
3 2002 NULL 1
2 1997 NULL 2
3 1850 NULL 3
3 1999 NULL 4
2 2002 2002 4
3 2002 NULL 5
It's the Nermalization example, which I guess is pretty popular.
Anyway, I think I am just supposed to set up a dependency within MySQL for the from pending a change to the lives_with table or the cat_name table, and then set up a dependency between the until and from column. I figure the owner might want to come and update the cat's info, though, and override the 'from' column, so I have to use PHP? Is there any special way I should do the time stamp on the override (for example, $date = date("Y-m-d H:i:s");)? How do I set up the dependency within MySQL?
I also have a column that can be generated by adding other columns together. I guess using the cat example, it would look like:
combined_family_age family_name
75 Alley
230 Koneko
132 Furrdenand
1,004 Whiskers
Should I add via PHP and then input the values with a query, or should I use MySQL to manage the addition? Should I use a special engine for this, like MemoryAll?
I disagree with the nermalization example on two counts.
There is no cat entity in the end. Instead, there is a relation (cat_name_no, cat_name), which in your example has the immediate consequence that you can't tell how many cats named Lara exist. This is an anomaly that can easily be avoided.
The table crams two relations, lives_with_owner and under_the_name into one table. That's not a good idea, especially if the data is temporal, as it creates all kinds of nasty anomalies. Instead, you should use a table for each.
I would design this database as follows:
create table owner (id integer not null primary key, name varchar(255));
create table cat (id integer not null primary key, current_name varchar(255));
create table cat_lives_with (
cat_id integer references cat(id),
owner_id integer references owner(id),
valid_from date,
valid_to date);
create table cat_has_name (
cat_id integer references cat(id),
name varchar(255),
valid_from date,
valid_to date);
So you would have data like:
id | name
1 | Andrea
2 | Sarah
3 | Louise
id | current_name
1 | Ada
2 | Shelley
cat_id | owner_id | valid_from | valid_to
1 | 1 | 1998-02-15 | 2002-08-11
1 | 3 | 2002-08-12 | 9999-12-31
2 | 2 | 2002-01-08 | 2001-10-23
2 | 3 | 2002-10-24 | 9999-12-31
cat_id | name | valid_from | valid_to
1 | Ada | 1998-02-15 | 9999-12-31
2 | Shelley | 2002-01-08 | 2001-10-23
2 | Callisto | 2002-10-24 | 9999-12-31
I would use a finer grained date type than just year (in the nermalization example having 2002-2002 as a range can really lead to messy query syntax), so that you can ask queries like select cat_id from owner where '2000-06-02' between valid_from and valid_to.
As for the question of how to deal with temporal data in the general case: there's an excellent book on the subject, "Developing Time-Oriented Database Applications in SQL" by Richard Snodgrass (free full-text PDF distributed by Richard Snodgrass), which i believe can even be legally downloaded as pdf, Google will help you with that.
Your other question: you can handle the combined_family_age either in sql externally, or, if that column is needed often, with a view. You shouldn't manage the content manually though, let the database calculate that for you.

Categories