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
Related
I am using jQuery datatble server side processing (https://datatables.net).
First i am sharing my database structure you are aware about idea that how the data stored in database and how to render it.
Table Row Master:
Table Column Master:
Table Data Master:
These are the three tables
And I am putting query for the datatable with limit and searching but i can not achieve it with ordering any column because all data save in one db table table (Table Data master) so can you please provide solution to do this or suggest me any other way to do for it.
$strQuery = "SELECT tc.id as column_id,tr.id as row_id,td.data_value,td.link_type,td.link_value,td.is_new_tab FROM
(
SELECT tc.id as column_id,tr.id as row_id,td.data_value,td.link_type,td.link_value,td.is_new_tab
from table_data td
INNER join table_columns tc on tc.id=td.column_id and tc.table_id='1'
INNER join table_rows tr on tr.id=td.row_id and tr.table_id='1'
where td.table_id='1' and td.deletedBy=0 ".$strWhere."
GROUP BY td.id order by tr.isSequence ASC,tc.isSequence ASC
)
as tdata
INNER JOIN table_data td on td.row_id = tdata.row_id
INNER JOIN table_rows tr ON tr.id = td.row_id
INNER JOIN table_columns tc ON tc.id = td.column_id
GROUP by td.id ORDER BY tr.isSequence asc,tc.isSequence asc ";
$cols = ((int)$_POST['cols']); // number of cols
$top = 0;
if(isset($_POST['start']))
{
$top = ($cols) * ((int)$_POST['start']);
}
$limit = $cols *10;
if(isset($_POST['length']))
{
$limit = $cols * ((int)$_POST['length']);
}
$strQuery .= " LIMIT ".$top.",".$limit." ";
I want to fetch 10 rows then I want 60 records and prepare the array as row and cols array
foreach ($list_data['data'] as $tableData) {
$rowData[$tableData["row_id"]][$tableData["column_id"]] = $tableData;
}
This is how I am getting final array but I am not getting order by functionality in this. so please provide me the suggestion.
I am trying to return all the results from table one, AKA ship_skill_tree, while matching up the rows found in table two, AKA character_sheet_skills, even if the rows do not exist in table two.
SELECT c.`level` , t.`skillLevel` AS levelNeeded, i.`typeName`
FROM `ship_skill_tree` t
LEFT JOIN `character_sheet_skills` c ON t.`skillTypeID` = c.`typeID`
LEFT JOIN `invTypes` i ON i.`typeID` = t.`skillTypeID`
WHERE t.`shipTypeID` = 11176 AND c.`character_id` = 1;
Table One Data:
|shipTypeID|shipGroupID|skillTypeID|skillLevel
______________________________________________
|11011|26|3332|1
|11129|31|3327|1
|11132|31|3327|1
|11134|31|3327|1
|11172|830|3328|5
|11172|830|12093|1
|11174|893|3328|5
|11174|893|28615|1
|11176|831|3330|5
|11176|831|12092|1
Table Two Data:
|character_id|typeID|skillpoints|level|published
______________________________________________
|1|3300|1415|2|1
|1|3301|8000|3|1
|1|3327|256000|5|1
|1|3330|2829|2|1
|1|3340|181020|4|1
|1|3341|1024000|5|1
|1|3342|32000|3|1
|1|3343|32202|3|1
|1|3380|256000|5|1
|1|3385|256000|5|1
|1|3386|256000|5|1
|1|3392|256000|5|1
|1|3394|90514|4|1
|1|3402|256000|5|1
|1|3410|768000|5|1
|1|3411|135765|4|1
|1|3412|750|1|1
|1|3413|256000|5|1
|1|3416|45255|4|1
|1|3417|0|0|1
|1|3418|0|0|1
|1|3419|135765|4|1
|1|3420|181020|4|1
|1|3423|0|0|1
|1|3425|90510|4|1
|1|3426|45255|4|1
|1|3428|500|1|1
|1|3429|8000|3|1
|1|3436|45255|4|1
|1|3437|45255|4|1
|1|3438|500|1|1
|1|3449|256000|5|1
|1|3453|0|0|1
|1|3455|256000|5|1
|1|3456|226275|4|1
|1|11579|271530|4|1
|1|12186|0|0|1
|1|12187|0|0|1
|1|12188|0|0|1
|1|12190|22547|3|1
|1|12191|45255|4|1
|1|12192|45255|4|1
|1|12193|45255|4|1
|1|12195|45255|4|1
|1|16281|256000|5|1
|1|17940|1024000|5|1
|1|20342|1280000|5|1
|1|22551|40000|3|1
|1|22578|181020|4|1
|1|25739|0|0|1
|1|26252|16000|3|1
|1|26253|750|1|1
|1|26261|750|1|1
|1|32918|16000|3|1
invTypes table:
|typeID|typeName
________________
|3327|Spaceship Command
|3328|Gallente Frigate
|3330|Caldari Frigate
|3332|Gallente Cruiser
|12092|Interceptors
|12093|Covert Ops
|28615|Electronic Attack Ships
In the above query shipTypeID will always, or should always, be valid and match a record in table one, however, in table two, the rows that match may not exist. What I need is to output as follows:
|level|levelNeeded|typeName
___________________________
|2|5|Caldari Frigate
|NULL|1|Interceptors
Currently this is what is returned:
|level|levelNeeded|typeName
___________________________
|2|5|Caldari Frigate
EDIT: Solution!
SELECT c.`level` , t.`skillLevel` AS levelNeeded, i.`typeName`
FROM `ship_skill_tree` t
LEFT JOIN `character_sheet_skills` c ON t.`skillTypeID` = c.`typeID` AND c.`character_id` = 1
INNER JOIN `invTypes` i ON i.`typeID` = t.`skillTypeID`
WHERE t.`shipTypeID` = 11176
You need to put any restrictions on the table being joined in the ON clause. If you put them in the WHERE clause it doesn't work, because the rows that don't have any matches will produce NULL for those columns, and the WHERE clause will filter them out.
SELECT c.`level` , t.`skillLevel` AS levelNeeded, i.`typeName`
FROM `ship_skill_tree` t
LEFT JOIN `character_sheet_skills` c ON t.`skillTypeID` = c.`typeID` AND c.`character_id` = 1
LEFT JOIN `invTypes` i ON i.`typeID` = t.`skillTypeID`
WHERE t.`shipTypeID` = 11176
DEMO
You need to use a right join or an outer join rather than a left join. Have a look through the Visual Representation of SQL Joins for a good overview
The following code is used in a query for fetching records. It uses the electors.ID to find the corresponding voting_intention.elector from a second table.
$criteria = "FROM voting_intention,electors WHERE voting_intention.elector = electors.ID AND voting_intention.pledge IN ('C','P') AND electors.postal_vote = 1 AND electors.telephone > 0"
The problem is that some electors will have more than one pledge in the voting_intentions table.
I need it to match only on the latest voting_intention.pledge based on the field votin_intention.date for each elector.
What is the simplest way of implementing that.
The rest of the code:
function get_elector_phone($criteria){
$the_elector = mysql_query("SELECT * $criteria ORDER BY electors.ID ASC"); while($row = mysql_fetch_array($the_elector)) {
echo $row['ID'].','; }
}
You could use a sub-select with the MAX() function. Add the following into your WHERE clause.
AND voting_intention.date = (select MAX(date)
from voting_intention vote2
where voting_intention.elector = vote2.elector)
Here is a SQLFiddle of the results.
So pretty much, you only want to bother looking at the most recent row that fits the first two criteria in your code. In that case, you would want to filter out the voting_intention table beforehand to only have to worry about the most recent entries of each. There's a question/answer that shows how do do that here.
Try selecting the following instead of voting_intention (from the answer of the linked question, some table and field names replaced):
SELECT voting_intention.*
FROM voting_intention
INNER JOIN
(
SELECT elector, MAX(date) AS MaxDate
FROM voting_intention
GROUP BY elector
) groupedintention ON voting_intention.elector = groupedintention.elector
AND voting_intention.date = groupedintention .MaxDate
Question url: How can I SELECT rows with MAX(Column value), DISTINCT by another column in SQL?
i am having trouble creating a single mysql query for what i am trying to do here.
first off, i will show you the table structures and fields of the tables i am using for this particular query:
users:
- id
- name
- photo_name
- photo_ext
user_attacks:
- id
- level
user_news_feed:
- id
- id_user
- id_status
- id_attack
- id_profile
- id_wall
- the_date
user_status:
- id
- status
user_wall:
- id
- id_user
- id_poster
- post
whenever the user posts an attack, or status update, updates their profile, or posts on someones wall, it inserts the relevant data into its respective table and also inserts a new row into the user_news_feed table.
now, what i want to do is select the last 10 news feed items from the database. these news feed items need to grab relevant data from other tables as long as their value is not 0. so if the news feed is for a status update, the id_status would be the id of the status update in the user_status table, and the "status" would be the data needing to be selected via a left join. hope that makes sense.
heres my first mysql query:
$sql = mysql_query("select n.id_user, n.id_status, n.id_attack, n.id_profile, n.id_wall, n.the_date, u.id, u.name, u.photo_name, u.photo_ext, s.status
from `user_news_feed` as n
left join `users` u on (u.id = n.id_user)
left join `user_status` s on (s.id = n.id_status)
where n.id_user='".$_GET['id']."'
order by n.id desc
limit 10
");
now this works great, except for 1 problem. as you can see the user_wall table contains the id's for 2 different users. id_user is the user id the post is being made for, and id_poster is the user id of the person making that wall post. if the user makes a wall post on his/her own wall, it is inserted into the database as a status update into the user_status table instead.
so i have a conditional statement within the while loop for the first query, which has another sql query within it. here is the whole code for the while loop and second sql query:
while ($row = mysql_fetch_assoc($sql))
{
if ($row['id_wall'] != 0)
{
$sql_u = mysql_query("select u.id, u.name, u.photo_name, u.photo_ext, w.post
from `user_wall` as w
left join `users` u on (u.id = w.id_poster)
where w.id='".$row['id_wall']."'
");
while ($row_u = mysql_fetch_assoc($sql_u))
{
$row['photo_name'] = $row_u['photo_name'];
$row['photo_ext'] = $row_u['photo_ext'];
$row['id_user'] = $row_u['id'];
$row['name'] = $row_u['name'];
$content = $row_u['post'];
}
}
else
{
if ($row['id_status'] != 0)
$content = $row['status'];
else if ($row['id_attack'] != 0)
$content = '<i>Had an attack</i>';
else if ($row['id_profile'] != 0)
$content = '<i>Updated profile</i>';
}
echo '<li'.(($count == $total_count) ? ' class="last"' : '').'>';
echo '<img src="images/profile/'.$row['photo_name'].'_thumb.'.$row['photo_ext'].'" alt="" />';
echo '<div class="content">';
echo '<b>'.$row['name'].'</b>';
echo '<span>'.$content.'</span>';
echo '<small>'.date('F j, Y \a\t g:ia', $row['the_date']).'</small>';
echo '</div>';
echo '<div style="clear: both;"></div>';
echo '</li>';
}
i hope what i am trying to do here makes sense. so basically i want to have both sql queries ($sql, and $sql_u) combined into a single query so i do not have to query the database every single time when the user_news_feed item is a wall post.
any help would be greatly appreciated and i apologise if this is confusing.
SELECT n.id_user, n.id_status, n.id_attack, n.id_profile, n.id_wall, n.the_date,
u.id, u.name, u.photo_name, u.photo_ext, s.status,
w.id AS wall_user_id, w.name AS wall_user_name,
w.photo_name AS wall_user_photo_name,
w.photo_ext AS wall_user_photo_ext,
w.post
FROM user_news_feed AS n
LEFT JOIN users AS u ON (u.id = n.id_user)
LEFT JOIN user_status s ON (s.id = n.id_status)
LEFT JOIN (SELECT a.id AS id_wall, b.id, b.name, b.photo_name, b.photo_ext, a.post
FROM user_wall AS a
LEFT JOIN users AS b ON (b.id = a.id_poster)
) AS w ON w.id_wall = n.id_wall
WHERE n.id_user = ?
ORDER BY n.id desc
LIMIT 10
The '?' is a placeholder where you can provide the value of $_GET['id'].
Basically, this adds an extra outer join, to the main query (and some extra columns, which will be NULL if the news feed event is not a wall posting), but the outer join is itself the result of an outer join.
Back again ;)
Anyway, forget about merging the queries in my opinion.
What you should do instead is to do the first query, loop through all the results and store all "id_wall"s in a separate array... then rather than doing a separate query per "id_wall" you do this:
$wallids = array();
while ($row = mysql_fetch_assoc($sql))
{
$wallids[] = $row['id_wall'];
// also store the row wherever you want
}
$sql_u = mysql_query("select u.id, u.name, u.photo_name, u.photo_ext, w.post
from `user_wall` as w
left join `users` u on (u.id = w.id_poster)
where w.id IN ('".implode(',', $wallids) ."')
");
$wallids being an array with all the "id_wall"s. So now you have a total of 2 queries.
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;
?>