I have some problem with a table that I want to build.
This table use a mysql database with mutliple tables linked by many-to-many tables.
I use JSON code to insert values in the jQuery Table.
Here is the model used to query the values in database :
function list_all()
{
$login_id = $this->session->userdata('User_id');
$this->db->select('p.project_id, p.Project, p.Description, p.Status, p.Thumbnail, p.StartDate, p.EndDate, t.template_id, t.Template')
->select('GROUP_CONCAT(DISTINCT v.Name SEPARATOR ",") as PeopleList, GROUP_CONCAT(DISTINCT w.asset_id SEPARATOR ",") as AssetsList', FALSE)
->from('projects p')
->join('assigned_projects_ppeople a', 'a.project_id = p.project_id')
->join('assigned_assets_pproject w', 'w.project_id = p.project_id', 'left')
->join('project_templates t', 't.template_id = p.template_id')
->join('people v', 'v.people_id = a.people_id')
->where('a.people_id', $login_id)
->group_by('p.project_id');
$query = $this->db->get();
$rows = $query->result_array();
//Return result to jTable
$jTableResult = array();
$jTableResult['Result'] = "OK";
$jTableResult['Records'] = $rows;
$result = json_encode($jTableResult);
return $result;
}
All values are well listed in the jQuery table, except GROUP_CONCAT values which are duplicated for People Name by the number of assets listed in Assets column...
EDIT : Problem resolved using DISTINCT keyword in GROUP_CONCAT.
But, if there's no entry assets linked to a project, the project doesn't be loaded and doesn't appear in table. EDIT : Resolved using LEFT JOIN on "assigned_assets_pproject" table.
Here is the screenshot of the table with JSON code http://i.stack.imgur.com/jEj4D.png
For information, there's just one user "Michael Bonfill" in each project.
Here is the output of last_query()
SELECT `p`.`project_id`, `p`.`Project`, `p`.`Description`, `p`.`Status`, `p`.`Thumbnail`, `p`.`StartDate`, `p`.`EndDate`, `t`.`template_id`, `t`.`Template`, GROUP_CONCAT(v.Name SEPARATOR ", ") as PeopleList, GROUP_CONCAT(w.asset_id SEPARATOR ", ") as AssetsList
FROM (`projects` p)
JOIN `assigned_projects_ppeople` a ON `a`.`project_id` = `p`.`project_id`
JOIN `assigned_assets_pproject` w ON `w`.`project_id` = `p`.`project_id`
JOIN `project_templates` t ON `t`.`template_id` = `p`.`template_id`
JOIN `people` v ON `v`.`people_id` = `a`.`people_id`
WHERE `a`.`people_id` = '1'
GROUP BY `p`.`project_id`
I can export the SQL of my database for more info if you want.
Thank you !
use DISTINCT keyword like that GROUP_CONCAT(DISTINCT v.Name SEPARATOR ",")
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 applied the method Additional Translation Approach in the database design.
With such structure of the tables, the code becomes more complex for per query.
My PHP code in models:
<?php
// SHOW ALL RECORDS
$this->db->select('m.id, m.title, m.content');
$table = 'blog';
if (MULTILINGUAL) {
$this->db->from($table.' AS m');
$this->db->select('t.title, t.content');
$this->db->join($table.'_translation AS t', 'm.id = t.parent_id', 'left');
$this->db->where('t.language_id', LANGUAGE);
$query = $this->db->get();
} else $query = $this->db->get($table.' AS m');
?>
So I want to change it's code...
When MULTILINGUAL is true, and with per query has column fields is title, content,...
$table = 'blog';
$this->db->select('id, title, content');
$query = $this->db->get($table);
it will automatically use the method JOIN with a table have suffix _translation (as my code above).
Otherwise, queries should be run as a normal query.
How can I do modified db class but don't affects core system of Codeigniter?
PHP code (using Codeigniter):
// Query 1:
$this->db->select('id, title, content');
$query = $this->db->get('blog');
// Query 2:
$this->db->select('id, title, content');
$this->db->where('id', 1);
$query = $this->db->get('blog');
Produces $this->db->last_query():
if (MULTILINGUAL) {
// Query 1:
// SELECT t.title, t.content FROM blog AS m LEFT JOIN blog_translation AS t ON m.id = t.parent_id WHERE t.language_id = 1
// Query 2:
// SELECT t.title, t.content FROM blog AS m LEFT JOIN blog_translation AS t ON m.id = t.parent_id WHERE t.language_id = 1 WHERE m.id = 1
else {
// Query 1:
// SELECT title, content FROM blog
// Query 2:
// SELECT title, content FROM blog WHERE id = 1
}
I want it to be completely automatic.
I think that could change the db class to solve this problem, but direct intervention into the core system is unstable (within core update)...
I truly appreciate your help in resolving my problem!
This might help you to work around I don't know how you were using that config file but can achieve that functionality as
function your_function($multilingual = false) {
$table = 'blog';
if ($multilingual === true) {
$this->db->select('t.title, t.content');
$this->db->join($table . '_translation AS t', 'm.id = t.parent_id', 'left');
$this->db->where('t.language_id', LANGUAGE);
$query = $this->db->get($table . ' AS m')->result_array();
} else {
$this->db->select('m.id, m.title, m.content');
$query = $this->db->get($table . ' AS m')->result_array();
}
return $query;
}
You could create a view and just call : $this->db->where('t.language_id', LANGUAGE); , but I don't know really if this is a better solution.
I've two categories and I want to fetch three records of each category later I found this link UNION query with codeigniter's active record pattern after this I change my DB_Active_rec file and add this code also
var $unions = array();
public function union_push($table = '') {
if ($table != '') {
$this->_track_aliases($table);
$this->from($table);
}
$sql = $this->_compile_select();
array_push($this->unions, $sql);
$this->_reset_select();
}
public function union_flush() {
$this->unions = array();
}
public function union() {
$sql = '(' . implode(') union (', $this->unions) . ')';
$result = $this->query($sql);
$this->union_flush();
return $result;
}
public function union_all() {
$sql = '(' . implode(') union all (', $this->unions) . ')';
$result = $this->query($sql);
$this->union_flush();
return $result;
}
and then I create codeigniter's function based query like this
$this->db->select("*");
$this->db->from("media m");
$this->db->join("category c", "m.category_id=c.id", "INNER");
$this->db->order_by("m.media_files", "DESC");
$this->db->limit(3);
$this->db->union_push();
$this->db->select("*");
$this->db->from("media m");
$this->db->join("category c", "m.category_id=c.id", "INNER");
$this->db->order_by("m.media_files", "DESC");
$this->db->limit(3);
$this->db->union_push();
$getMedia = $this->db->union_all();
create this
(SELECT * FROM media m INNER JOIN category c ON
m.category_id = c.id ORDER BY m.media_files DESC LIMIT 3)
UNION ALL
(SELECT * FROM media m INNER JOIN category c ON
m.category_id = c.id ORDER BY m.media_files DESC LIMIT 3)
Now it is fetching records but not properly I want to use only query, it showing six records first query fetch 3 records and second query fetch three records now records are duplicate I check the id of records it is 6,5,4 and again 6,5,4. It can be done also by PHP but I want to use query. Thanks in advance
I dont know code-igniter, but basicly you want it to do the union first and then apply the order by over the whole set. This would require a subquery. It should result in the following SQL query:
select * from
((SELECT * FROM media m INNER JOIN category c ON m.category_id = c.id )
UNION ALL
(SELECT * FROM media m INNER JOIN category c ON m.category_id = c.id)) T
ORDER BY m.media_files DESC LIMIT 3
Hope it helps you some.
I have a database with many-to-many relations on tag and person. I am in trouble when tried to search a person with multiple tags. I tried this but its fail:
$person = Doctrine_Query::create()
->from("Model_Person p")
->innerJoin("p.tags t")
->whereIn('t.id',$t)
->execute();
The above statement return all persons that have at least one of the tags in array $t, but I want only person that havel 'all' the tags on that array.
Anyone know how to achieve this ?
Thanks
This is because IN returns all results with at least one match. Say if you have id IN(1,2,3) it's the same as id = 1 OR id = 2 OR id = 3.
To archieve your desired goal, use Doctrine's andWhere() method with each value that is now in $t e.g. [...]->andWhere('id = ?', 1)->andWhere('id = ?', 2)->[...]
After some time of research I found and adopt a pure SQL code which works exactly as I need.
In my app rizidoro's person table is info table.
//$tagQueryString contains tags separated by space ex. "crisis usa"
$tagsArray = explode(' ', $tagQueryString);
$tagsArrayQuery = '"' . implode('","', $tagsArray) . '"';
//$tagsArrayQuery='"crisis","usa";
$fromQuery =
'
info i
INNER JOIN (SELECT it.info_id
FROM info_tag it
INNER JOIN info i
ON i.id = it.info_id
INNER JOIN tag t
ON t.id = it.tag_id
WHERE t.name IN (' . $tagsArrayQuery . ')
GROUP BY it.info_id
HAVING COUNT(it.info_id) = ' . count($tagsArray) . ') ii
ON i.id = ii.info_id';
$query = new Doctrine_RawSql();
$query->select('{i.*}');
$query->from($fromQuery);
$query->addComponent('i', 'Model_Info i');
//you can add offset and limit for pagination
$infos = $query->limit($resultsPerPage)
->offset($resultsPerPage * ($currentPage - 1))
->execute();
That is not elegant code, but works fine. I have no idea how to do it more "Doctrine-like".
Here are other solutions to similar problems:
http://www.sergiy.ca/how-to-write-many-to-many-search-queries-in-mysql-and-hibernate
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