I'm hoping someone may be able to provide some advice regarding a database schema I have created and a SELECT statement that I am using to query the database.
I am attempting to create a database of very old newspaper articles from the 1800's, storing such things as the date, title and full text of the article, an image of the article, the name of the newspaper the article came from, names of locations mentioned in the article and individuals mentioned within the article.
Basically below is the current structure I've created with tbArticle being the main table focus ("test" is the name of the database). I've normalised the name of the newspaper, image info, location info and individuals into their own tables and because it is assumed there will be many articles to many individuals, I've added a link table (lktbArticleIndividuals) of sorts between tbArticle & tbIndividual;
The reason for creating the database is to obviously make a focused set of newspaper articles searchable and store them in a logical format.
My issue or question is this ...
All I want to do is display a list of all the articles in the database, obviously including data from the other tables other than tbArticle and to do this I am using this SELECT query;
SELECT *
FROM tbArticle a
, tbLocation l
, tbNewspapers n
, tbIndividual i
, lktbArticleIndividuals ai
, tbImage m
WHERE a.idLocation = l.idLocation
and a.idNewspaper = n.idNewspaper
and a.idArticle = ai.idArticle
and ai.idIndividual = i.idIndividual
and a.idImage = m.idImage;
Which does what I want ... except ... if more than one individual is listed as being in an article, then two (or more) instances of the whole article are returned with the only difference being the different individual's names being displayed.
If possible, I want to just list each article ONCE, but iterate through the two or more individuals to include them. Can this be done?
If I were to query the database in say PHP I suspect what I might have to do is some sort of loop within a loop to achieve the results I want, but this doesn't seem very efficient to me!!
Does any of this make sense to anyone?!
Instead of SELECT *, you could name the columns you're interested in, and for things such as individuals, use GROUP_CONCAT() to add them all into one field, and at the end of your query, use GROUP BY a.idArticle to limit each article to one row per article.
Assuming you just want the first_name of each individual you could use a group by with a GROUP_CONCAT.
SELECT *,
GROUP_CONCAT(i.firstname)
FROM tbArticle a
, tbLocation l
, tbNewspapers n
, tbIndividual i
, lktbArticleIndividuals ai
, tbImage m
WHERE a.idLocation = l.idLocation
and a.idNewspaper = n.idNewspaper
and a.idArticle = ai.idArticle
and ai.idIndividual = i.idIndividual
and a.idImage = m.idImage;
GROUP BY a.idArticle
However, if you want to get many details of each individual I would encourage you to do two separate queries: one for the articles and another one to get the individuals of each article.
Related
I'm working on an existing application that uses some JOIN statements to create "immutable" objects (i.e. the results are always JOINed to create a processable object - results from only one table will be meaningless).
For example:
SELECT r.*,u.user_username,u.user_pic FROM articles r INNER JOIN users u ON u.user_id=r.article_author WHERE ...
will yield a result of type, let's say, ArticleWithUser that is necessary to display an article with the author details (like a blog post).
Now, I need to make a table featured_items which contains the columnsitem_type (article, file, comment, etc.) and item_id (the article's, file's or comment's id), and query it to get a list of the featured items of some type.
Assuming tables other than articles contain whole objects that do not need JOINing with other tables, I can simply pull them with a dynamicially generated query like
SELECT some_table.* FROM featured_items RIGHT JOIN some_table ON some_table.id = featured_items.item_id WHERE featured_items.type = X
But what if I need to get a featured item from the aforementioned type ArticleWithUser? I cannot use the dynamically generated query because the syntax will not suit two JOINs.
So, my question is: is there a better practice to retrieve results that are always combined together? Maybe do the second JOIN on the application end?
Or do I have to write special code for each of those combined results types?
Thank you!
a view can be thot of as like a table for the faint of heart.
https://dev.mysql.com/doc/refman/5.0/en/create-view.html
views can incorporate joins. and other views. keep in mind that upon creation, they take a snapshot of the columns in existence at that time on underlying tables, so Alter Table stmts adding columns to those tables are not picked up in select *.
An old article which I consider required reading on the subject of MySQL Views:
By Peter Zaitsev
To answer your question as to whether they are widely used, they are a major part of the database developer's toolkit, and in some situations offer significant benefits, which have more to do with indexing than with the nature of views, per se.
I have been trying to create a database for fun to get a better understanding of databases and using PHP to query them for a website I'm messing around with. Pretty much I have one database with 4 tables when a user enters a search term in a PHP search box my code searches the database for any entries containing the search term. Now I can easily get my code to search individual tables, but I cannot seem to get it to search all 4 tables and display the results on the same page.
info: making a database for skyrim
Table names: classes, powers, skills, shouts
column names: name, information
Here is a snippet of the code I have that works so far:
$raw_results = mysql_query("
SELECT *
FROM `xaviorin_skyrim`.`shouts` , `xaviorin_skyrim`.`classes`
WHERE (CONVERT(`UID` USING utf8) LIKE '%".$query."%' OR
CONVERT(`Name` USING utf8) LIKE '%".$query."%' OR
CONVERT(`Information` USING utf8) LIKE '%".$query."%')
") or die(mysql_error());`
Literally all I thought I would need to do is change the table name from "shouts" to say "classes" in a new raw_results line of code but that didn't work. I have attempted unions and joins and either keep screwing them up or just don't understand how to properly format them.
echo "<p><h3>".$results['Name']."</h3>".$results['Information']."</p>";
The code above this text is what displays the results on the page on my website... it works but I don't know how to combine the information from all 4 tables into one page. If I'm going about this in the wrong way and anyone can point me in the right direction I would GREATLY appreciate it... I've been trying to research the problem without finding a proper answer for near a month now.
The problem with your approach is that relational databases do a cross join when there are several query results from two different tables. So basically every match in one table will be combined with every match from the second table. When you have 3 entries in the first and 4 in the second table, you will get 3 * 4 = 12 entries in your query result. If you add more tables, you get even more results. You want to do a full text search in several tables that are totally unrelated, thus creating some kind of non-existing relation via cross joining them will not be useful.
What you actually want to do is a UNION ALL (UNION is slower because it prunes duplicates) of several queries:
SELECT name, information, 'shouts' AS tablename FROM shouts WHERE ...
UNION ALL
SELECT name, information, 'classes' AS tablename FROM classes WHERE ...
This will do search queries on every single table and then place the results in a single result. Also note that I added a third column to each query to ensure that the originating table is not lost after merging the results.
Unless you need to do some sorting afterwards, I would suggest that you do all statements separately. Combining them this way will most likely make the post-processing more complex. And several single queries will also be faster than one big query with UNION statements.
And as I mentioned in the comments: Don't use mysql_* functions!
I have primarily 3 major tables called news,albums and videos. I want to create a facebook wall kind of page where in all the updates from all the three tables would appear sorted by posted time in descending order.
Is it possible to make this kind of call in a single query to db.
i will explain briefly my tables
news has id,title,content,timestamp
albums has id,title,albumdirectory,timestamp
videos has id,title,youtubelink,timestamp.
If not possible what would be the best way to do it.
Querying all three tables at the same time for this purpose will be not a good practice. You can create a feed table. and insert reference ids from all other tables you want i.e (news,albums,videos) and with the date of that field. Now you can query the feed table and put a join to other three tables on the basis of that reference id in that table and display them according to date in the feed table. I'm using this approach and this is working good for me.
Hope this helps.
It depends on how that data is designed. If it is all related using some shared ID, you can make a single join query to get all the data. If that data is not related, you will need to make 3 separate calls.
If the info you want on each entity shares the same structure (i.e. id, title, timestamp) then you can do this with a UNION
SELECT * FROM (
SELECT CONCAT('news','-',id),title,`timestamp`
FROM news
UNION
SELECT CONCAT('albums','-',id),title,`timestamp`
FROM albums
UNION
SELECT CONCAT('videos','-',id),title,`timestamp`
FROM videos
) AS all_items
ORDER BY `timestamp`
If the id fields are unique across the database (rather than just within each table) then you can remove the CONCATs and just return the ids.
Actually, I have no idea how does it looks like the question's title, is it exactly related to the issue or not, but I'm gonna try explain my problem:
I have a table named as AdvertClick (that stores the stats of each "Advert" by "AdvertID") and Advert table besides (as you guess the main table that stores my ad lists). So I need to store each advert click data (i.e. "Country","Browser","Language" etc.) in AdvertClick table. For example, the code is below getting top country data from AdvertClick table.
SELECT Count(_ac.ID) AS Click, _ac.Country
FROM `Advert` _a
LEFT JOIN `AdvertClick` _ac ON _ac.AdvertID = _a.ID
WHERE _a.UserID = $UserID
GROUP BY _ac.Country
ORDER BY Click DESC
LIMIT 1
But, I've replicated AdvertClick table as AdvertClick0, AdvertClick1 ... AdvertClick9, because it's (the unique stats table) was getting so heavy and slower. And now, I have 10 tables and all off them are same formed (meaning table colums). I'm just inserting every click data like this;
$TableName = "AdvertClick". $AdvertID % 10;
$SQL = "INSERT INTO ${TableName} ... VALUES (...)";
So, now I want to do same thing above but failing.
I've tried CREATE VIEW in very different ways like;
CREATE ALGORITHM = TEMPTABLE VIEW _tmp_ (colums...) SELECT x,y,z -> fail
CREATE VIEW _tmp_ (colums...) SELECT x,y,z JOIN AdvertClick0 _ac0 -> fail
etc...
Is anyone help me about this (really annoying) issue?
Thanks in advance...
hm... I think view would be appropriate for this kind of situation you should also have a good structured table to get the proper approach on this another approach I would do is to add another column that will identify what type of data are on that row for example:
table: advert
columns: name, country, type
the type will take any value you want or you may create another table and reference it from there as a foreign key to make it more flexible in that case you'll be having only 2 tables. To make if work as a view you can do it this way:
CREATE VIEW 'AdvertClick1' AS SELECT name, country FROM advert WHERE type = 1;
CREATE VIEW 'AdvertClick2' AS SELECT name, country FROM advert WHERE type = 2;
CREATE VIEW 'AdvertClick3' AS SELECT name, country FROM advert WHERE type = 3;
and so on and so forth. :)
i will propose something else entirely.
i think you should keep only one AdvertClick table - DO NOT create many of them and therefore create these other headaches. To get you answers will be even more difficult and even more slow.
Instead, if you are sure you have optimized your queries, you may choose to denormalize selectively to gather your common statistics.
for example:
create an ON INSERT trigger on the single AdvertClick table that increments the count value in a new AdvertClickByCountry table for the current Country. Also, create an ON DELETE trigger that decrements that count for the country.
Then your query above would be trivial - something like:
SELECT click_count, country
from AdvertClickByCountry
ORDER BY Click_count DESC
LIMIT 1
My application has a facebook-like stream that displays updates of various types. So it will show regular posts (from the "posts" table), and events (from the "events" tables) table and so on.
The problem is I have no idea how to fetch these records from different tables since they have different columns. Shall I query the database multiple times and then organize the data in PHP? if so, how? I'm not sure how I should approach this.
Your help is much appreciated :)
Unless the events and post are related to each other, then you'd probably query them separately, even if they show up on the same page.
You're not going to want to use JOIN just for the sake of it. Only if there is a foreign key relationship. If you don't know what that is, then you don't have one.
If the data tables are related to each other you can generally get the data back in a single query using some combination of JOINs and UNIONs. For a better answer, however, you'll have to post the structure of your data tables and a sample of what (combined) records you need for the website.
If you don't know the columns, you can get the table meta-data and find out what the columns represent and their corresponding data types.
If you know which columns, you can select from the multiple tables or even use nested selects or joins to get the data out.
Ideally you'd simply use a JOIN to obtain data from multiple tables in one query. However, without knowing more about your table schemas it's hard to provide any useful specifics. (It most likely won't be possible unless you've factored this in from the beginning that said.)
As such, you might also want to create a generic "meta" table that provides information for each of the posts/events in a common format, and provides a means to link to the relevant table. (i.e.: It would contain the "parent" type and ID.) You could then use this meta table as the source for the "updates" stream and drill down to the approriate content as required.
Join the tables on user_id i.e.
Select * from posts p
left join status_updates su on p.user_id = su.user_id
limit 25;
or if your tables differ too much then play with a temporary table first
create table tmp_updates
(
select user_id, p.id as update_id, 'post' as update_type, p.text from posts;
);
insert into table tmp_updates
(
select user_id, su.id as update_id, 'status' as update_type, su.text from status_updates;
);
Select * from tmp_updates
where user_id = '...'
limit 25;