Slow MySQL query in Laravel, but very fast in phpMyAdmin - laravel

I have worked with one query in Laravel, i need to optimize it. At first i have such query
DB::select(DB::raw("select `discounts`.`id`, `discounts`.`discount` from `discounts` left join `discount_companies_criteria` on `discounts`.`id` = `discount_companies_criteria`.`discount_id` and `discount_companies_criteria`.`is_active` = 1 where `discount_companies_criteria`.`company_id` is null and `discounts`.`status` = 1 group by `discounts`.`id`")) order by discounts.discount ASC;
laravel debugbar shows - 579ms
in phpmyadmin this query run by 503ms
than i have refactored code and get modified query (just remove sorting)
DB::select(DB::raw("select `discounts`.`id`, `discounts`.`discount` from `discounts` left join `discount_companies_criteria` on `discounts`.`id` = `discount_companies_criteria`.`discount_id` and `discount_companies_criteria`.`is_active` = 1 where `discount_companies_criteria`.`company_id` is null and `discounts`.`status` = 1 group by `discounts`.`id`")) ;
laravel debugbar shows - 579ms
in phpmyadmin this query run by 3ms
Can somebody explain, how i can get the same speed in laravel as in phpMyadmin?
Table discounts - 1900 rows
Table discount_companies_criteria - 7500000 rows

General tip, don't ever run query without limit "select, delete, or update", in your case use pagination
First the following query has no limit, which obviously would have bad performance depends on the size of the data you're dealing with:
SELECT `discounts`.`id`,
`discounts`.`discount`
FROM `discounts`
LEFT JOIN `discount_companies_criteria` ON `discounts`.`id` = `discount_companies_criteria`.`discount_id`
AND `discount_companies_criteria`.`is_active` = 1
WHERE `discount_companies_criteria`.`company_id` IS NULL
AND `discounts`.`status` = 1
GROUP BY `discounts`.`id`
The reason why this query or the other one are faster in phpMyAdmin is because phpMyAdmin by default set limit to the query.

Related

Laravel Paginate with OrderBy

When querying a product code table I have the following
$results = Stock::orderBy('stk_physical', 'desc')->paginate(10);
This works fine on the initial load of 10 records but when a subsequent call is made for page 2 I get the following error
Incorrect syntax near 'offset'. (SQL: select * from [stock_records] order by [stk_physical] desc offset 10 rows fetch next 10 rows only)
I'm using Laravel 8.0 with SQL
You should append query string to the pagination like this
$results = Stock::orderBy('stk_physical', 'desc')->paginate(10);
$results->appends(["order_by" => "stk_physical"]);
This will append the &order_by=stk_physical to each link in the view and you can also use withQueryString() to take in consideration query string in future pagination like this
$results = Stock::orderBy('stk_physical', 'desc')->paginate(10)->withQueryString();

Update with count and group by expressions

First, I know there is a common issue in Stack Overflow, but the following solutions are not working well here. So I still need some help.
Oracle - Update COUNT of rows with specific value
Oracle - Update rows with a min value in the group of a column from another table
Oracle update statement with group function
Oracle - Update COUNT of rows with specific value
The problem is: I have a +700k lines table:
REVIEWS (PRODUCT_ID, REVIEW, REVIEW_DATE, RELEASE_DATE, ..., REVIEW_COUNT)
I'm trying to update REVIEW_COUNT by counting the lines with the same PRODUCT_ID (I want just reviews before product release). So the code below works very well for my purpose:
SELECT COUNT(PRODUCT_ID) FROM REVIEWS
WHERE REVIEW_DATE < RELEASE_DATE
GROUP BY PRODUCT_ID
But I'm having a hard time to do the update. First I tried this:
UPDATE REVIEWS R
SET R.REVIEWS_COUNT =
(SELECT COUNT(RR.PRODUCT_ID) FROM REVIEWS RR
WHERE RR.DATA < RR.REL_DATE
GROUP BY RR.PRODUCT_ID)
The error is "more than one row", which is not surprising, but since I'm using the group by statement, it shouldn't occur. So I tried a self-join:
UPDATE REVIEWS R
SET R.REVIEWS_COUNT =
(SELECT COUNT(RR.PRODUCT_ID) FROM REVIEWS RR
WHERE RR.PRODUCT_ID = R.PRODUCT_ID AND RR.DATA < RR.REL_DATE)
But the query is taking forever and I don't think that should take so long, the simple select is pretty normal-fast.
I've also tested some more fancy and more simple stuff, but the outcome remains the same: long time waiting and it seems just wrong.
Please, what I'm missing in such easy update?
Maybe instead of updating you could define view:
select product_id, review_date, release_date,
count(case when review_date < release_date then 1 end)
over (partition by product_id) review_count
from reviews;
You could also try merge instead update:
merge into reviews a
using (select product_id, count(product_id) cnt from reviews
where review_date < release_date
group by product_id ) b
on (a.product_id = b.product_id)
when matched then update set reviews_count = b.cnt
dbfiddle
I think your second update is correct:
UPDATE REVIEWS R
SET R.REVIEWS_COUNT =
(SELECT COUNT(RR.PRODUCT_ID) FROM REVIEWS RR
WHERE RR.PRODUCT_ID = R.PRODUCT_ID AND RR.DATA < RR.REL_DATE)
;
This will update every record in the reviews table. Is that what you wanted?
An index on product_id will make the inner query run faster, but it will still update all 700K or so records.

Subtraction with aggregation using 2 tables

My Laravel query is not working properly. but MySQL query works fine
Laravel Query :
$data = DB::table(DB::raw('select (sum(case when type="credit" then amount else -amount end)) - (select sum(amount) from total) from report'))
Please refer mysql query in sqlfiidle : http://sqlfiddle.com/#!9/2d0343/9
Rather than try rewrite your MySQL query, let's simplify it and make it more eloquent.
Assuming $data is the overall balance and your model for the report table is called Report then the below should achieve what you are after:
$data = Report::where('type', 'credit')->sum('amount') - Report::where('type', 'debit')->sum('amount');
This will give you the sum of all your credits, minus the sum of all your debits.

Running "exists" queries in Laravel query builder

I'm using MySQL and have a table of 9 million rows and would like to quickly check if a record (id) exists or not.
Based on some research it seems the fastest way is the following sql:
SELECT EXISTS(SELECT 1 FROM table1 WHERE id = 100)
Source: Best way to test if a row exists in a MySQL table
How can I write this using Laravel's query builder?
Use selectOne method of the Connection class:
$resultObj = DB::selectOne('select exists(select 1 from your_table where id=some_id) as `exists`');
$resultObj->exists; // 0 / 1;
see here http://laravel.com/docs/4.2/queries
Scroll down to Exists Statements, you will get what you need
DB::table('users')
->whereExists(function($query)
{
$query->select(DB::raw(1))
->from('table1')
->whereRaw("id = '100'");
})
->get();
This is an old question that was already answered, but I'll post my opinion - maybe it'll help someone down the road.
As mysql documentation suggests, EXISTS will still execute provided subquery. Using EXISTS is helpful when you need to have it as a part of a bigger query. But if you just want to check from your Laravel app if record exists, Eloquent provides simpler way to do this:
DB::table('table_name')->where('field_name', 'value')->exists();
this will execute query like
select count(*) as aggregate from `table_name` where `field_name` = 'value' limit 1
// this is kinda the same as your subquery for EXISTS
and will evaluate the result and return a true/false depending if record exists.
For me this way is also cleaner then the accepted answer, because it's not using raw queries.
Update
In laravel 5 the same statement will now execute
select exists(select * from `table_name` where `field_name` = 'value')
Which is exactly, what was asked for.

Oracle ignores hint for index with synonym and 2 views

This is the query i am running:
select /*+ index(V_AMV_PLG_ORDER_HISTORY_200_MS.orders.T0 IDX_ORDER_VERSION_3) */ *
from V_AMV_PLG_ORDER_HISTORY_200_MS
where EXCHANGE_SK = 32 and PRODUCT_SK = 1000169
And it uses a different index than the one i am ordering it to.
As you can see, I am querying from the view V_AMV_PLG_ORDER_HISTORY_200_MS, you can see its sql query here:
V_AMV_PLG_ORDER_HISTORY_200_MS view SQL Query:
SELECT AMV_PERF_PROFILES_FRONTEND.AMV_PLG_GET_SEGMENT(200, orders.ORDER_GLOBAL_DATE_TIME) AS ORDER_DATE_TIME,
SUM(orders.BASE_VOLUME) AS VOLUME,
SUM(orders.BASE_CURR_LIMIT_PRICE*orders.BASE_VOLUME)/SUM(orders.BASE_VOLUME) AS PRICE,
orders.PRODUCT_SK AS PRODUCT_SK,
orders.EXCHANGE_SK AS EXCHANGE_SK,
orders.DIRECTION_CD AS DIRECTION_CD,
orders.AGG_UNIT_CD AS AGG_UNIT_CD,
orders.TRADER_KEY AS EXECUTING_REPRESENTATIVE_KEY,
orders.ACCOUNT_KEY AS ACCOUNT_KEY,
a.BUSINESS_UNIT_CD AS BUSINESS_UNIT_CD
FROM AMV_PERF_PROFILES_FRONTEND.S_AMV_ORDER_VERSION_NEW orders
INNER JOIN AMV_PERF_PROFILES_FRONTEND.S_AMV_ACCOUNT a
ON a.ACCOUNT_KEY = orders.ACCOUNT_KEY
WHERE BASE_VOLUME > 0
GROUP BY AMV_PERF_PROFILES_FRONTEND.AMV_PLG_GET_SEGMENT(200, orders.ORDER_GLOBAL_DATE_TIME),
orders.PRODUCT_SK,
orders.EXCHANGE_SK,
orders.ACCOUNT_KEY,
a.BUSINESS_UNIT_CD,
orders.AGG_UNIT_CD,
orders.TRADER_KEY,
orders.DIRECTION_CD;
He is getting the data using the Synonym S_AMV_ORDER_VERSION_NEW, Which directs to another Scheme, to a view called V_AMV_ORDER_VERSION and refering to it as orders, its sql query here:
V_AMV_ORDER_VERSION view Sql query:
SELECT T1.ENTITY_KEY ,
T2.AGG_UNIT_CD ,
T0.BASE_CURR_LIMIT_PRICE ,
T7.DIRECTION_CD ,
T0.EXCHANGE_SK,
T0.ORDER_LOCAL_DATE_TIME ,
T0.PRODUCT_SK,
T18.ENTITY_KEY ,
T19.ENTITY_KEY ,
T0.NOTIONAL_VALUE2 ,
T0.NOTIONAL_VALUE ,
T0.ORDER_GLOBAL_DATE_TIME ,
T0.BASE_VOLUME ,
T31.TRANSACTION_STATUS_CD ,
T0.ORDER_VERSION_KEY
FROM ETS_UDM_CDS_NEW.ORDER_VERSION T0
LEFT OUTER JOIN ETS_UDM_CDS_NEW.ENTITY T1
ON T0.ACCOUNT_SK = T1.ENTITY_SK
LEFT OUTER JOIN ETS_UDM_CDS_NEW.AGG_UNIT T2
ON T0.AGG_UNIT_SK = T2.ENTITY_SK
LEFT OUTER JOIN ETS_UDM_CDS_NEW.DIRECTION T7
ON T0.DIRECTION_SK = T7.ENTITY_SK
LEFT OUTER JOIN ETS_UDM_CDS_NEW.ENTITY T18
ON T0.LOCAL_TIME_ZONE_SK = T18.ENTITY_SK
LEFT OUTER JOIN ETS_UDM_CDS_NEW.ENTITY T19
ON T0.TRADER_SK = T19.ENTITY_SK
LEFT OUTER JOIN ETS_UDM_CDS_NEW.TRANSACTION_STATUS T31
ON T0.TRANSACTION_STATUS_SK = T31.ENTITY_SK;
Which takes its data from a table called ORDER_VERSION and refers to it as T0
this table has an index called IDX_ORDER_VERSION
The problem is that oracle ignores my hint, And uses a different index, Now, I have managed to use a hint to make oracle use an index i wanted when i was querying a view that gets data from a table, But this time I am querying a view which gets his data from another view which gets his data from a table.
And also, The second view in the line is on a different Scheme and i am using a synonym, So perhaps that is why i am missing something Cuz i tried many combinations of possible solutions i found on google but nothing seems to be working...
I would say that if i go one step forward and query directly from V_AMV_ORDER_VERSION (Without the synonym) IT works and i can make oracle work with any index i want, so this query works perfect:
select /*+ index(orders.T0 IDX_ORDER_VERSION_5) */ * from V_AMV_ORDER_VERSION orders
where EXCHANGE_SK =32 and PRODUCT_SK = 1000169
Well me and our company's DBA looked at it for a while, it seems like an Oracle bug in the Global Hint manifestation, We have created the view V_AMV_PLG_ORDER_HISTORY_200_MS using a regular join rather than an ANSI join, and now it works properly:
V_AMV_PLG_ORDER_HISTORY_200_MS view SQL Query:
SELECT AMV_PERF_PROFILES_FRONTEND.AMV_PLG_GET_SEGMENT(200, orders.ORDER_GLOBAL_DATE_TIME) AS ORDER_DATE_TIME,
SUM(orders.BASE_VOLUME) AS VOLUME,
SUM(orders.BASE_CURR_LIMIT_PRICE*orders.BASE_VOLUME)/SUM(orders.BASE_VOLUME) AS PRICE,
orders.PRODUCT_SK AS PRODUCT_SK,
orders.EXCHANGE_SK AS EXCHANGE_SK,
orders.DIRECTION_CD AS DIRECTION_CD,
orders.AGG_UNIT_CD AS AGG_UNIT_CD,
orders.TRADER_KEY AS EXECUTING_REPRESENTATIVE_KEY,
orders.ACCOUNT_KEY AS ACCOUNT_KEY,
a.BUSINESS_UNIT_CD AS BUSINESS_UNIT_CD
FROM AMV_PERF_PROFILES_FRONTEND.S_AMV_ORDER_VERSION_NEW orders,
AMV_PERF_PROFILES_FRONTEND.S_AMV_ACCOUNT a
WHERE BASE_VOLUME > 0 AND a.ACCOUNT_KEY = orders.ACCOUNT_KEY
GROUP BY AMV_PERF_PROFILES_FRONTEND.AMV_PLG_GET_SEGMENT(200, orders.ORDER_GLOBAL_DATE_TIME),
orders.PRODUCT_SK,
orders.EXCHANGE_SK,
orders.ACCOUNT_KEY,
a.BUSINESS_UNIT_CD,
orders.AGG_UNIT_CD,
orders.TRADER_KEY,
orders.DIRECTION_CD;

Resources