How to retrieve 500k records DB data faster? - mysql

I have two tables T1 1 000 records and T2 with 500 000 records. I have a query where I run a join between them and fetch data by performing some aggregations. My page seems to be loading slow. Are there any approaches to make this query faster?
I have created indexes on columns for which aggregations are being performed. I think it is a generic statement.
$query = Mymodel::selectRaw("supplier_data.name as distributor,supplier_data.name as name, supplier_data.group_id as group_id, supplier_data.pay,supplier_data.group_id as submitted_group_plan,supplier_data.group_id as group_id_string,
(SELECT sum(t.net_claim) AS trans_number
FROM transactions_data_new as t
JOIN `supplier_data` AS d ON `t`.`member_id` = `d`.`group_id`
WHERE
(
(
t.`submit_date`>= '$date_from' and t.`submit_date`<= '$date_to'
AND t.`member_id` = supplier_data.group_id
)
OR
(
(t.claim_status IS NULL)
AND
(t.submit_date is NULL)
)
)
AND d.id = supplier_data.id
) as trans_number,
(SELECT sum(t.claim) AS trans_number
FROM transactions_data_new as t
JOIN `supplier_data` AS d ON `t`.`member_id` = `d`.`group_id`
WHERE
(
(
t.`submit_date`>= '$date_from' and t.`submit_date`<= '$date_to'
AND t.`member_id` = supplier_data.group_id
)
OR
(
(t.claim_status IS NULL)
AND
(t.submit_date is NULL)
)
)
AND d.id = supplier_data.id
) as claim,
(SELECT sum(t.reversed) AS trans_number
FROM transactions_data_new as t
JOIN `supplier_data` AS d ON `t`.`member_id` = `d`.`group_id`
WHERE
(
(
t.`submit_date`>= '$date_from' and t.`submit_date`<= '$date_to'
AND t.`member_id` = supplier_data.group_id
)
OR
(
(t.claim_status IS NULL)
AND
(t.submit_date is NULL)
)
)
AND d.id = supplier_data.id
) as reversed,
(SELECT sum(t.reversal) AS trans_number
FROM transactions_data_new as t
JOIN `supplier_data` AS d ON `t`.`member_id` = `d`.`group_id`
WHERE
(
(
t.`submit_date`>= '$date_from' and t.`submit_date`<= '$date_to'
AND t.`member_id` = supplier_data.group_id
)
OR
(
(t.claim_status IS NULL)
AND
(t.submit_date is NULL)
)
)
AND d.id = supplier_data.id
) as reversal
");

I don't see the need of this too complex/repeated with same clauses and multiple sub selects for same table which can done using a single left join
SELECT
s.name AS distributor,
s.name AS name,
s.group_id AS group_id,
s.pay,
s.group_id AS submitted_group_plan,
s.group_id AS group_id_string,
SUM(t.net_claim) AS trans_number,
SUM(t.claim) AS claim,
SUM(t.reversed) reversed,
SUM(t.reversal) reversal
FROM
supplier_data s
LEFT JOIN transactions_data_new t
ON `t`.`member_id` = s.`group_id`
AND (
(
t.`submit_date` >= '$date_from'
AND t.`submit_date` <= '$date_to'
)
OR (
t.claim_status IS NULL
AND t.submit_date IS NULL
)
)
GROUP BY s.name,
s.group_id,
s.pay

As I understand it the chunk() method is for use when you need to work with a large dataset and take an action on that data chunk by chunk.
From your question, it sounds like you're performing a query then returning the data as JSON so to me, it doesn't sound like you're taking an action on your dataset that requires chunking.
If you want to break up the returned JSON data you should be instead looking at pagination.
You could apply pagination to your query like so:
$data = Inspector::latest('id')
->select('id', 'firstname', 'status', 'state', 'phone')
->where('firstname', 'LIKE', '%' . $searchtext . '%')
->paginate();
You can specify the size of each set by passing a number to the paginate method:
$data = Inspector::latest('id')
->select('id', 'firstname', 'status', 'state', 'phone')
->where('firstname', 'LIKE', '%' . $searchtext . '%')
->paginate(25);
If I've misunderstood and you did actually want to do the chunking, I believe you could do the following:
$data = Inspector::latest('id')
->select('id', 'firstname', 'status', 'state', 'phone')
->where('firstname', 'LIKE', '%' . $searchtext . '%')
->chunk(50, function($inspectors) {
foreach ($inspectors as $inspector) {
// apply some action to the chunked results here
}
});
Also, if you're returning an eloquent object it will be automatically cast to json so you don't need to perform json_encode() as far as I'm aware.

Related

Trying to convert a query to eloquent

I have the following SQL:
SELECT arv.*
FROM article_reference_versions arv
INNER JOIN (SELECT `order`,
Max(`revision`) AS max_revision
FROM article_reference_versions
WHERE `file` = '12338-230180-1-CE.doc'
GROUP BY `file`,
`order`) AS b
ON arv.order = b.order
AND arv.revision = b.max_revision
WHERE arv.file = '12338-230180-1-CE.doc'
I need to convert this to Eloquent, so that I can properly access the data in object form. I tried doing it as such,
$s = Models\EloArticleReferenceVersion::select(
'SELECT arv.*
FROM article_reference_versions arv
INNER JOIN (
SELECT `order`, max(`revision`) as max_revision
FROM article_reference_versions
WHERE file = ? group by `file`, `order`) AS b
ON
arv.order = b.order AND arv.revision = b.max_revision
WHERE arv.file = ?',
[
'12338-230180-1-CE.doc',
'12338-230180-1-CE.doc'
])->get();
dd($s);
But I'm running into a plethora of issues, one after another. I figured it'd be easier to just convert this into an eloquent query, looking for some help with this.
DB Query to Query using Eloquent.
$query = EloArticleReferenceVersion::query()
->join(DB::raw('( SELECT `order`,Max(`revision`) AS max_revision FROM article_reference_versions WHERE `file` = '12338-230180-1-CE.doc' GROUP BY `file`, `order`) as sub_table'), function($join) {
$join->on('sub_table.order', '=', 'article_reference_versions.order');
$join->on('sub_table.max_revision ', '=', 'article_reference_versions.revision');
})
->where('article_reference_versions.file', '=', '12338-230180-1-CE.doc' )
->get();
Not Tested

Laravel: Joining multiple select statements

I have a survey app.
Each SurveyResponse has multiple QuestionResponses.
For reporting purposes I want to filter the QuestionResponses based on values answered for other QuestionResponses that are part of the same SurveyResponse - eg. Give me all the question responses of those that selected Male for the Genderquestion.
The following example SQL query is working, but I am struggling to replicate it in Laravel:
SELECT *
FROM ( SELECT *
FROM ( SELECT *
FROM question_responses ) AS A
JOIN ( SELECT survey_response_id AS JOIN_A
FROM question_responses
WHERE (question_short_name = 'Gender') AND (value = 'Male')) AS B
ON A.survey_response_id= JOIN_A) AS C
JOIN ( SELECT survey_response_id AS JOIN_B
FROM question_responses
WHERE (question_short_name = 'Age') AND (value = '45 to 54')) AS D
ON C.survey_response_id=JOIN_B
How would I do this using Eloquent or Laravel's query builder?
You can use DB::select method to retrieve result.
use Illuminate\Support\Facades\DB;
$results = DB::select("SELECT *
FROM ( SELECT *
FROM ( SELECT *
FROM question_responses ) AS A
JOIN ( SELECT survey_response_id AS JOIN_A
FROM question_responses
WHERE (question_short_name = ?) AND (value = ?)) AS B
ON A.survey_response_id= JOIN_A) AS C
JOIN ( SELECT survey_response_id AS JOIN_B
FROM question_responses
WHERE (question_short_name = ?) AND (value = ?)) AS D
ON C.survey_response_id= JOIN_B", ['Gender', 'Male', 'Age', '45 to 54']);
foreach ($results as $key => $row) {
print_r($row);
}
You can use the eloquent relationship, but if you want to use this particular type of query then you can use the DB class in laravel.
use Illuminate\Support\Facades\DB;
$data = DB::select(DB::raw('SELECT *
FROM ( SELECT *
FROM ( SELECT *
FROM question_responses ) AS A
JOIN ( SELECT survey_response_id AS JOIN_A
FROM question_responses
WHERE (question_short_name = 'Gender') AND (value = 'Male')) AS B
ON A.survey_response_id= JOIN_A.your_id) AS C
JOIN ( SELECT survey_response_id AS JOIN_B
FROM question_responses
WHERE (question_short_name = 'Age') AND (value = '45 to 54')) AS D
ON C.survey_response_id=JOIN_B.your_id'))
after getting the response you can retrieve the data by $data[0]['key'] for using '->' or making it collection you can use the Collection class.
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\Collection;
$tempData = Collection::make(DB::select(DB::raw('select query'));
($tempData[0])->key;

MySQL multiple column search return wrong result

I have below conditional query to perform multiple columns searches
SELECT
prod.pid,
prod.pname,
prod.description,
prod.status,
foto.set_cover,
foto.filename
FROM products prod
INNER JOIN second_child_categories secd ON prod.category_id = secd.second_id
INNER JOIN photos foto ON prod.pid = foto.pid
WHERE CONCAT( prod.pname, prod.description ) LIKE '%home%'
OR CONCAT( prod.description, prod.pname ) LIKE '%home%'
AND foto.set_cover = '1'
AND prod.status = '1'
ORDER BY prod.created_date DESC
Here's the output:
As refer to query, I set conditions:
foto.set_cover='1' AND prod.status='1'
the results are not matches with the condition.
How can I fix this query?
Thanks.
See Mysql or/and precedence?
ORhas a higher precedence, what you (implicitly) wrote:
CONCAT( prod.pname, prod.description ) LIKE '%home%'
OR (
CONCAT( prod.description, prod.pname ) LIKE '%home%'
AND foto.set_cover = '1'
AND prod.status = '1'
)
what I think what you meant
(
CONCAT( prod.pname, prod.description ) LIKE '%home%'
OR
CONCAT( prod.description, prod.pname ) LIKE '%home%'
)
AND foto.set_cover = '1'
AND prod.status = '1'

How to combine dynamic fields and DB::raw with Laravel's fluent query builder?

This is my desired SQL:
SELECT
p.id , p.title , p.msrp , i.files
, p.cost
FROM
`products` p
LEFT JOIN (
SELECT
GROUP_CONCAT( `images`.`filename` SEPARATOR ',' ) AS files , `product_id`
FROM
`images`
GROUP BY
`product_id`
) i ON i.product_id = p.id
Here is my query using Laravel's fluent query builder:
$products = DB::table( 'products as p' )
->where( 'p.active' , '=' , 1 )
->select( $fields )
->get();
The $fields variable is an array of fields based on whether the user is logged in or not:
$fields = array( 'p.id' , 'p.title' , 'p.msrp' , 'i.files' ) ;
if( Auth::check() ) array_push( $fields , 'p.cost' ) ;
I am trying the LEFT JOIN using DB::raw:
->leftJoin(DB::raw( "( SELECT GROUP_CONCAT( images.filename SEPARATOR ',' ) , product_id FROM images GROUP BY product_id ) i" , "i.product_id", "=" , "p.id" )
For some reason this is not working... am I doing something wrong? I need to use DB::raw so that I can GROUP_CONCAT multiple rows in the images table into a delimited string.
On a tangent... I think sub-query select is not supported so if anyone has a better option, please let me know.

Sum Qtys from 3 tables

The view below does not return correct results. I am trying to get the sum of Picked, Printed & Scanned grouped by Plan_Id & PartNum. I need to return the correct totals regardless if there are corresponding records in the child tables. I know how to do it if I use three different views and join them, but how do i do it all in a single view? Any help appreciated.
SELECT
`prod_plan`.`Prp_ProdPlanId` AS `PlanId`,
`prod_plan`.`Prp_PartNum` AS `PartNum`,
sum(`prod_plan`.`Prp_Picked`) AS `Picked`,
sum(`printed`.`PtQty`) AS `Printed`,
sum(`scanned`.`PtQty`) AS `Scanned`
FROM
(
(
`prod_plan`
LEFT JOIN `product_trans` `printed` ON (
(
(
`printed`.`PtPlanId` = `prod_plan`.`Prp_ProdPlanId`
)
AND (
`printed`.`PtPartNum` = `prod_plan`.`Prp_PartNum`
)
)
)
)
LEFT JOIN `product_trans` `scanned` ON (
(
(
`scanned`.`PtPlanId` = `prod_plan`.`Prp_ProdPlanId`
)
AND (
`scanned`.`PtPartNum` = `prod_plan`.`Prp_PartNum`
)
)
)
)
WHERE
(
(
`printed`.`PtPart` = 'Barcode Print'
)
AND (
`scanned`.`PtPart` = 'Barcode Scan'
)
)
GROUP BY
`prod_plan`.`Prp_ProdPlanId`,
`prod_plan`.`Prp_PartNum`
You need to check PtPart in the ON clauses. Otherwise, you won't get the rows with no matches in the child tables, because those columns will be NULL.
SELECT
`prod_plan`.`Prp_ProdPlanId` AS `PlanId`,
`prod_plan`.`Prp_PartNum` AS `PartNum`,
sum(`prod_plan`.`Prp_Picked`) AS `Picked`,
sum(`printed`.`PtQty`) AS `Printed`,
sum(`scanned`.`PtQty`) AS `Scanned`
FROM `prod_plan`
LEFT JOIN `product_trans` `printed`
ON `printed`.`PtPlanId` = `prod_plan`.`Prp_ProdPlanId`
AND `printed`.`PtPartNum` = `prod_plan`.`Prp_PartNum`
AND `printed`.`PtPart` = 'Barcode Print'
LEFT JOIN `product_trans` `scanned`
ON `scanned`.`PtPlanId` = `prod_plan`.`Prp_ProdPlanId`
AND `scanned`.`PtPartNum` = `prod_plan`.`Prp_PartNum`
AND `scanned`.`PtPart` = 'Barcode Scan'
GROUP BY
`prod_plan`.`Prp_ProdPlanId`,
`prod_plan`.`Prp_PartNum`
Here is what I have done. Below are 3 views that work correctly.
v_trans_printed:
SELECT
product_trans.PtPlanId AS PlanId,
product_trans.PtLot AS Lot,
product_trans.PtPartNum AS PartNum,
Sum(product_trans.PtQty) AS Printed
FROM
product_trans
WHERE
product_trans.PtPart = 'Barcode Print'
GROUP BY
product_trans.PtPlanId,
product_trans.PtLot,
product_trans.PtPartNum
v_trans_scanned:
SELECT
product_trans.PtPlanId AS PlanId,
product_trans.PtLot AS Lot,
product_trans.PtPartNum AS PartNum,
Sum(product_trans.PtQty) AS Scanned
FROM
product_trans
WHERE
product_trans.PtPart = 'Barcode Scan'
GROUP BY
product_trans.PtPlanId,
product_trans.PtLot,
product_trans.PtPartNum
And here I put them all together. This returns the correct results:
vSELECT
prod_plan.Prp_ProdPlanId AS PlanId,
prod_plan.Prp_Lot AS Lot,
prod_plan.Prp_PartNum AS PartNum,
Sum(prod_plan.Prp_Picked) AS Picked,
Printed.Printed AS Printed,
Scanned.Scanned AS Scanned
FROM
prod_plan
LEFT JOIN v_trans_printed AS Printed ON Printed.PlanId = prod_plan.Prp_ProdPlanId AND Printed.Lot = prod_plan.Prp_Lot AND Printed.PartNum = prod_plan.Prp_PartNum
LEFT JOIN v_trans_scanned AS Scanned ON Scanned.PlanId = prod_plan.Prp_ProdPlanId AND Scanned.Lot = prod_plan.Prp_Lot AND Scanned.PartNum = prod_plan.Prp_PartNum
GROUP BY
prod_plan.Prp_ProdPlanId,
prod_plan.Prp_Lot,
prod_plan.Prp_PartNum
But it would be better if I could use one view.
Note: I left out Lot originally by accident. But it didn't affect the results w. the sample data set.