How should I name fields in a relational database? - php

I'm about to create a database that has fields that will appear in a few different tables (as keys). For example, in my listing table I will have MLS_ID. This field will also appear in the listing_photos table. Should I prefix the field name to make it unique: listings_MLS_ID as an example?

there is absolutely no point in doing that. you could ALWAYS prefix your fieldname with both database and table names, getting your unique identifier, keeping the name itself neat and concise.
listings.mls.id
will address an id field in the mls table which belongs to listings database.
this is going to be quite flexible: any time you need long unique identifier - you have it.
in all other cases you can use short name.

Name the key the same in both tables (MLS_ID). The database server rewards you for doing this by letting you use the more concise USING clause:
SELECT * FROM listings
JOIN listing_photos
USING (MLS_ID)
Rather than this:
SELECT * FROM listings
JOIN listing_photos
ON listing_photos.listings_MLS_ID = listings.MLS_ID

this is personal preference.
for me:
i do not prefix with the local table name except on the primary key. e.g.
PERSON table will get a PERSON_ID column, ADDRESS table will get ADDRESS_ID etc.
the PERSON_ADDRESS table gets PERSON_ID and ADDRESS_ID, not something else.
in your queries, the column names are equal when they should be equivalent as keys, and the table alias tells you which is which.

Related

Uniquely identify same column name of two different table after join - mysql

I have 2 tables. suppose a & b
a has id, name, roll. b has id,group,name
This name column data are not same. How can I select and uniquely identify them?
I know about
SELECT a.id,a.name,a.group FROM a,b ............
I know this. But this is an example. I am working with huge amount of data with 20-30 columns in each table. So I don't want to write the column names I need to select rather I want to write the names that I want to exclude.
Like
SELECT * Except b.name............
OR is there any way to uniquely identify after join. Like
.......... a,b WHERE a.name as name1
Please don't ask why those column names are same. I admit it was a mistake. But it's already implemented and heavily used. So finding another way. Is there any simple way to exclude a column while merging them?
Well, you can't write the names you wish to exclude. That is not how SQL works.
However, if writing out 20-30 column names is that much of a burden, you can use information_schema.columns. I write it that way, because 20-30 column names is not particularly large and writing them out is probably less effort than writing the question.
But, back to the solution. It looks something like this:
select concat(c.column_name, ' as ', 'a_', column_name, ', ')
from information_schema.columns c
where table_name = 'a' ;
You might want to include the table schema as well.
As an IDEA, what you can do is, if you want to avoid columns of specific table & your statements have multiple table, you can try following,
Suppose you have 20 columns in table a & 5 columns in table b, you want to avoid col2,col3 & col4 of table b. Standard method is that you should write name of all columns of table a & required columns of table b. But you can avoid to write long list of 20 columns of table by writing a.* & then type required columns of table b. Please see below statement.
Select a.*,b.col1,b.col4,b.col5 from a,b
But if you require to exclude some columns from both table, then I think there is no other way than writing all required column names from both table.
There is no way to exclude a column in SQL SELECT Statement, you can only select a column. You can give alias name to columns while selecting them like below, so that you can identity columns using those alias names.
SELECT a.id as [column1],a.name as [column2],a.group as [column3] FROM a,b ............
There is no way to exclude a specific column but you can avoid to write all columns name and easy your job by below steps-
Step1: Execute below query-
SELECT a.*,b.* FROM a,b ............limit 1;
Step2: Export it into csv format with headings.
Step3: Copyp first (heading) row from csv.
Step4: Delete columns, those are not required and use other columns in your query.
There's only one waY i could see-
first create a temorary table
CREATE TEMPORARY TABLE IF NOT EXISTS mytable
(id int(11) NOT NULL, PRIMARY KEY (id)) ENGINE=MyISAM;
then put your column in temporary table-
SELECT * INTO mytable
FROM YourTable
/* Drop the cloumns that are not needed */
ALTER TABLE mytable
DROP COLUMN ColumnToDrop
/* Get results and drop temp table */
SELECT * FROM #TempTable
DROP TABLE #TempTable

MySQL Search for line in text

in MySQL, I have a row for each user, with a column that contains friends names separated by \n.
eg.
Friend1
Friend2
Friend3
I'd like to be able to quickly search all the users where the Friends field contains Friend2.
I've found FIND_IN_SET but that only works for commas and the data can contains commas and foreign characters.
Obviously searching with regular expressions and the such will be slow. I'm new to the whole cross referencing so I'd love some help on the best way to structure the data so that it can be found quickly.
Thanks in advance.
Edit: Ok, I forgot to mention a point that the data is coming from a
game where friends names are stored locally and there are no links to
another users ID. Thus the strings. Every time they connect I am given
a dump of their friends names which I use in the background to help match games.
The most commonly used structure for this kind of data is usually adding an extra table. I.e.
user
id,
name
email,
e.t.c.
user_friend
user_id
friend_id
Querying this is a matter of querying the tables. I.e.
List all of a users friends names:
SELECT friend_id
FROM user_friend
WHERE user_id = :theUser
Edit: Regarding OPs edit. Just storing the names is possible too. In this case the table structure would become:
user_friend
user_id
friend_name
and the query:
SELECT friend_name
FROM user_friend
WHERE user_id = :theUser
Why are you keeping friend names as text? This will be inefficient to edit uf say a user removes a friend or changes their name. That's another thing, you should store friend names by some auto_increment id key in your database. It's much faster to search for an integer than a string, especially in a very large database. You should set up a friends table which is like
Column 1: connectionid auto_increment key
Column 2: user1id int
Column 3: user2id int
Column 4: date added date
ect...
Then you can search the connection table above for all rows where user is user1id or user2id and get a list of the other users from that.
My database hasn't been filled yet so I can easily change the format and structure in which the data will be stored.
Yes, you need to normalize your database a bit. With current structure, your searches will be quite slow and consume more space.
Check out this wiki for detailed help on normalization.
You can have the friends table and users table separate and link them both by either foreign key constraint or inner joins.
The structure would be:
Users table
id: AUTO_INCRMENT PK
name
other columns
Friends table
id: AUTO_INCREMENT(not required, but good for partitioning)
UserID:
FriendsID
DateAdded
OtherInfo if required.

mysql_fetch_assoc can not access similars columns names

I have more than one table in my query
$query = mysql_query("SELECT * FROM reservation r, users u ...")
while( $fetch = mysql_fetch_assoc($query)){
if($fetch['u.id'] == $fetch['r.id'])
...
And here is the problem I have same column name on tables and when i try to compare the id of reservations with the id of user i get only the id of users!!!
I can replace the '*' with the column names and to change their name with AS (SELECT s.id as sid ...) but is there any easier way?
For e.x:
If your two tables both have a column with the same name, then you're only going to be able to access one of those fields in an associative array - the key is the name of the field, so the first one is over-written by the second.
The quick fix is, as you say, to explicitly rename the fields so they don't clash. Though you might be able to work around it by using mysql_fetch_array - that will give you all the fields, but you'll need to access fields by index number; so if you change table structures, you'll need to re-write code.
The better way is not to have two fields with the same name in different tables, if at all possible - if they were called reservationID and userID, you'd avoid this completely. I try not to re-use fieldnames unless it's a foreign key.

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%'";

Managing Foreign Keys

So I have a database with a few tables.
The first table contains the user ID, first name and last name.
The second table contains the user ID, interest ID, and interest rating.
There is another table that has all of the interest ID's.
For every interest ID (even when new ones are added), I need to make sure that each user has an entry for that interest ID (even if its blank, or has defaults).
Will foreign keys help with this scenario? or will I need to use PHP to update each and every record when I add a new key?
Foreign keys are a kind of constraint, so they can only fail when you attempt to add records.
You can accomplish what you are describing with a trigger. I don't know the MySql syntax, but in SQL Server it would look something like this:
CREATE TRIGGER TR_ensure_user_interest ON interest FOR INSERT, UPDATE AS
BEGIN
INSERT user_interest (user_id, interest_id)
SELECT user_id, interest_id
FROM inserted
,user
EXCEPT (SELECT user_id, interest_id)
END
Note that this is a rather inefficient approach, but it should cover many of the cases you're concerned about.
UPDATE: I agree with the others who have observed the design "smell" here. If you can accomplish the required result using JOIN queries, that would be a much more efficient solution. However, I was trying to answer the question actually asked. (Plus, I have been in this situation, where physical records are helpful to other database users who are not adept at compound queries.)
For every interest ID (even when new
ones are added), I need to make sure
that each user has an entry for that
interest ID (even if its blank, or has
defaults).
It sounds like you need an OUTER JOIN (either LEFT or RIGHT) in one of your queries instead.
For example, if you wanted to get the level of interest a particular person has for each interest:
Assuming your tables look like this:
users:
user_id PK
user
user_interests:
user_id PK FK
interest_id PK FK
interest_level
interests:
interest_id PK
interest
SELECT i.interest, ui.interest_level
FROM interests i
INNER JOIN user_interests ui USING (interest_id)
LEFT JOIN users u USING (user_id)
WHERE user_id = ?
? is a placeholder.
Note that ui.interest_level will be null for interests with no data.
It sounds like you are forcing your physical design to mirror your logical design too tightly.
Maybe it would be a good idea to rethink exactly why you need to insert a row for every user in the physical table. Couldn't you just write your queries to assume the default value for an interestID if there isn't an associated interestID for a given user?
"Will foreign keys help with this scenario?"
No.
Your constraint is a sort of "completeness" constraint. It implies that for each new Interest added, there must be as many rows added to the USER_INTEREST table as there are users.
No SQL system is able to enforce that for you. It's up to you to enforce it through code.

Categories