I have three tables which are currently structured in the following way
Table: Images
image_id
image_title
...
Table: Keywords
keyword_id
keyword
Table: Image_Keyword
image_id
keyword_id
With this structure, I'm able to search if any images match any keywords using joins and or statements - however I would like to be able to retrieve images that have multiple keywords matches e.g. "keyword = ('red' or 'dress') and 'night'" - which would return all images that had either 'red' or 'dress' in them, alongside night.
Ideally I want to allow the user to be able to specify the AND and OR commands in the search box, which is why I have so far opted out of making separate joins for each new keyword - however I'm not sure how to proceed with the structuring of the query.
Currently I have the following, without the 'and' implementation:
SELECT i.* FROM images i
JOIN image_keyword ik ON i.id = ik.image_id
JOIN keywords k ON k.id = ik.keyword_id
WHERE k.keyword IN ('night','red')
Any help on how to go about creating the 'and' portion of this query would be greatly appreciated! Thanks kindly,
Dan
// UPDATE
So it looks as if I am going to have to do it by creating joins for each 'AND' request that I need to sort out - however I have an extension on the requirements now...
I have two other tables which follow the following structure
Table ImageData
id
image_id
caption_id
...
Table Caption
id
data (text)
In this instance, I would want to search for the keywords ('red','dress' and 'night'), using the same 'AND' and 'OR' capability as before, but also return the image if the text matches (using the same rules) in the caption data field. I would assume I potentially use an OR after the 'keyword' search, and then use a fulltext search on the caption, however I don't know if there is a cleaner way of combining the two, maybe even as two separate queries and then choosing the distinct results - which might allow for instances where the AND is successful in the keywords, and the OR is successful in the caption.
Any thoughts would be fantastic
Thanks again
I think what you will end up is this -
One INNER JOIN for all your ORs.
One INNER JOIN each for all your ands.
For example -
SELECT i.* FROM images i
INNER JOIN image_keyword ik ON i.id = ik.image_id
INNER JOIN keywords kOR ON kOR.id = ik.keyword_id AND (kOR.keyword IN ('dress', 'red'))
INNER JOIN keywords kAND1 ON kAND1.id = ik.keyword_id AND kAND1.keyword = 'night'
PHP script would look something like.
$orKeywords = arrya('dress', 'red', 'white');
$andKeywords = array('night', 'day');
$orJoin = '';
$andJoin = '';
if(count($orKeywords) > 0)
{
$orCondition = "'".implode("', '", $orKeywords)."'";
$orJoin = " INNER JOIN keywords kOR ON kOR.id = ik.keyword_id AND kOR.keyword IN ($orCondition) ";
}
if(count($andKeywords) > 0)
{
$cnt = 1;
foreach($andKeywords as $keyword)
{
$andJoin .= " INNER JOIN keywords kAND{$cnt} ON kAND{$cnt}.id = ik.keyword_id AND kAND{$cnt}.keyword = '$keyword' ";$cnt++;
}
}
$sql = "SELECT i.* FROM images i
INNER JOIN image_keyword ik ON i.id = ik.image_id
$orJoin
$andJoin";
You get the idea..
I would just generate the WHERE part of the query in PHP script, like this:
<?php
$entered_keywords = array('night','red');
$logic = 'OR'; // or 'AND'
$sql_where = implode(' '.$logic.' ', "k.keyword='$entered_keywords'"); //don't forget the escaping here!
$sql = 'SELECT i.* FROM images i
JOIN image_keyword ik ON i.id = ik.image_id
JOIN keywords k ON k.id = ik.keyword_id
WHERE '.$sql_where;
?>
Related
I have this php script called title, where it is supposed to list movie details of those movies with the title matching the inputed substring. The expected output is supposed to be like in the link/picture below. I have trouble with concatenating the genres of each movies since one movie can have many genres. I have tried using the concat(), array_to_string() but still fails.
mkSQL() constructs "safe" SQL query strings by taking a query template
string and filling in printf-like slots in the template with values
supplied in subsequent arguments. The function takes a variable number
of arguments; the first is always a query template string, with the
following arguments corresponding exactly to the slots in the
template. E.g.
$id = 3012345;
$q1 = mkSQL("select * from R where id = %d",$id);
would create the query strings:
$q1: "select * from R where id = 12345"
Below are the codes, any helps and tips will be greatly appreciated, thanks!
This is the Genre Table Schema
CREATE TABLE Genre (
movie_id integer REFERENCES Movie(id),
genre GenreType,
primary key (movie_id,genre));
#!/usr/bin/php
<?php
// include the common PHP code file
require("a2.php");
$db = pg_connect("dbname=mydb");
// Check arguments
if (count($argv) < 2) exit("$usage\n");
// Get the return results
$val = $argv[1];
$q = "select m.title, m.year, m.content_rating, r.imdb_score, array_to_string(array(select g.genre FROM Genre g where g.movie_id = m.id),',')
-- concat(select g.genre FROM Genre g where g.movie_id = m.id
from Movie m JOIN Rating r ON r.movie_id = m.id
where m.title ilike %p
order by m.year, r.imdb_score desc, m.title asc";
$r = pg_query($db, mkSQL($q, $val));
// Iterate through the results and print
$i = 1;
while ($t = pg_fetch_array($r)) {
echo "$i. $t[0] ($t[1], $t[2], $t[3]) [$t[4]]\n";
$i++;
}
?>
The expected output is supposed to be in this format
Change your query like,
SELECT CONCAT(m.title, ' (', m.year, ', ', m.content_rating, ',', r.imdb_score, ') [', (SELECT array_to_string(array_agg(g.genre), ',') FROM Genre g WHERE g.movie_id = m.id), ']') movie_title
FROM Movie m JOIN Rating r ON r.movie_id = m.id
WHERE m.title ilike %p
ORDER BY m.year, r.imdb_score desc, m.title ASC
Here, I have concat all columns into one and given it an alias movie_title. You will get the movie name as per your specified format.
For achieving this, you can use the group_concat function in your mysql script.
This will concatenate your respective column via comma(,).
I have used this code to get definitions:
//assuming you've connected to your MySQL db
$word=$_GET['s']; //This variable stores the value given through url
if (ctype_alpha($word)){ // If it's alphabetical
$word_clean=mysql_real_escape_string($word); //Sanitize it for MySQL
}else{
//Not a valid word, error handle
exit();
}
$query='SELECT wordno FROM word WHERE lemma=`$word_clean` LIMIT 1';
$result=mysql_query($query);
$query='SELECT synsetno FROM sense WHERE wordno=`$wordno`';
$query='SELECT definition FROM synset WHERE synsetno=`$synset`';
BUT NOW I want to get synonyms and similar of that word ($word);
SELECT a.lemma, c.definition
FROM word a
INNER JOIN sense b
ON a.wordno = b.wordNo
INNER JOIN synset c
ON b.synsetno = c. synsetno
WHERE a.lemma = 'valueHere'
To fully gain knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
A fast way to get a synonym/similar word using wordnet v3.0 with SQL:
SELECT w.lemma
FROM words AS w
LEFT JOIN senses AS s ON s.wordid = w.wordid
LEFT JOIN senses AS s2 ON s2.synsetid = s.synsetid
LEFT JOIN words AS w2 ON w2.wordid = s2.wordid
WHERE w.lemma <> 'coder'
AND w2.lemma = 'coder'
GROUP BY w.lemma
ORDER BY s.synsetid
I am having a hard time creating a mySQL join statement.
The issue is that it seems to return the correct results, but it returns duplicates.
$result= mysql_query("SELECT Photos.Filename, Photos.Filetype
FROM Photos, PhotoUserTags
WHERE PhotoUserTags.User_ID IN ($friendlist) && PhotoUserTags.Photo_ID = Photos.Photo_ID && Photos.Event_ID = $eid");
I am new to these statements, any help or guidance is greatly appreciated.
Here's your query:
SELECT
Photos.Filename, Photos.Filetype
FROM Photos
INNER JOIN PhotoUserTags ON (PhotoUserTags.Photo_ID = Photos.Photo_ID)
WHERE
Photos.Event_ID = $eid
AND PhotoUserTags.User_ID IN ($friendlist) /* assuming they are IDs separated by a comma) */
GROUP BY Photos.Photo_ID;
I would also explain this query just in case you use the right indexes to maximize the performance of your query
For one, the logical and in mysql is not && but AND, like this:
SELECT * from table WHERE field1 = 'value1' AND field2 = 'value2';
You should also use the newer join syntax like this:
SELECT Photos.Filename, Photos.Filetype
FROM Photos, PhotoUserTags
INNER JOIN PhotoUserTags ON PhotoUserTags.Photo_ID = Photos.Photo_ID
WHERE PhotoUserTags.User_ID IN ($friendlist)
AND Photos.Event_ID = $eid
GROUP BY Photos.Photo_ID
Note the join expression (I used inner join, assuming a matching record needs to exist in both tables) - it makes your where cleaner and easier to read.
Does this help:
$result= mysql_query("SELECT Photos.Filename, Photos.Filetype
FROM Photos, PhotoUserTags
WHERE PhotoUserTags.User_ID IN ($friendlist) && PhotoUserTags.Photo_ID = Photos.Photo_ID && Photos.Event_ID = $eid GROUP BY Photos.Photo_ID");
I want multiple photos and multiple videos, the main problem is that I can't get them inline if I don't use joins.
So for example, I get 2 photos a video and again a photo.
I have a parent news table and 2 secondary table news_photos and news_videos and I want to get in one query the photos and videos for the news.
Is this somehow possible?
mysql_query("
SELECT *
FROM news_photos, news_videos
FULL JOIN news_videos
ON news_id = {$news_id}
FULL JOIN news_photos
ON news_id = {$news_id}
");
An image about the structure:
There's actually only a single FULL JOIN in that, since you are not involving the news table at all.
SELECT *
FROM news_photos
FULL JOIN news_videos
ON news_photos.news_id=news_videos.news_id
WHERE news_photos.news_id=... OR news_videos.news_id=...
FULL JOIN is not supported by MySQL. It can be less-efficiently simulated using two LEFT JOINs and a UNION, but it's relatively rare that you actually need to. Assuming every photo and video does belong to a news, you could avoid it and get a more conventional query by bringing the news table into it:
SELECT *
FROM news
LEFT JOIN news_photos ON news_photos.news_id=news.id
LEFT JOIN news_videos ON news_videos.news_id=news.id
WHERE news_id=...
But still, this is almost certainly not what you mean! If there are multiple photos and videos for a news item, you would be effectively creating a cartesian product, where every combination of photo and video produces a row. This is the sort of combinatorial explosion you almost never want!
If you just want one of each photo and video, I suppose you could hack that into a single query using a LEFT JOIN that will always give NULL on the other side:
SELECT * FROM news_photos
LEFT JOIN news_videos ON 0
WHERE news_photos.news_id=...
UNION SELECT * FROM news_photos
RIGHT JOIN news_videos ON 0
WHERE news_videos.news_id=...
But there's really nothing to be gained by this. Don't shoehorn two separate queries (“I'd like the photos for a news, and the videos for a news”) into one. Just do it the trivial way:
SELECT * FROM news_photos
WHERE news_id=...
SELECT * FROM news_videos
WHERE news_id=...
i would do it using a stored procedure that had multiple select statements as follows:
http://pastie.org/1141100
drop procedure if exists list_news_photos_videos;
delimiter #
create procedure list_news_photos_videos
(
in p_news_id int unsigned
)
proc_main:begin
select n.* from news n where n.news_id = p_news_id;
select p.* from news_photos p where p.news_id = p_news_id order by photo_id desc;
select v.* from news_videos v where v.news_id = p_news_id order by video_id desc;
end proc_main #
you would call this in mysql as follows:
call list_news_photos_videos(2);
then you can call the stored procedure from php (1 db call only) using mysqli as follows:
http://pastie.org/1141103
<?php
// quick and dirty demo - needs to be made more robust !!
$db = new Mysqli("localhost", "foo_dbo", "pass", "foo_db");
$sql = sprintf("call list_news_photos_videos(%d)", 2); // get all the news related data in one query
$result = $db->query($sql);
//news item
$row = $result->fetch_assoc();
echo sprintf("<h2>news item</h2>news_id = %d subject = %s <br/>", $row["news_id"], $row["subject"]);
$result->free();
//news photos
$db->next_result();
$result = $db->use_result();
echo "<h2>news photos</h2>";
while ($row = $result->fetch_assoc()){
echo sprintf("photo_id = %d subject = %s<br/>", $row["photo_id"], $row["subject"]);
}
$result->free();
//news videos
$db->next_result();
$result = $db->use_result();
echo "<h2>news videos</h2>";
while ($row = $result->fetch_assoc()){
echo sprintf("video_id = %d subject = %s<br/>", $row["video_id"], $row["subject"]);
}
$result->free();
$db->close();
?>
I have two tables(entries and tags) with a many-to-many linking table. Right now, I'm making a query to retrieve all the entries that meet my criteria, and a query for each entry to retrieve the tags.
Would there be a way to say, have the tags returned through a new column as an array in the first query?
This query:
SELECT e.id entry_id
, GROUP_CONCAT(t.txt ORDER BY t.txt) tags
FROM entry e
LEFT JOIN entry_tag et
ON e.id = et.entry_id
LEFT JOIN tag t
ON et.tag_id = t.id
GROUP BY e.id
Will return the tags as a comma-separated list. You can read up on GROUP_CONCAT for more options: http://dev.mysql.com/doc/refman/5.1/en/group-by-functions.html#function_group-concat
In your app you should be able to expand this into an array quite easily. For example in php, you could use explode: http://php.net/manual/en/function.explode.php
If you need more attributes from the tag or entry_tag tables, you can either add yet more GROUP_CONCAT columns, or think of some serialization format for your data (like JSON) and use GROUP_CONCAT on that, or you can simply return multiple rows per entry and process the results in the application to keep tags together with entries:
$sql = '
SELECT e.id entry_id
, t.id tag_id
, t.txt tag_text
, t.published tag_published
FROM entry e
LEFT JOIN entry_tag et
ON e.id = et.entry_id
LEFT JOIN tag t
ON et.tag_id = t.id
ORDER BY e.id
';
$result = mysql_query($ql);
$entry_id = NULL;
$entry_rows = NULL;
while ($row = mysql_fetch_assoc($result)) {
if ($entry_id != $row['entry_id']) {
if (isset($entry_id)) { //ok, found new entry
process_entry($entry_rows); //process the rows collected so far
}
$entry_id = $row['entry_id'];
$entry_rows = array();
}
$entry_rows[] = $row; //store row for his entry for later processing
}
if (isset($entry_id)){ //process the batch of rows for the last entry
process_entry($entry_rows);
}
You can use GROUP BY and GROUP_CONCAT function to get all tags at once as a concatenated string. http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html