I want to group by user_id and get the last record.
id | user_id | code
---------------------------------------
1 20 12345678
2 22 45678877
3 20 78945642
4 90 45644564
5 20 00000000
Here is what I have so far
$this->db->select('id,user_id, code');
$this->db->from('test');
$this->db->group_by(user_id);
$this->db->where('user_id', 20);
$query = $this->db->get();
return $query->result();
The result to that query is as follows:
Array ( [0] => stdClass Object ( [id] => 1 [user_id] => 20 [code] => 12345678 ) )
Desired output
Array ( [0] => stdClass Object ( [id] => 5 [user_id] => 20 [code] => 00000000) )
I have tried order_by with no luck. Select MAX('id') gets me the id of 5 which is what I want, but the content of id 1.
Because max is an aggregate function, you cannot use it directly in a where clause. So you either need to run two separate queries and save the result of the max function, or just get the max from a sub-query.
Add this to your query:
$this->db->where('id = (select max(id) from test)', NULL, FALSE)
Incidentally, here's a valid query which satisfies the stated criteria...
SELECT x.*
FROM test x
JOIN
( SELECT MAX(id) id FROM test WHERE user_id = 20 ) y
ON y.id = x.id;
you can try :
$this->db->order_by('id', 'DESC');
$this->db->where('user_id', 20);
$query = $this->db->get('test');
return $query->result();
Related
I have a problem with showing 3 latest data from 2 tables with active record in codeigniter.
The tables
album: id_album, album_name
photo: id_photo, album_id, photo_name
Current data
Album:
Car
Bike
Airplane
Photo:
Bike 001
Bike 002
Airplane 001
Airplane 002
Airplane 003
Car 001
The condition is how to show the data by 3 latest album with 1 latest photo from each album. Maybe the result like this:
Car 001,
Airplane 003,
Bike 002
My active record in codeigniter:
$this->db->select('album.album_name, album.id_album, photo.id_photo, photo.photo_name);
$this->db->join('album', 'photo.album_id = album.id_album');
$this->db->limit(3);
$this->db->order_by('album.id_album', 'desc');
$this->db->order_by('photo.id_photo', 'desc');
$this->db->group_by('album.album_name');
return $this->db->get($this->table)->result();
If i use the query above, the data will be like this:
Car 001,
Airplane 001,
Bike 001
Any help will be so appreciate
To pick a latest record for each album you can use a self join
SELECT a.album_name, a.id_album, p.id_photo, p.photo_name
FROM albums AS a
JOIN photos AS p ON a.id_album= p.album_id
LEFT JOIN photos AS p1 ON p.album_id = p1.album_id
AND p.id_photo < p1.id_photo
WHERE p1.id_photo IS NULL
ORDER BY a.id_album DESC
LIMIT 3
or
SELECT a.album_name, a.id_album, p.id_photo, p.photo_name
FROM albums AS a
JOIN photos AS p ON a.id_album= p.album_id
JOIN (
SELECT album_id , MAX(id_photo) id_photo
FROM photos
GROUP BY album_id
) AS p1 ON p.album_id = p1.album_id
AND p.id_photo = p1.id_photo
ORDER BY a.id_album DESC
LIMIT 3
In query build you could write it as
$this->db->select('a.album_name, a.id_album, p.id_photo, p.photo_name');
$this->db->from('albums AS a');
$this->db->join('photos AS p', 'a.id_album= p.album_id');
$this->db->join('photos AS p1', 'p.album_id = p1.album_id AND p.id_photo < p1.id_photo', 'left');
$this->db->where('p1.id_photo IS NULL', null, false)
$this->db->limit(3);
$this->db->order_by('a.id_album', 'desc');
$query = $this->db->get();
Rather than dealing with joins, you may want to try a different approach:
// first, fetch the 3 latest albums:
$this->db->select('id_album', 'album_name');
$this->db->from('albums');
// ordering by descending ID would give you the lastest ones first
$this->db->order_by('id_album','desc');
$this->db->limit(3);
$query = $this->db->get();
$albums = $query->result();
// now, loop each album and fetch the latest photo
foreach ($albums as $a)
{
$this->db->select('id_photo', 'album_id', 'photo_name');
$this->db->from('photo');
$this->db->where('album_id', $a->id_album);
// ordering by descending ID should return the lastest photo first
$this->db->order_by('id_photo','desc');
$this->db->limit(1);
$query = $this->db->get();
$a->latest_photo = $query->row(0);
}
// return the album information for the 3 latest ones, along with the lastest photo for each
return $albums;
The return will be an array of objects that looks pretty much like this:
Array
(
[0] => stdClass Object
(
[id_album] => 0
[album_name] => name of album 0
[latest_photo] => stdClass Object
(
[id_photo] => latest_photo_0
[album_id] => 0
[photo_name] => Some_name_0
)
)
[1] => stdClass Object
(
[id_album] => 1
[album_name] => name of album 1
[latest_photo] => stdClass Object
(
[id_photo] => latest_photo_1
[album_id] => 1
[photo_name] => Some_name_1
)
)
[2] => stdClass Object
(
[id_album] => 2
[album_name] => name of album 2
[latest_photo] => stdClass Object
(
[id_photo] => latest_photo_2
[album_id] => 2
[photo_name] => Some_name_2
)
)
)
Yes, you'll be running more queries than you'd run with a single joined query, but the performance impact is negligible and you retain a lot more control on the final result, without unnecesarily complicate things from a SQL perspective
I have query like this,
SELECT * FROM users ORDER BY score
So, the result is like this.
Array
(
[0] => stdClass Object
(
[userid] => 3
[user] => John Doe
[score] => 50
)
[1] => stdClass Object
(
[userid] => 1
[user] => Mae Smith
[score] => 38
)
[2] => stdClass Object
(
[userid] => 2
[user] => Mark Sam
[score] => 26
)
)
But, I want to add a rank using find_in_set query. So the result might be like this. So that the user can view their ranks when they login to their account.
Array
(
[0] => stdClass Object
(
[userid] => 3
[user] => John Doe
[score] => 50
[rank] => 1
)
[1] => stdClass Object
(
[userid] => 1
[user] => Mae Smith
[score] => 38
[rank] => 2
)
[2] => stdClass Object
(
[userid] => 2
[user] => Mark Sam
[score] => 26
[rank] => 3
)
)
I tried this one.
$listOfUser = array();
foreach($users as $user) {
$listOfUser[] = $user->userid;
}
And used another query
$userid = 2 // => id of loggedin user
SELECT *, find_in_set($userid, $listOfUser) as rank FROM users where userid=$userid ORDER BY score
So, I got this result
Array
(
[1] => stdClass Object
(
[userid] => 2
[user] => Mark Sam
[score] => 26
[rank] => 3
)
)
Which is somehow correct. But, is there another way of querying that result using only one SQL query and without using foreach loop?
Something like this.
$userid = 2 // => id of loggedin user
SELECT *, find_in_set($userid, (SELECT * FROM users ORDER BY score)) as rank FROM users where userid=$userid ORDER BY score
But I got this error Subquery returns more than 1 row
If You don't insist on using find_in_set, you can get result with simple join. You ask for list of users (p) and for each user you ask, how many users have better score than him or her (c):
SELECT p.userid, COUNT(c.userid) AS rank
FROM users AS p
LEFT JOIN users AS c ON c.score > p.score
GROUP BY p.userid
This works even if you add other conditions, like WHERE p.userid = 123.
If more users have the same score, the ranks would look like 0,1,2,2,2,5,6.
In your query, you can add counter, like this:
set #n:=0;
SELECT #i := #i + 1 AS rank, * FROM users ORDER BY score
The rank here is relative to the score distribution across all users. I believe you should try something originally proposed in this answer:
SELECT users.*,
#rownum := #rownum + 1 as rank
FROM users
CROSS JOIN (select #rownum := 0) r
ORDER BY score DESC
What it does is basically order all users by score, and assign each of them an incremental value "rank". So the top scorer would have a rank of 1, the second scorer would have a rank of 2 etc.
Keep in mind that this solution is not "fair" - each user will have a different rank, even if all users have the same score. If you try to rank users as they do in sports (if two top competitors have the same score, they both take 1st place, and the next best competitor takes 3rd place, not second), you should think of a different solution.
I have two tables
Meetings:
m_id ProjectName
1 Test
2 Test2
Meeting_next:
id fk_m_id Meetingdate status
1 1 9-1-2018 0
1 1 10-1-2018 0
1 1 13-1-2018 1
I want to join this two tables when I left join it I will get duplicate value
Expected output
Array
(
[0] => Array
(
[m_id] => 1
[ProjectName] => test
[meetingDate] =>13-1-2018
)
[1] => Array
(
[m_id] => 2
[ProjectName] => test2
[meetingDate] =>
)
)
I tried -
select * from meetings left join meeting_next on meetings.m_id= meeting_next.fk_m_id where meeting_next.status=1 order by m_id desc
myOutput:
Array
(
[0] => Array
(
[m_id] => 1
[ProjectName] => test
[meetingDate] =>13-1-2018
) )
Bad luck I got only first Project name. I need second too. Please help me. Any help would be appreciated.
Your WHERE condition filters the number of rows to only the row of the first project.
If you want to show both projects, even if there are no meetings with status 1, you need to move the condition to the join condition:
select *
from meetings
left join meeting_next
on meetings.m_id= meeting_next.fk_m_id
and meeting_next.status=1
order by m_id desc
Now you will get all rows from meetings with only the matching entries from meeting_next.
I am trying to get the total dollar amount of all my inventory. The item row is setup like:
store_items
id stockNumber priceTotal
3 123 20.00
4 456 15.00
So, I am trying to run a query that will return one result that equals 35.00.
$results = $db->query("SELECT SUM(`priceTotal`) as `sum` FROM `store_items`");
print_r($results);
This is just giving me:
mysqli_result Object ( [current_field] => 0 [field_count] => 1 [lengths] => [num_rows] => 2 [type] => 0 )
How do I adjust the query to actually add the value of each row?
I believe this is the query you are looking for.
You need to group by specific columns.
$results = $db->query("SELECT SUM(priceTotal) as `sum` FROM `store_items` GROUP BY stockNumber");
I have the following query:
$count = (SELECT COUNT(*) FROM post GROUP BY ID
HAVING ID NOT IN (SELECT taxiID FROM taxi WHERE userID = '.$userID.' AND value IS NOT NULL)
ORDER BY postID), OBJECT);
Count contains this:
count = Array ( [0] => stdClass Object ( [COUNT(*)] => 1 ) [1] => stdClass Object ( [COUNT(*)] => 1 ) [2] => stdClass Object ( [COUNT(*)] => 1 ) [3] => stdClass Object ( [COUNT(*)] => 1 ) [4] => stdClass Object ( [COUNT(*)] => 1 ) [5] => stdClass Object ( [COUNT(*)] => 1 ) [6] => stdClass Object ( [COUNT(*)] => 1 ) [7] => stdClass Object ( [COUNT(*)] => 1 ) [8] => stdClass Object ( [COUNT(*)] => 1 ) [9] => stdClass Object ( [COUNT(*)] => 1 ) [10] => stdClass Object ( [COUNT(*)] => 1 ) [11] => stdClass Object ( [COUNT(*)] => 1 ) [12] => stdClass Object ( [COUNT(*)] => 1 ) [13] => stdClass Object ( [COUNT(*)] => 1 ) [14] => stdClass Object ( [COUNT(*)] => 1 ) [15] => stdClass Object ( [COUNT(*)] => 1 ) [16] => stdClass Object ( [COUNT(*)] => 1 ) [17] => stdClass Object ( [COUNT(*)] => 1 ) [18] => stdClass Object ( [COUNT(*)] => 1 ) [19] => stdClass Object ( [COUNT(*)] => 1 )
I need to count the number of results delivered by the above. Thing is, I have no idea how to use the result!
I had this code but now it won't work:
<?php if($count[0]->{'COUNT(*)'} > 10){ ?
echo "Load More";
}else {
echo "Nothing to load";
} ?>
$count should be more than 10 and my php should echo Load More but it is echoing Nothing to load.
The taxi table looks like this:
ID taxiID userID value
1 1 1 1
2 1 6 1
3 1 4 0
4 2 1 0
5 2 6 1
6 2 4 0
7 3 6 1
8 3 4 0
The post table looks like this:
ID postID randomNum
1 1 564
2 2 789
3 3 234
4 4 845
5 5 089
Assuming $userID is 1, the query returns postID 1,3,4,5 (1 is liked, 3 is not liked and not disliked by user 1, 4 and 5 are not liked and not disliked by any user). Therefore $count should contain 4 (4 results are found).
If my query is inefficient, how do I adapt it to be efficient?
Ultimately, the question is how do I do something like:
if ($count > 10) {}
Your problem is, your query isn't returning what you think it returns (it always helps to run you query standalone, to see if the result set is what you expect).
Right, let's break this down.
It is counting all posts that the user has not liked or disliked. likes and dislikes are stored in the taxi table. taxi.taxiID matches post.ID. Hence if the userID with any value that isn't null is found, ignore that post.ID. I am counting those post.ID which are not ignored
You're trying count all posts that don't have a matching record in the taxi table, for that userID. What you want here is to JOIN the tables and get those rows in post that would normally be excluded by the join. This is achieved by an left outer join
(edited)
SELECT p.ID, t.taxiID
FROM post p LEFT OUTER JOIN taxi t ON p.ID = t.taxiID AND t.userID = '.$user.'
HAVING t.taxiID IS NULL
That HAVING clause is saying: only those rows in the resultset that didn't have a corresponding t.taxiID.
If you run this query and get the expected set of rows (posts that do not have likes or dislikes by that user) THEN you can add an outer query to count the number of returned rows:
SELECT COUNT(*) as count FROM (
SELECT p.ID, t.taxiID
FROM post p LEFT OUTER JOIN taxi t ON p.ID = t.taxiID AND t.userID = '.$user.'
HAVING t.taxiID IS NULL
) a
This should return a single scalar named count. In this case you'll be able to say:
if ($count[0]->count > 10) { blah blah blah }
(2nd edit) This inner query will get you those posts that have either value = 1 in the taxi table, or no value at all, which results in 4 being returned for your example:
SELECT p.ID, t.taxiID, t.value
FROM post p LEFT OUTER JOIN taxi t ON p.ID = t.taxiID AND t.userID = '.$user.'
HAVING t.taxiID IS NULL OR t.value = 1
In case you want to know how many results would have been returned WITHOUT the LIMIT clause, according to the MySQL documentation:
A SELECT statement may include a LIMIT clause to restrict the number
of rows the server returns to the client. In some cases, it is
desirable to know how many rows the statement would have returned
without the LIMIT, but without running the statement again. To obtain
this row count, include a SQL_CALC_FOUND_ROWS option in the SELECT
statement, and then invoke FOUND_ROWS() afterward:
mysql> SELECT SQL_CALC_FOUND_ROWS * FROM tbl_name
-> WHERE id > 100 LIMIT 10;
mysql> SELECT FOUND_ROWS();
http://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_found-rows
SELECT COUNT(*) FROM post
WHERE postID NOT IN
( SELECT taxiID
FROM taxi
WHERE userID = '.$userID.'
AND value = 0
)
ORDER BY postID;
SELECT COUNT(*) FROM post
WHERE postID NOT IN
( SELECT taxiID
FROM taxi
WHERE userID = '.$userID.'
AND value = 0
)
Can you also provide us with the error message if this does not work?
Why not this?
SELECT COUNT(*) FROM post
WHERE postID NOT IN (
SELECT taxiID FROM taxi
WHERE userID = '.$userID.' AND value = 0
)
LIMIT 10
Note there is no need to perform an order by if you are only looking for the count. Also note you have a limit there so the result won't have more than 10 records. Not sure if that is the idea.
I found the answer and it was dead simple:
if (count($count) > 10) {}