How to simplify this 2 MySQL Queries - mysql

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)

Related

how to optimize mysql query in phalcon

i used this query:
$brands = TblBrand::find(array("id In (Select p.brand_id From EShop\\Models\\TblProduct as p Where p.id In (Select cp.product_id From EShop\\Models\\TblProductCategory as cp Where cp.group_id_1='$id'))", "order" => "title_fa asc"));
if($brands != null and count($brands) > 0)
{
foreach($brands as $brand)
{
$brandInProductCategory[$id][] = array
(
"id" => $brand->getId(),
"title_fa" => $brand->getTitleFa(),
"title_en" => $brand->getTitleEn()
);
}
}
TblBrand => 110 records
TblProduct => 2000 records
TblProductCategory => 2500 records
when i used this code, my site donot show and loading page very long time ...
but when i remove this code, my site show.
how to solve this problem?
The issue is your query. You are using the IN statement in a nested format, and that is always going to be slower than anything else. MySQL will need to first evaluate what is in the IN statement, return that and then do it all over again for the next level of records.
Try simplifying your query. Something like this:
SELECT *
FROM Brands
INNER JOIN Products ON Brand.id = Products.brand_id
INNER JOIN ProductCategory ON ProductCategory.product_id = Products.id
WHERE ProductCategory.group_id_1 = $id
To achieve the above, you can either use the Query Builder and get the results that way
https://docs.phalconphp.com/en/latest/api/Phalcon_Mvc_Model_Query_Builder.html
or if you have set up relationships in your models between brands, products and product categories, you can use that.
https://docs.phalconphp.com/en/latest/reference/model-relationships.html
example:
$Brands = Brands::query()
->innerJoin("Products", "Products.brand_id = Brand.id")
->innerJoin("ProductCategory", "ProductCategory.product_id = Products.id")
->where("ProductCategory.group_id_1 = :group_id:")
->bind(["group_id" => $id])
->cache(["key" => __METHOD__.$id] // if defined modelCache as DI service
->execute();
$brandInProductCategory[$id] = [];
foreach($Brands AS $Brand) {
array_push($brandInProductCategory[$id], [
"id" => $Brand->getId(),
"title_fa" => $Brand->getTitleFa(),
"title_en" => $Brand->getTitleEn()
]);
}

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 ..

Optimizing MySQL operation to get counts

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

Classic structure - self connected DB table, list

I have the following:
ID NAME PAREN_ID
1 a null
2 b null
3 c 2
4 d 3
I want to list the ID 4 item, and its all parent, so I would like to get:
4 d 3
3 c 2
2 b null
I tried something:
SELECT * FROM categories c1
JOIN categories c2 ON c2.ID = c1.PARENT_ID;
but thats not the good result, even if I try to filter to ID 4, it returns nothing.
Its MySQL!
I'm not really sure how you can do it with MySQL. I think your best bet would be fetching everything recursively.
function getCategories( $id ) {
$ret = array();
$cat = mysql_query( 'SELECT * FROM categories' );
$curid = $id;
do {
$ret[] = $cat[ $curid ];
$curid = $cat[ $curid ][ 'PARENT_ID' ];
} while ( $curid );
return $ret;
}
You should alter this so that it uses the PDO.
Only problem would be the query returning much data.

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);
}