CodeIgniter count within query as extra column - php

I am having a query in CodeIgniter which is working fine.
Now I would like to have an additional COUNT inside per output line, this data is shown inside a table.
I want to see per line how much times the user has requested a device.
I don't want the output to be grouped, I want one column per row that says this user has 3 requests, so in this case 3 rows from the same user, having a different deviceRequest, with 3 times in this column count being 3.
PS: I am quite new to this, info about your solution would be appreciated.
Query:
$datatables->select('
deviceRequestID,
deviceRequestStatuses.deviceRequestStatusShortname,
users.userEmail,
users.userID,
users.userName,
users.userSurname,
deviceRequestComments,
deviceRequests.createdAt,
deviceRequests.updatedAt,
rooms.roomName,
hotspots.hotspotName,
hotspots.hotspotAddress,
hotspots.hotspotCity,
hotspots.hotspotZIP,
DATEDIFF(CURDATE(), deviceRequests.createdAt) AS daysDifference,
')
->join('users', 'deviceRequests.userID=users.userID', 'left')
->join('deviceRequestStatuses', 'deviceRequests.deviceRequestStatusID=deviceRequestStatuses.deviceRequestStatusID')
->join('rooms', 'users.roomID=rooms.roomID', 'left')
->join('hotspots', 'rooms.hotspotID=hotspots.hotspotID', 'left')
->from('deviceRequests');

I added this extra query line inside the select part of the query above, it does exactly what I need.
(SELECT count(userID) FROM deviceRequests WHERE userID = users.userID) AS deviceCount

Related

intersection and pagination in Eloquent ORM

Hi everybody I have 3 table:
a table called content with the below attributes:
id
name
table_type_id
release_date
popularity
another table called content_genres with the below attributes:
content_id
genres_id
another table called genres with the below attributes:
id
name
each content can have multiple genres, and a genre can have multiple content.(many to many)
okay, until here is the definition of the different tables, now i am trying to make a query to search the content that has for example the genre_id=1 and at the same time the genre_id=2
in postgresql this would be easy:
SELECT content.id
FROM content INNER JOIN content_genres ON content.id =content_genres.content_id
WHERE content_genres.`genres_id`= 1
INTERSECT
SELECT content.id
FROM content INNER JOIN content_genres ON content.id =content_genres.content_id
WHERE content_genres.`genres_id`= 2
;
I make one query, I make another query and then I make an intersection getting that content that has the genre_id 1 and 2
but when I try to write this same query in eloquent I have some problems:
query 1:
$content1=$this->content::join('content_genres','content_genres.content_id','=','content.id')
->with('genres')
->where('content_genres.genres_id',1)
->where('content.table_type_id',1)
//->whereYear('release_date',2017)
->select('content.id','content.name','content.popularity')
->orderBy('popularity','desc')->get();
query 2:
$content2=$this->content::join('content_genres','content_genres.content_id','=','content.id')
->with('genres')
->where('content_genres.genres_id',2)
->where('content.table_type_id',1)
//->whereYear('release_date',2017)
->select('content.id','content.name','content.popularity')
->orderBy('popularity','desc')->get();
intersection:
$final_result=$content1->intersect($content2);
okay how we have seen at this way we are able to make a intersection but I have some problems:
when I want to do a manual pagination I don't know how can I count the elements that is going to have the intersection, and after that limit the results of the intersection.
example:
number of results from query1:
18950
number of results from query2:
22650
number of results from intersection
3457
this is very slow, because I can not say limit the query 1 to 100 results, limit the query 2 to 100 results and then make the intersection, I can not do this because the number of results from the intersection is not going to be always the same so for that reason how can I make a manual pagination over the intersection without load all the results from query1 and query2, saying I want to paginate the intersections in pages from 20 results?
The last thing is the big problem which I have had all the week.
real example
you go to this page, then in year put none, and in genres select two random genres. how you can see the pagination of that intersection is always 20, doesn't depends if there is more results in the intersection or there isn't, always is 20. And I am pretty sure that they haven't load from the db all the results.
Good result:
Thanks to the answer the correct way to do this is the below:
$this->content::join('content_genres as g1','g1.content_id','=','content.id')
->join('content_genres as g2','g2.content_id','=','content.id')
->where('g1.genres_id', 1)
->where('g2.genres_id', 2)
it works for me, I could have chosen the other option but I have a many to many relation , because my content_genres is a pivot table, but I think that I would be also valid.
You should merge both queries. I see two ways of doing this.
1) Join content_genres twice:
$this->content::join('content_genres as g1','g1.content_id','=','content.id')
->join('content_genres as g2','g2.content_id','=','content.id')
->where('g1.genres_id', 1)
->where('g2.genres_id', 2)
2) Use whereHas():
$this->content::whereHas('content_genres', function($query) {
$query->where('genres_id', 1)
})->whereHas('content_genres', function($query) {
$query->where('genres_id', 2)
})
This requires a relationship: content → HasMany → content_genres

MYSQL count rows instead of showing results

So I have the following query, which I use it to get some analytics stats.
SELECT count(*) as total,CONCAT(YEAR(created),'-',MONTH(created),'-',DAY(created))
as date_only FROM logs where action = 'banner view'
and created BETWEEN '2015-07-03 21:03'
AND '2017-08-02 21:03' group by date_only order by created asc
This works, and it gives me this:
So what I actually need is, the total count of the rows in this case is 20, this is a dummy example, but I need to use this count to check before showing the stats if the data is too big to be displayed on a graphic.
Can this be achieved?
//LE
So the process will be like this:
1. Get a count of the total rows, if the count of rows is smaller than X(number will be in config and it will be a basic if statement), then go ahread and run the above query.
More info:
I actually use this query to display the stats, I just need to adapt it in order to show the total count rows
So the result of thquery should be
total | 20 in this case
I think you would want to use a derived table. Just wrap your original query in parenthesis after the FROM and then give the derived table an alias (in this case tmp). Like so:
SELECT count(*) FROM (
SELECT count(*) as total,CONCAT(YEAR(created),'-',MONTH(created),'-',DAY(created))
as date_only FROM logs where action = 'banner view'
and created BETWEEN '2015-07-03 21:03'
AND '2017-08-02 21:03' group by date_only order by created asc
) as tmp;
If I understand what you want to do correctly, this should work. It should return the actual number of results from your original query.
What's happening is that the results of the parenthesized query are getting used as a sort of virtual table to query against. The parenthesized query returns 20 rows, so the "virtual" table has 20 rows. The outer count(*) just counts how many rows there are in that virtual table.
Based on the PHP tag, I assume you are using PHP to send the queries to MySQL. If so, you can use mysqli_num_rows to get the answer.
If your query result is in $result then:
$total = mysqli_num_rows($result);
Slightly different syntax for Object Oriented style instead of procedural style.
The best part is you don't need an extra query. You perform the original query and get mysqli_num_rows as an extra without running another query. So you can figure out pagination or font size or whatever and then display without doing the query again.
This is an small query but works fine, and give me the total number of rows, you just need add your conditions.
SELECT COUNT(*) FROM table WHERE field LIKE '%condition%'
The group by I think you need to eliminated, becouse, this instead of count the records, divide in all your group by, example: records = 4, with group by you have
1
1
1
1
I hope this help you
You can try this way .
SELECT COUNT(*) FROM ( SELECT count(*) as total,CONCAT(YEAR(created),'-',MONTH(created),'-',DAY(created))
as date_only FROM logs where action = 'banner view'
and created BETWEEN '2015-07-03 21:03'
AND '2017-08-02 21:03' group by date_only HAVING total >=20 ) temp

Apply distinct on join query with CodeIgniter Query Builder

I am trying to make a query with codeigniters Query Builder
$this->db->select('*')
->from('users')
->join('user_to_group', 'users.id=user_to_group.user_id')
->where('user_to_group.group_id !=', $group->id);
Here in above code I'm trying to fetch records of users which are not in provided group. This query is working fine at the stage but sometimes it returns same record multiple times as a user can be part of multiple groups. So to overcome this problem I want to apply distinct to this query.
But I don't find the correct way to do it.
Please help..
You need to add group_by in query.
Write your query as below:-
$this->db->select('*')
->from('users')
->join('user_to_group', 'users.id=user_to_group.user_id')
->where('user_to_group.group_id !=', $group->id)
->group_by('users.id'); // add this line
Note : this query will work in only case if you use "user_to_group" table as multiple relation table mean user and group both tables id you used in this third table name "user_to_group".
Use group by if you need unique record on base of group_id
Try this :
$this->db->select('*')
->from('users')
->join('user_to_group', 'users.id=user_to_group.user_id')
->where('user_to_group.group_id !=', $group->id)
->group_by("user_to_group.group_id");
Because you will get multiple record when user is part of multiple group so you will use this to get groups unique records user vise or you will apply both group_id and user_id in group_by to get it unique from both field.

PHP, MySQL selecting posts and comments

I'm having problems with my script which need to select all my posts and related comments.
Right now I've following query:
$sql = "SELECT posts.post_title, posts.post_modified, post_content,update_modified, update_content
FROM posts
LEFT JOIN updates
ON posts.post_ID = updates.update_post_ID";
The query works great besides if the post has multiple comments it gives me multiple entries.
I've searched around but unfortunately I wasn't able to re-script my query for my needs.
I really hope someone can help me out?
I think you want the DISTINCT keyword, used as SELECT DISTINCT ... to avoid duplicates. However if I understand correctly your comments are in the updates table and you're pulling update_modified and update_content into your recordset. So assuming those are (potentially) unique values then DISTINCT will not collapse them down. It might be best to only pull updates.update_post_ID with DISTINCT, then pull whatever you need from updates based on the IDs you retrieve when you need it.
If you want to return only 1 row per post, with all the comments with the post, the easiest way is using GROUP_CONCAT(). This returns a csv of all the column data. Assuming that update_content is the post comments, try something like -
SELECT posts.post_title, posts.post_modified, post_content, GROUP_CONCAT(update_modified), GROUP_CONCAT(update_content)
FROM posts
LEFT JOIN updates
ON posts.post_ID = updates.update_post_ID
GROUP BY updates.update_post_ID
note - GROUP_CONCAT() has a group_concat_max_len default of 1024. If your comments become too long you will want to increase this before running the GROUP_CONCAT() query or the comments will be truncated -
SET [GLOBAL | SESSION] group_concat_max_len = 10240; // must be in multiples of 1024
SELECT id, name
GROUP_CONCAT(comment) AS comment
FROM table
GROUP BY name;
you will also need to be aware of max_allowed_packet as this is the limit you can set var_group_concat_max_len to.

Mysql Unique Query

I have a programme listing database with all the information needed for one programme packed into one table (I should have split programmes and episodes into their own) Now since there are multiple episodes for any given show I wish to display the main page with just the title names in ascending and chosen letter. Now I know how to do the basic query but this is all i know
SELECT DISTINCT title FROM programme_table WHERE title LIKE '$letter%'
I know that works i use it. But I am using a dynamic image loading that requires a series number to return that image full so how do I get the title to be distinct but also load the series number from that title?
I hope I have been clear.
Thanks for any help
Paul
You can substitute the DISTINCT keyword for a GROUP BY clause.
SELECT
title
, series_number
FROM
programme_table
WHERE title LIKE '$letter%'
GROUP BY
title
, series_number
There are currently two other valid options:
The option suggested by Mohammad is to use a HAVING clause in stead of the WHERE clause this is actually less optimal:
The WHERE clause is used to restrict records, and is also used by the query optimizer to determine which indexes and tables to use. HAVING is a "filter" on the final result set, and is applied after ORDER BY and GROUP BY, so MySQL cannot use it to optimize the query.
So HAVING is a lot less optimal and you should only use it when you cannot use 'WHERE' to get your results.
quosoo points out that the DISTINCT keyword is valid for all listed columns in the query. This is true, but generally people do not recommend it (there is no performance difference *In some specific cases there is a performance difference***)**. The MySQL optimizer however spits out the same query for both so there is no actual performance difference.
Update
Although MySQL does apply the same optimization to both queries, there is actually a difference: when DISTINCT is used in combination with a LIMIT clause, MySQL stops as soon as it finds enough unique rows. so
SELECT DISTINCT
title
, series_number
FROM
programme_table
WHERE
title LIKE '$letter%'
is actually the best option.
select title,series_number from programme_table group by title,series_number having title like '$letter%';
DISTINCT keyword works actually for a list of colums so if you just add the series to your query it should return a set of unique title, series combinations:
SELECT DISTINCT title, series FROM programme_table WHERE title LIKE '$letter%'
Hey thanks for that but i have about 1000 entries with the same series so it would single out the series as well rendering about 999 programmes useless and donot show.
I however found out away to make it unique and show the series number
SELECT * FROM four a INNER JOIN (SELECT title, MIN(series) AS MinPid FROM four WHERE title LIKE '$letter%' GROUP BY title) b ON a.title = b.title AND a.series = b.MinPid
Hopefully it helps anyone in the future and thank you for the replies :)

Categories