Improve SQL queries speed - mysql

I'm using MySQL and i have schema like:
|------------|-------------|------------|--------------|
| cities |category_city| categories| companies |
|------------|-------------|------------|--------------|
| id | city_id | id | id |
| name | category_id | name |subcategory_id|
| | | parent_id | city_id |
| | | |...other cols |
|____________|_____________|____________|______________|
Relationships:
City with Category has ->belongsToMany()
public function categories()
{
return $this->belongsToMany(Category::class);
}
Categories has subcategories:
public function subcategories()
{
return $this->hasMany(Category::class, 'parent_id', 'id');
}
And i'm getting companies from category and filtering by city, because i need the current city companies and for that i have a global scope:
public function getCompanies($city_id)
{
return $this->companies()->whereHas('mainCity', function ($q) use ($city_id) {
$q->where('city_id', $city_id);
});
}
mainCity method:
public function mainCity()
{
return $this->belongsTo(City::class, 'city_id');
}
Here is my method performing the query with AJAX request:
public function getPlaces(City $city, Category $subcategory, $north, $south, $east, $west)
{
$companies = $subcategory->companies()
->withCount('comments')
->companiesByBounds($north, $south, $east, $west)
->paginate(8);
$markers = $subcategory->companies()
->companiesByBounds($north, $south, $east, $west)
->get(['lat', 'lng', 'slug', 'title']);
return response()->json(['companies' => $companies, 'markers' => $markers], 200, [], JSON_NUMERIC_CHECK);
}
and by companiesByBounds scope method:
public function scopeCompaniesByBounds($query, $north, $south, $east, $west)
{
return $query->whereBetween('lat', [$south, $north])
->whereBetween('lng', [$west, $east]);
}
In companies i have ~2m records. The main problem is that the queries taking 3.5 seconds. Help please to improve my queries.
Here is the query:
select count(*) as aggregate from `companies` where `companies`.`category_id` = '40' and `companies`.`category_id` is not null and `lat` between '53.68540097020851' and '53.749703253622705' and `lng` between '91.34262820463869' and '91.51600619536134'

To improve speed you need to add indexes on the columns lat and lng.
CREATE INDEX idx_lat ON companies (lat);
The indexes are used in queries when the columns are added to conditions.

Related

How to handle Many To Many relationship with three or more classes with Laravel and MySQL

I'm working on a project with Laravel and Eloquent/MySQL.
I'd like to know how to handle a many-to-many relationship with three tables (users, merchants, roles).
Any user can have one or more merchant.
Any merchant can be shared between users.
Any user have a specific role for a merchant.
Is there a best practice to follow?
How can I get all the merchants by a user having a specific role?
Thanks for helping
This is my current structure:
Users
-------------------------------
| id | first_name | last_name |
-------------------------------
Merchants
-------------------
| id | trade_name |
-------------------
Roles
-------------------------
| id | name | hierarchy |
-------------------------
merchant_user_role
-----------------------------------
| merchant_id | user_id | role_id |
-----------------------------------
Merchant Model
<?php
class Merchant extends Model
{
public function users()
{
return $this->belongsToMany('App\User', 'merchant_user_role')
->withPivot('role_id')
->withTimestamps()
->orderBy('first_name', 'asc');
}
}
User Model
<?php
class User extends Model
{
public function merchants()
{
return $this->belongsToMany('App\Merchant', 'merchant_user_role')
->withPivot('role_id')
->withTimestamps()
->orderBy('trade_name', 'asc');
}
public function roles()
{
return $this->belongsToMany('App\Role', 'merchant_user_role')
->withTimestamps();
}
}
Role Model
<?php
class Role extends Model
{
public function users()
{
return $this->belongsToMany('App\User')->withTimestamps();
}
}
$merchants = $user->merchants()->where('role_id', $some_role_id)->get();

How to show Json field value in laravel

I have a json field in my database name is permissions {"read": true, "create": true}. Now, I want to show this in my blade view. I had try this way but it's show an error.
#foreach($users as $user)
<tr>
<td>{{$user->roles->permissions}}</td>
</tr>
#endforeach
show this Error Message.
Property [permissions] does not exist on this collection instance.
User Model
public function roles()
{
return $this->belongsToMany(Role::class,'user_roles');
}
I tested this scenario and was OK . i hope usefull for you :
my route :
Route::get('/test', function (Request $request) {
$users=\App\User::with('roles')->get();
return view('welcome',compact('users'));
});
define roles method inside user model :
public function roles()
{
return $this->belongsToMany(Role::class,'user_roles');;
}
and get permissions in view :
#foreach($users as $user)
#foreach($user->roles as $role)
<tr>
<td>{{$role}}</td>
</tr>
#endforea
#endforeach
I test with these tables
user_roles
user_id | role_id
-----------------
1 | 2
1 | 2
roles:
id | permissions
-----------------
1 | {"read": true, "create": true}
2 | {"read": true, "create": false}
users:
id | name | email | password | remember_token | created_at|updated_at
----------------------------------------------------------------------
1 | alihossein|ali#gmail.com|XXXXX|UIWKK67ei5CCuiv1OXilKY2aRkTfSqGLpqJch0F9YmenGSorsQGHVvWiX6kP| 2018-05-28 22:25:14 | 2018-05-28 22:25:14

How to Fetch data from two tables in cakephp 3

I am new in cakephp and my table like:
city
id | name
1 | city1
2 | city2
state
id | name | cityid
1 |state1| 2
so how do i get the city name if i having state id.
In controller i have code like this.
public function getCity()
{
if( $this->request->is('ajax') ) {
$this->autoRender = false;
}
if ($this->request->isPost()) {
$sId= $this->request->data['stateid'];
}
}
In the $sId i get value so how do i write query.
If you have a BelongsTo relationship between both Model, you just have to do a query on the States which contains City:
public function getCity()
{
if( $this->request->is('ajax') ) {
$this->autoRender = false;
}
if ($this->request->isPost()) {
$stateEntity = $this->States->find('all')
->where(['id' => $this->request->data['stateid']])
->contain(['Cities'])
->first();
// Now the State Object contains City
$cityName = $stateEntity->city->name;
}
}
To create this relationship you need to do like this:
class StatesTable extends Table
{
public function initialize(array $config)
{
$this->belongsTo('Cities')
->setForeignKey('city_id')
->setJoinType('INNER');
}
}

Laravel 4.2 Multiple Query in date range

I'm trying to get data for each date in range from 3 tables: Main table which is connected with 2 other tables using hasMany method.
Data stored as in example:
Main Table:
id | article title | url | created_at | updated_at |
----------------------------------------------------------------
14 | Some Title | www.example.com | TIMESTAMP | TIMESTAMP |
Views table (there is written count of views for each hour):
id | article_id | views | created_at | updated_at |
---------------------------------------------------
1 | 14 | 317 | TIMESTAMP | TIMESTAMP | (01:00:00)
2 | 14 | 186 | TIMESTAMP | TIMESTAMP | (02:00:00)
Clicks Table (there is written every click on this article):
id | article_id | ip_adress | created_at | updated_at |
---------------------------------------------------------
1 | 14 | 192.168.1.1 | TIMESTAMP | TIMESTAMP |
For example:
I need to get Articles from 01-02-2016 to 01-03-2016.
For every article I need to sum views and clicks for each day.
So in result i need to get something like this:
ID: 14, Title: Some Title, Views: 503, Clicks: 27
First, I wrote this code, but it makes lots of requests to database:
$dates = new DatePeriod($start, new DateInterval('P1D'), $stop);
foreach ($dates as $i => $date) {
$articles = Articles::with(['views' => function ($query) use ($date) {
$query->where('created_at', $date);
}, 'clicks' => function ($query) use ($date) {
$query->where('created_at', $date);
}])->get();
foreach ($articles as $article) {
foreach ($views->countOfViews as $i) {
// Code
}
foreach ($clicks->countOfClicks as $i) {
// Code
}
// Code
}
}
Then I found solution, to get exact same result as I'm getting in first example, but making only three requests:
$dates = new DatePeriod($start, new DateInterval('P1D'), $stop);
$articles = Articles::with(['views' => function ($query) use ($start, $stop) {
$query->whereBetween('created_at', array($start, $stop));
}, 'clicks' => function ($query) use ($start, $stop) {
$query->whereBetween('created_at', array($start, $stop));
}])->get();
foreach ($dates as $date) {
foreach ($articles as $article) {
foreach ($views->countOfViews as $i) if ($date->format('Y-m-d') === $i->created_at->toDateString()) {
// Code
}
foreach ($clicks->countOfClicks as $i) if ($date->format('Y-m-d') === $i->created_at->toDateString()) {
// Code
}
// Code
}
}
It solves problem with too many queries, but it takes to much time. Is it possible to do same thing faster?
Final Code:
$dates = new DatePeriod($start, new DateInterval('P1D'), $stop);
$articles = Articles::with(['views' => function ($query) use ($start, $stop) {
$query->whereBetween('created_at', array($start, $stop))
->groupBy('campaign_id', DB::raw('DATE(created_at)'))
->selectRaw('*, sum(views) as views');
}, 'clicks' => function ($query) use ($start, $stop) {
$query->whereBetween('created_at', array($start, $stop))
->groupBy('campaign_id', DB::raw('DATE(created_at)'))
->selectRaw('*, sum(clicks) as clicks');
}])->get();
foreach ($dates as $date) {
foreach ($articles as $article) {
foreach ($views->countOfViews as $i) if ($date->format('Y-m-d') === $i->created_at->toDateString()) {
// Code
}
foreach ($clicks->countOfClicks as $i) if ($date->format('Y-m-d') === $i->created_at->toDateString()) {
// Code
}
// Code
}
}

Can I build a Model for two Tables on PhalconPHP?

I have one games table and two tables to store their scores, game_scores and thirdparty_game_scores with exactly the same structure, something like that:
-------------------------GAMES-------------------------
slug | title | technology | (other non-relevant fields)
-------------------------------------------------------
----------------------GAME SCORES----------------------
id (PK) | game_slug | user | team | score | timestamp
-------------------------------------------------------
----------------THIRDPARTY GAME SCORES-----------------
id (PK) | game_slug | user | team | score | timestamp
-------------------------------------------------------
And their Models like:
class GameScores extends BaseGamesModel {
public function initialize() {
parent::initialize();
$this->belongsTo( 'game_slug', 'Games', 'slug' );
$this->skipAttributes(array('creation_time', 'last_modified'));
}
}
I'm building a weekly ranking with game scores stored only on game_scores:
$ranking = GameScores::sum(
array(
"column" => "score",
"conditions" => $conditions,
"group" => "team",
"order" => "sumatory DESC"
)
);
Is there any way to create a Model that concatenates all data from game_scores and thirdparty_game_scores to create the same ranking without any extra effort?
Thank you so much!
You can set the source table dynamically in your model. See Can I build a Model for two Tables on PhalconPHP?
You should use the setSource() method and put your condition in there, e.g.:
$source = 'default_table';
if (class_name($this) === 'A') {
$source = 'A_table';
}
return $source;