Joining two Mysql VIEWs takes a very long time - php

I have a textbox (for which I used jQuery auto-completion) in my PHP form where user can insert an actor/actress name and then by clicking "Search" button, a new window is opened showing list of movies by that actor/actress.
This is a query where I get movies by actor name:
$query = $conn->prepare("SELECT DISTINCT c.movieName, c.castName, c.movieImdbId, f.year, f.posterLink FROM cast_movie as c JOIN film_info as f ON c.ImdbId = f.ImdbId WHERE c.castName = :q");
$query->execute(array(':q' => $searchText ));
My question:
The above query works fine if user select a name from auto-completion list. However I would like to enable user to write any name (even if he couldn't find from auto-completion list). For example, If user write "tom" in the textbox, and click on "search" button, I want to show list of all movies by all actors that their name containing word "tom".
For this purpose, I used LIKE :q and ':q' => '%' . $searchText . '%' in the above query, but the query never ends!!(I think because cast_movie is a VIEW which is quite large (with 3 million rows) and joining this view with the other table takes a very long time (actually I waited for 10 minutes and it didn't finished yet).
Could someone kindly let me know if there is any way to fix this? (I read we can use index for joining very large tables, but I think it's not possible to define index for cast_movie since it is VIEW.)
UPDATE:
Regarding comments, in order to provide more information:
cast_movie is a view which is made by joining "movie_roleNames" and "movies".
movie_roleNames is also a view which is made by joining two tables "Cast" and "nameRoles".
film_info is also a view that is made by joining two tables "movies" and "movies_info".
The structure of the above tables:
table "movies": Id, movieName, ImdbId(unique Id of movies), Rate, numVotes, year (indexes: ImdbId, movieName, year)
table "cast": castName, castImdbID (unique Id of casts) (indexes: castName, castImdbID)
table "nameRoles": Id, castImdbId, movieImdbId, role_Id, (indexes: movieImdbId, castImdbId)
VIEW "movie_roleNames": Id, castName, castImdbId, movieImdbId and the join statement was: SELECT n.Id, c.castName, n.castImdbId, n.movieImdbId
FROM nameRoles as n
join Cast as c
ON n.castImdbId = c.castImdbID
VIEW "cast_movie": Id, castName, castImdbId, movieImdbId, movieName and the join statement was: SELECT m.Id, r.castName, r.castImdbId, r.movieImdbId, m.movieName
FROM movie_roleNames AS r
JOIN movies AS m
ON r.movieImdbId = m.ImdbId
All ideas are highly appreciated,

Related

Getting data from 2 tables with relationship with WHERE clausule

I've been following the PHP Authentication Tutorial en managed to get it working.
Now I want to add a function to my navbar. I will try to explain and hopefully someone has the answer.
I have the 2 user tables, users and user_permissions.
In the user table there are additional columns named district_id, district, gemeente_id and gemeente. I also have 2 extra tables in the database called districten and gemeenten.
The districten table has 2 colums. id and district which holds the unique ID and the name. The gemeente table has at least these 3 columns id, district_id and gemeente.
Here is what I would like to happen:
When the user logs in the query will give a result in an <ul> in the navbar where it will show all values (column gemeente) from the gemeenten table where the user has been assigned to.
Example:
User ZZZ is assigned to district A.
This district holds 4 gemeenten : City 1, City 2, City 3 and City 4
When the user logs in, he should
only see the cities from the District A.
I don't know how to get this working in Slim2 Framework and integrated in the PHP Authentication code. So any help is much appreciated.
First you have to normalize your table and get rid of redundant information since it's hard to maintain it in relational database(I assume here you're using relational database like MySQL).
So basically each user is assigned to gemeente, each gemeente assigned to district and district in his turn has cities(gemeenten). In user table you only need the gemeente_id. The schema of districten and gemeente seems fine.
There are two way how to retrieve needed data: by running 2 queries(1 for fetch user's district; 2nd for fetch district's cities) and run everything in one query. Which way to choose is up to you.
1st approach:
SELECT d.id FROM district AS d LEFT JOIN gemeente AS g ON g.district_id=d.id LEFT JOIN user AS u ON u.gemeente_id=g.id WHERE u.id=<user_id>
SELECT g.id, g.gemeenten from gemeente as g where g.district_id = <res_of_prev_query>
2nd approach:
SELECT
g.id, g.gemeenten
FROM gemeente AS g
LEFT JOIN
districten AS d
ON g.district_id=d.id
INNER JOIN (
SELECT DISTINCT district_id, u.id AS user_id
FROM gemeente as gg
INNER JOIN user AS u
ON u.gemeente_id=gg.id) AS ud
ON ud.district_id=g.district_id
WHERE ud.user_id=<user_id>
You need transform this query to your ORM/ActiveRecord, run it and display the ul list
UPD: I've updated my answer. Thanks #DarkBee for pointing me to my mistake

Retrieving data from multiple tables and listing them in a list

I'm having some problems retrieving data from two tables and then listing them. I'd like to list the user's feed posts and their likes activity all in one.
Feeds - Table for users posts
Likes - Table for users likes (So when a use likes a post, a record is added to likes (Table likes contains data which contains the feeds ID of the post liked)
What I'm attempting to make: List BOTH feeds and user's Like activity in an ACTIVITY WALL.
So it should output like (ordered by timestamp desc):
"THIS IS A POST by user A"
Shows that user C liked user B's post
"THIS IS A POST by user B"
"THIS IS A POST by user L"
Shows that user A liked user F's post
"THIS IS A POST by user F"
-and it goes on-
My current SQL:
SELECT * FROM feeds,likes WHERE feeds.deleted!=0 or likes.deleted!=0 ORDER BY feeds.timestamp, likes.timestamp
However, my problem is I have no idea how to link both tables, since the IDs in my 'feeds' differ from those in 'likes'
To combine the two sets, you can use a UNION ALL set operator.
Something like this:
SELECT f.timestamp AS `timestamp`
, 'feed' AS `src`
, f.feed_id AS `id`
, f.feed_content AS `content`
FROM feeds f
WHERE f.deleted!=0
UNION ALL
SELECT l.timestamp AS `timestamp`
, 'like' AS `src`
, l.like_id AS `id`
, l.note AS `content`
FROM likes l
WHERE l.deleted!=0
ORDER BY 1 DESC
Note the the queries (on either side of the UNION ALL operator) need to match, in terms of the number of columns returned, and the datatype of each column.
To accommodate differences, such as extra columns returned from one table, but not from the other, you can add literal expressions in place of the "missing" columns.
The return of the extra src column is one way we can use to distinguish which query a row was returned by. It's not mandatory to return such a column, but it's something I often find useful. (The src column could be removed from each query, if it's not useful for your use case.)
Note that it's also possible to combine the results from more than two queries in this way, we'd just add another UNION ALL and another query.
The column names in the combined resultset are determined from the first query. The column names and aliases in the second query are ignored.
The ORDER BY applies to the entire set, and follows the last select.
Query should be linked via postID
F=feeds table, L=likes table, U1=usertable linked to owned feeds, U2=usertable linked to likes table
SELECT F.postTitle+' posted by '+ U1.username,'liked by'+U2.username
FROM likes L
LEFT JOIN feeds F on (F.postID=L.postID)
LEFT JOIN users U1 on (U1.userID=F.userID)
LEFT JOIN users U2 on (U2.userID=L.userID)
ORDER BY L.date,L.postID DESC
When you write SELECT * FROM feeds,likes... you are implicitly CROSS JOINing both tables. The engine will combine every record in each table with every record in the other. That is far from what you want.
I don't think you should be "linking" both tables, either. What you need, roughly speaking, is to get every post and every like, and then order that big set according to timestamps.
It sounds more like a UNION between two queries, and an ORDER BY applied to the whole UNION. UNIONs are never easy on the eye, by the way...
The thing with UNIONs is that both sub-queries need to return the same amount of columns. Not knowing exactly which columns you have, I'll show you one possible solution:
SELECT activity, timestamp FROM (
( SELECT CONCAT(u.name,' posted ',f.content) as activity, timestamp
FROM user u
JOIN feed f on (f.user_id=u.id)
WHERE f.deleted!=0
) UNION
( SELECT CONCAT(u.name, ' liked a post by ',u2.name) as activity, timestamp
FROM user u
JOIN likes l on (l.user_id=u.id)
JOIN feed f on (l.feed_id=f.id)
JOIN user u2 on (f.user_id=u2.id)
WHERE l.deleted!=0
)
) as whole_result
ORDER by timestamp desc
You should, of course, modify this to match your structure.
Hope this helps!
I think, it's better to use 3rd table, say, "actions", and insert to it real actions. Then just select rows from this table, joined to "posts" & "users" table.
When user posts articles, o likes an article, insert corresponding row to "actions" table.
actions table:
|id|action_name|user_id|post_id| date |
1 posted 3 3 5/7/2014
2 liked 5 3 5/7/2014
3 liked 4 3 6/7/2014
4 posted 5 6 7/7/2014
5 liked 3 6 7/7/2014
SELECT user_name a, post_title b, action_name c FROM actions c LEFT JOIN users a ON a.id=c.user_id LEFT JOIN posts b ON b.id = c.post_id ORDER BY c.date DESC LIMIT 10
Then, in loop, choose how to display this data, according to "action_name".
In such way you can expand your wall for other activities, +use indexes for better database performance.

Select from one table where id (from another table) exists

First of all, I'd just want to say that I'm sorry for the poor title. I'm really struggling with explaining the problem I'm facing in just a short sentence.
I have a table called actors which contains an aID (primary key) and an aName and some more stuff. I also have a table called videos which contains vID (primary key) and other data. The third and last table I have is called connections and that one contains a primary key, a cVideoID and a cActorID.
Let's say I have created a video and when I navigate to the video *www.example.com/video.php?v=primary_key* I'd like to print out all actors who are in that movie (actor1, actor2, actor3 and actor4). Therefor I created the connections-table to keep track of all movie-actor connections. For every movie I create I connect the actors with the movie.
I thought that I could do something like this:
<?php
$result2 = mysql_query("SELECT `actors`.`aID`, `actors`.`aName` FROM `actors` WHERE `connections`.`cVideoID` = {$_get['v']}");
while($actors = mysql_fetch_array($result2))
{
echo "<a href='actor.php?id={$actors['aID']}>{$actors['aName']}</a> ';
}
?>
But it seems like that's not working. Any ideas?
What you want to do, is select all of the entries from connections, where the video ID is the selected video. Then, you JOIN on the actors table, to get all of the information about the actors that were found.
Example: Get all of the actor's names for a specific video ID:
SELECT a.aName
FROM connections c
LEFT JOIN actors a
ON a.aID = c.aID
WHERE c.vID = 1;
SQL Fiddle
What you need here is a join.
A normal left join works like this:
LEFT JOIN [name of table] [name of table you will want to use]
ON ([where statements searching for the right columns to join])
So your query will look something like this:
SELECT a.aID, a.aName
FROM `connections` c
LEFT JOIN `author` a ON (c.cActorID=a.aID)
WHERE c.cVideoID=[id of the video]
Now first of all you say the database you want to catch the columns aID and aName from the table a next you use the FROM statement to "import" the connections table as "c". You then load the author table and make it accessible as "a" (see the select statement a.[...]) and you also say that it should join the two tables ON every c.cActorID=a.aID and in the end you make a where statement to declare you are only searching for videos with the c.cVideoId=[id]

Search multiple tables Mysql

I'm working on a advanced search functionality with php and mysql.
The tables are:
blogPost[id, title, description]
water[id, title, description,]
waterSpecie[id, waterId, specieId]
specie[id, name]
user[id, username]
What I want done is this:
The user will be able to search from a text field with a keyword, and the results should only be unique once.
For example:
The user searches for cod then I would like to show all the blogposts that has cod in there titles or description and all the waters that has cods in it and also if a user is nicked cod. But if let's say the blogPost has cod in its title and description I don't want it to show up twice in the results list.
But if only lets say the blogPost table has cod in its title I would only want to show that and nothing else.
I've managed to do it with this sql query: I would now like to add the blogPost table to that query and make it possible to search from it. So if only one table have cod I want to show only the result from that table.
SELECT DISTINCT W.lat, W.lng, W.municipalityId, W.title, W.description
FROM water AS W, specie AS S, waterSpecie AS WS
WHERE S.name LIKE '%$term%' AND W.id = WS.waterId AND S.id = WS.specieId
OR W.title LIKE '%$term%' AND W.id = WS.waterId AND S.id = WS.specieId;
I hope I've managed to explain my problem and hopefully I could get some help on this issue.
You might try this:
Select distinct tablename,id from
(select 'blogpost', as tablename, id, title as searchfield from blogpost
union
select 'blogpost' as tablename, id, description as searchfield from blogpost
union
select 'water' as tablename, id, description as searchfield from water
union
select 'specie' as tablename, id, name as searchfield from specie
union
select 'user' as tablename, id, username as searchfield from user) searcher
where searcher.searchfield='cod'
I don't exactly get what's going on with waterSpecie, so I've left it out.
You should get results something like:
tablename id
blogpost 5
user 12
And then you can query the database to get the record from blogpost with id=5, and the record from user with id=12.
You'll be able to do this programmatically in your PHP. And if you add a table that you want to add to the search functionality, you only have to do it by adding a UNION in the query below, rather than making a join and adding another piece to a WHERE clause.
I'm not sure that this would scale to Amazon.com size, so consult somebody better than me before you get that big.
You will be best served by simplifying your MySQL queries and combining results in your PHP code. The main reason for this in your case is that you want to combine dissimilar results - blog entries if they have the search term and/or waters and other things if they have the search terms.
In pseudo code, I'd do something like this (use a DAO in the real code, of course):
$foundposts = findblogposts($searchterm);
$foundusers = findusers($searchterm);
$foundwaters = findwaters($searchterm);
// etc
if($foundposts){
print($foundposts);
}
if($founduers){
print($foundusers);
}
if($foundwaters){
print($foundwaters);
}
// etc
That way, you can handle the different formatting requirements you probably have to show blog posts correctly, and users correctly, etc.

Mysql join query retrieving profile images of users with ids from other table

I'm having trouble with a join query, my issue is as follows.
Table: battles
Fields: id,attacker_id,defender_id
Table: users
Fields: id,profile_image
I would like to do a query to retrieve a battle and get the profile images as well from the other table.
Is there a way to do this in a single or do I have to do more than one?
Thanks in advance.
I wanted to wait a while to see if you had any attempt or if you will answer my first question to know if I understood the problem. But maybe you don't have a starting point. Try something like:
SELECT
a.profile_image as attacker_profile_image,
d.profile_image as defender_profile_image
FROM
`battles` b
LEFT JOIN
`users` a
ON
b.`attacker_id` = a.`id`
LEFT JOIN
`users` d
ON
b.`defender_id` = d.`id`
the problem here is the fact that you need to join with the users table twice, so you will need to create aliases for the columns you plan to use
This query will fetch the two images only, you will need to add the extra fields

Categories