Optimizing MySQL operation to get counts - mysql

I have this lovely piece of code that copies out different counts of rows so I can build an associative list of how many of each good a citizen possesses.
$citizen_id = 1;
$goods = array();
$goods_result = mysql_query("SELECT id,name FROM goods");
while(($goods_row = mysql_fetch_assoc($goods_result))) {
$good_id = $goods_row['id'];
$query = "SELECT count(*) FROM possessions where citizen_id=$citizen_id and good_id=$good_id";
$possessed_result = mysql_query($query);
$possessed_row = mysql_fetch_row($possessed_result);
if($possessed_row) {
$possessed = $possessed_row[0];
$goods[$goods_row['name']] = $possessed;
}
}
echo json_encode ($goods);
It works, but it runs too slow. It seems to me that there must be a way to get MySQL to build a table of counts and return it to me in a single query, but I have no idea how to figure out how to do that. Any ideas on how to make this operation faster?

How about using a query like
SELECT g.id good_id,
count(*) cnt_of_goods
FROM possessions p INNER JOIN
goods g ON p.good_id = g.id
where citizen_id=$citizen_id
GROUP BY g.id

Related

How to simplify this 2 MySQL Queries

This must be an easy one, but I can't remember how I did it before.
I'm trying to make another query within the query where record IDs are match:
/*
tables:
models:
model_id
model_name
models_years:
model_year_id
model_id
year
*/
$models = DBW::run('SELECT * FROM models', [], true);
$models_years = DBW::run('SELECT * FROM models_years', [], true);
$output = [];
foreach ($models as $model)
{
$years = [];
foreach ($models_years as $model_year)
{
if ($model_year['model_id'] == $model['model_id'])
{
$years[] = $model_year['year'];
}
}
$output[] = [
'model_name' => $model['model_name'],
'years' => $years
];
}
var_dump( $output );
I use PDO (settings: ATTR_DEFAULT_FETCH_MODE = FETCH_ASSOC), and "DBW::run" function returns $stmt->fetchAll().
this is just an example of what I'm trying to do or improve, I know it's possible to do all of that in a single SQL, I've done it before and can't remember! :(
SELECT m.model_name, y.`year`
FROM models AS m
LEFT JOIN models_years As y ON m.model_id = y.model_id
OR from your original question, the following might be what you need:
SELECT m.model_name, group_concat(y.`year`) AS `year`
FROM models AS m
LEFT JOIN models_years As y ON m.model_id = y.model_id
GROUP BY 1
Why?
1 - Join table to reduce SQL calls
2 - GROUP the result (BY 1 == BY m.model_name, just a lazy shorthand here)
3 - group_concat(...) will by default produce: year1,year2,year3,... and then you can use PHP explode(',', ...) to change to array if you need
Join the tables, and use GROUP_CONCAT to combine all the years for each model. Then use explode() to split that into an array.
$output = DBW::run('select m.model_name, GROUP_CONCAT(y.year) AS years
FROM models AS m
LEFT JOIN models_years AS y ON m.model_id = y.model_id
GROUP BY m.model_id', [], true);
foreach ($output as &$row) {
$row['years'] = explode(',', $row['years']);
}
SELECT model_name, year FROM models, models_years WHERE
models.model_id=models_years.model_id
Or, if you prefer the JOIN syntax,
SELECT model_name, year FROM models JOIN models_years ON
models.model_id=models_years.model_id
another way to do this is with the sub select.
select * from models where model_id in (select model_id from models_years)

SubQueries in Laravel 4.2

Can anyone teach me how can we use a query like this in Laravel 4.2
SELECT
t.Truck_Number,
t.Created_dt,
t.Latitude,
t.Longitude,
t.Speed,
t.Heading
FROM
truckgps t
INNER JOIN
(
SELECT
Truck_Number,
max(Created_dt) as MaxDate
FROM
truckgps
GROUP BY
Truck_Number
) tm
ON
t.Truck_Number = tm.Truck_Number AND
t.Created_dt = tm.MaxDate
this query is provided by someone else, can it be made better using laravel ?
Using Laravel Query builder - you can make like this :
$results = DB::select("SELECT
t.Truck_Number,
t.Created_dt,
t.Latitude,
t.Longitude,
t.Speed,
t.Heading FROM truckgps t
INNER JOIN
( SELECT Truck_Number, max(Created_dt) as MaxDate from truckgps group by Truck_Number )
tm on t.Truck_Number = tm.Truck_Number and t.Created_dt = tm.MaxDate");
or if you have parameters, than you can binding like this :
DB::select("select count(1) count from table t where t.id= ? and t.roles_id = ?",
array($id, $role_id)
);
If you have two tables...I have one solution for your question.. you dont need use joins for this. Table1 name 'truckgps' and table 2 name 'truckgpss' My Ans
$truckgps = DB::table('truckgps')->select('Truck_Number','Created','Latitude','Longitude','Speed','Heading ')->get();
foreach ($truckgps as $t)
{
$result = DB::table('truckgpss')->select('MAX(Created_dt) AS MaxDate ','Truck_Number')->where('MaxDate','=',$t->Created_dt )->where('Truck_Number','=',$t->Truck_Number)->groupBy('Truck_Number')
->get();
// if u need value convert array to string use another foreach loop here
foreach ($result as $value)
{
echo $value->Truck_Number; // now u can see the result truck no..
}
}
I think its help to u...Thank you ..

How to refer to one table when two tables have been used in a MySQL query?

Up until now I have just been using one table in a MySQL query, but now I am using two tables in a query to return only users' favourites, i.e.:
$query1="SELECT tracks.id, favourites.track FROM tracks, favourites WHERE tracks.id = favourites.track";
$result=mysql_query($query1) or die(mysql_error());
I am wondering how then I would refer to columns in rows in just the tracks table to construct variables from this query. Previously I used:
while ($row = mysql_fetch_array($result)) {
$title = $row['title'];
...etc
}
But obviously this needs adapting to refer to just the rows in the tracks table, but how do I do this?
You can do the same using:
$query1="SELECT T.id as Tid, F.track , T.title as Title FROM tracks T inner join favourites F ON T.id = F.track";
$result = mysql_query($query1);
while ($row = mysql_fetch_object($result))
{
$title = $row->Title;
$tid = $row->Tid;
/*...etc...*/
}

CodeIgniter - MySQL join when there are no rows in secound table

I have this query in model:
public function Games() {
$q = $this->db->select('games.id, games.title, games.slug, games.dev_id, games.dev, games.plat_id, games.plat');
$q = $this->db->from('games');
$q = $this->db->join('rates', 'games.id = rates.game_id');
$q = $this->db->select_avg('rates.rate');
$q = $this->db->get();
return $q->result();
}
My goal is listing everything from games, additionally getting average rate from rates when available. Now it only shows those rows which are in both tables. How can I solve this?
Use this instruction
$this->db->select('games.id, games.title, games.slug, games.dev_id, games.dev, games.plat_id, games.plat');
$this->db->select_avg('rates.rate');
$this->db->from('games');
$this->db->join('rates', 'games.id = rates.game_id','left');
$this->db->group_by('rates.game_id');
$q = $this->db->get();
Left join will bring multiple results. using avg and group by will restrict to fetch only one row against each record.

How to combine sql select queries for this situation?

i'm writing a chat app with php/mysql
i have 3 tables: user, room and room_participant with these structures:
user: id, username
room: id, title
room_participant: room_id, user_id
Now i want to get list of all rooms along with list of all participants in each room.
Until now i just select all rooms from room table and iterate through all rooms and select users information out of each entry, which is very inefficient.
Is there any way to combine all these select into only 1 select query?
Not certain about this without testing, but give it a try:
SELECT
room.*,
user.*
FROM room
JOIN room_participant ON room_id = room_participant.id
JOIN user ON room_participant.user_id = user.id
ORDER BY room.id
To deduplicate rooms, use GROUP_CONCAT()
UPDATE GROUP_CONCAT() modified to return id|username
SELECT
room.id, room.name
GROUP_CONCAT(CONCAT(user.id,'|',user.username)) AS userlist
FROM room
JOIN room_participant ON room_id = room_participant.id
JOIN user ON room_participant.user_id = user.id
GROUP BY room.id, room.name
ORDER BY room.id
With the userlist generated by GROUP_CONCAT as id|name,id|name,id|name you can use PHP explode() to separate them.
// Split the list on the commas
$users = explode(",", $userlist);
$final_users = array();
// Then split each on the `|`
foreach ($users as $user) {
$split_user = explode("|", $user);
// Append each as a new associative array to $final_users
$final_users[] = array('id' => $split_user[0], 'username' => $split_user[1]);
}
// Now you have an array of users as id, username
var_dump($final_users);
You may wish to do two queries, and then match things up in whatever is using MySQL, e.g. PHP.
These two:
SELECT id, title
FROM room;
SELECT rp.room_id, rp.user_id, u.username
FROM room_participant AS rp
INNER JOIN user as u ON rp.user_id = u.id;
Or these two:
SELECT id, username
FROM user;
SELECT rp.room_id, rp.user_id, r.title
FROM room_participant AS rp
INNER JOIN room as r ON rp.user_id = r.id;
Which two queries make sense depends on what you're doing with the info really.
You could go a step further and select all three separately:
SELECT *
FROM room;
SELECT *
FROM user;
SELECT *
FROM room_participant;
Note: It's probably better to state the columns, rather than using '*', just in case in the future new columns are added to the table that you're not really interested in for these queries.
Obviously, you'd then have to match everything up in whatever is using MySQL, e.g. PHP. You could create a list of rooms and users from the selected info, and then match them up with something like:
// Use MySQL to populate $roomList from database, then do...
foreach ($roomList as $room)
{
$id = $room['id'];
$title = $room['title'];
$this->roomList[$id] = new Room($id, $title);
}
// Use MySQL to populate $userList from database, then do...
foreach ($userList as $user)
{
$id = $user['id'];
$username = $user['username'];
$this->userList[$id] = new User($id, $username);
}
// Use MySQL to populate $roomParticipantList from database, then do...
foreach ($roomParticipantList as $roomParticipant)
{
$room = $this->roomList[$roomParticipant['room_id']];
$user = $this->userList[$roomParticipant['user_id']];
// You could do one/both of these, depending on requirements.
$room->enterUser($user);
$user->joinRoom($room);
}