I am trying to rank the rows based on when was it lasted updated partitioned by category name.
$query = "SELECT
category_name,
topic_title,
updated_ts,
#topic_rank := IF(#current_category = category_name, #topic_rank + 1, 1) AS topic_rank,
#current_category := category_name AS current_category
FROM topic_master
ORDER BY category_name, updated_ts DESC
";
$data = $this->db->query($query);
if($this->db->affected_rows() > 0)
{
return $data;
}
else
{
return false;
}
This query runs perfectly fine in MySQL and gives me the topic_rank as 1,2,3 and so on.
When I run this in CodeIgniter, I get topic_rank as 1 for all records.
What could be the issue ?
Try to use codeigniger database methods result_array():
$q = $this->db->query($query);
$data = $q->result_array();
Found an alternate way to solve this problem. No idea why the user defined variable was not working.
select category_name, topic_title as last_topic, updated_ts as last_activity, topic_pri_key as last_topic_id from
(
select
a.category_name, a.topic_title,
a.updated_ts,
a.topic_pri_key,
count(b.updated_ts)+1 as rank
from
topic_master a
left join
topic_master b
on a.updated_ts < b.updated_ts and a.category_name = b.category_name
group by 1,2,3,4
) a
where rank = 1
Related
Hopefully someone here can help as I've spent 2 days searching.
Basically I am trying to send an array of user ids to a CI model and retrieve the last 10 transactions for each id.
I can limit the total number of records, but I want to limit to each id instead, and cant figure out how this is done?
I have read that ROW_NUMBER() may be what I'm looking for? But I'm unsure how to turn that from SQL into something suitable for using in a CI model. Below is the code I have at the moment:
function getTrans($cData, $date){
$this->db->select('id, userId, date');
$this->db->from('trans');
$this->db->where_in('userId', $cData);
if($date != 0){
$this->db->where('date >=', $date);
}
$this->db->order_by('id','asc');
$query = $this->db->get();
if ($query->num_rows() > 0) {
return $query->result();
} else {
return false;
}
}
As you can see I'm using WHERE_IN to cycle the array of ids, but is there a way to add a limit to each id called instead of just a limit on the whole amount?
I did try adding a loop in the model but it would just keep throwing me error 500s (I think its to do with the way the query is generated??)
Any help is much appreciated
You can use $this->db->query() with the following query:
$query = $this->db->query("
SELECT *
FROM
(
SELECT
trans.*,
IF(#sameClass = userId, #rn := #rn + 1,
IF(#sameClass := userId, #rn := 1, #rn := 1)
) AS rank
FROM trans
CROSS JOIN (SELECT #sameClass := 0, #rn := 1 ) AS var
WHERE userId IN (" . implode(', ', $cData) . ")
AND date >= {$date}
ORDER BY userId, id
) AS t
WHERE t.rank <= 10
ORDER BY t.userId, t.rank
");
I have 2 tables - users and articles.
users:
user_id (int)
name (varchar)
articles:
article_id (int)
user_id (int)
title (varchar)
description (text)
In my application I need to display 20 RANDOM articles on a page.
My query is like this:
SELECT a.title
, a.description
, u.name
FROM articles a
JOIN users u
USING (user_id)
ORDER
BY RAND()
LIMIT 20
A user can have any number of articles in the database.
Now the problem is sometimes out of 20 results, there are like 9-10 articles from one single user.
I want those 20 records on the page to not contain more than 3 (or say 4) articles from a particular user.
Can I achieve this through SQL query. I am using PHP and MySQL.
Thanks for your help.
You could try this?
SELECT * FROM
(
SELECT B.* FROM
(
SELECT A.*, ROW_NUMBER() OVER (PARTITION BY A.USER_ID ORDER BY A.R) USER_ROW_NUMBER
FROM
(
SELECT a.title, a.description, u.name, RND() r FROM articles a
INNER JOIN users u USING (user_id)
) A
) B
WHERE B.USER_ROW_NUMBER<=4
) C
ORDER BY RAND() LIMIT 20
Mmm, intresting I don't think this is possible through a pure sql query.
My best idea would be to have an array of the articles that you'll eventually display query the database and use the standard SELECT * FROM Articles ORDER BY RAND() LIMIT 20
The go through them, making sure that you have indeed got 20 articles and no one has breached the rules of 3/4 per user.
Have another array of users to exclude, perhaps using their user id as an index and value of a count.
As you go through add them to your final array, if you find any user that hits you rule add them to the array.
Keep running the random query, excluding users and articles until you hit your desired amount.
Let me try some code (it's been a while since I did php)
$finalArray = [];
$userArray = [];
while(count($finalArray) < 20) {
$query = "SELECT * FROM Articles ";
if(count($finalArray) > 0) {
$query = $query . " WHERE articleID NOT IN(".$finalArray.")";
$query = $query . " AND userID NOT IN (".$userArray.filter(>4).")";
}
$query = $query . " ORDER BY Rand()";
$result = mysql_query($query);
foreach($row = mysql_fetch_array($result)) {
if(in_array($finalArray,$row) == false) {
$finalArray[] = $row;
}
if(in_array($userArray,$row[userId]) == false) {
$userArray[$row[userId]] = 1;
}
else {
$userArray[$row[userId]] = $userArray[$row[userId]] + 1;
}
}
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 the following problem: here is the structure of my tables
and here is my code:
$SQL_NEXT_PROJECT_ID ="SELECT id FROM projects WHERE id < '".$id_project."' and project_types_id='".$project_type_id."' ORDER BY id DESC LIMIT 1";
$conn->query($SQL_NEXT_PROJECT_ID);
foreach ($conn->query($SQL_NEXT_PROJECT_ID) as $rowProjectNext) {
$next_project_id = $rowProjectNext['id'];
}
$SQL_PREVIOUS_PROJECT_ID ="SELECT id FROM projects WHERE id > '".$id_project."' and project_types_id='".$project_type_id."' ORDER BY id ASC LIMIT 1";
$conn->query($SQL_PREVIOUS_PROJECT_ID);
foreach ($conn->query($SQL_PREVIOUS_PROJECT_ID) as $rowProjectPrevious) {
$previous_project_id = $rowProjectPrevious['id'];
}
the thing is that i need to put the query with an aditional condition from the 'images' table i have to evaluate the main field ='1', i have been trying to do it with a inner join but it doesnt seem to work, here i attach the query that i have been trying
$SQL_NEXT_PROJECT_ID ="SELECT projects.id FROM projects INNER JOIN images WHERE projects.id > '".$id_project."' and project_types_id='1' and main='1' ORDER BY projects.id DESC LIMIT 1";
Basically what i need to do is to create next / previous links to projects of certain type (project_types_id) based on the current id of the project ($id_project) taking into consideration the fact that the project should have a image that has the main field equal to '1'
hope i explained myself well...
Well youre not joining on anything... you need an ON clause.
SELECT pr.id FROM projects pr
INNER JOIN images img ON (pr.id = img.projects_id AND img.main = 1)
WHERE pr.id > ?
AND pr.project_types_id = 1
ORDER BY pr.id DESC LIMIT 1
at the end i did it like this
$_nextPrevious = array();
$SQL_SEARCH_VALUES="SELECT images.projects_id AS projectId FROM projects INNER JOIN images ON ( projects.id = images.projects_id ) AND project_types_id = '".$project_type_id."' AND images.main = '1' LIMIT 0 , 30";
$conn->query($SQL_SEARCH_VALUES);
foreach($conn->query($SQL_SEARCH_VALUES) as $rowNextPrevious) {
$value = $rowNextPrevious['projectId'];
array_push($_nextPrevious, $value);
}
$currentValue = array_search($id_project, $_nextPrevious);
$next = $currentValue + 1;
$previous = $currentValue - 1;
$next_project_id = $_nextPrevious[$next];
$previous_project_id = $_nextPrevious[$previous];
return array($project_name,$project_description,$project_path, $_images, $next_project_id, $previous_project_id);
using an array instead of two searches...
I recently created a scoring system where the users are ordered by their points on descending basis. First I used to store ranks in a column of its own. I used to run this loop to update the rank:
$i = 1;
$numberOfRows = mysql_query('SELECT COUNT(`id`) FROM sector0_players');
$scoreboardquery = mysql_query("SELECT * FROM sector0_players ORDER BY points DESC");
while(($row = mysql_fetch_assoc($scoreboardquery)) || $i<=$numberOfRows){
$scoreid = $row['id'];
$mysql_qeury = mysql_query("UPDATE sector0_players SET scoreboard_rank = '$i' WHERE id = '$scoreid'");
$i++;
}
And it was really hard, not to mention slow to actually run this on a huge amount of users.
Instead, I tried to construct a query and ended up with this.
SET #rownum := 0;
SELECT scoreboard_rank, id, points
FROM (
SELECT #rownum := #rownum + 1 AS scoreboard_rank, id, points FROM sector0_players ORDER BY points DESC
)
as result WHERE id = '1';
But, this is just a select statement. Is there anyway I could get around it and change it so that it updates the table just as the loop does?
Please try using the following query :
set #rownum:=0;
update sector0_players set scoreboard_rank=#rownum:=#rownum+1 ORDER BY points DESC;
PHP code can be ,
mysql_query("set #rownum:=0;");
mysql_query("update sector0_players set scoreboard_rank=#rownum:=#rownum+1 ORDER BY points DESC;");
You can try using the RANK function .. I haven't actually executed the SQL, but it should work
UPDATE sector0_players
SET scoreboard_rank =
(
SELECT srank
FROM
(
SELECT id,points, RANK() OVER (ORDER BY points) AS srank
FROM sector0_players T
) D
WHERE D.id = sector0_players.id
AND D.points = sector0_players.points
)