Convert Raw SQL 'NOT' IN (too slow) to Laravel Eloquent - php

I have a running script using Raw SQL in Laravel 5.3 controller which I found it slow and not secure ( as Raw ). If there any way to make it more effecient and convert it to eloquent or Query Builder for Laravel ?
code as below & Thanks !
SELECT machine_code, machine_name
FROM factory_equipment
WHERE machine_code NOT IN
(
SELECT distinct(machine_code)
FROM echecklist_data
WHERE DATE_FORMAT(date, '%Y-%m-%d') = CURDATE()
)
AND type='production' ORDER BY machine_code ASC

You can make it more efficient using SQL. I would recommend NOT EXISTS:
SELECT fe.machine_code, fe.machine_name
FROM factory_equipment fe
WHERE NOT EXISTS (SELECT 1
FROM echecklist_data ed
WHERE ed.machine_code = fe.machine_code AND
ed.date >= CURDATE() AND
ed.date < CURDATE() + INTERVAL 1 DAY
) AND
fe.type = 'production'
ORDER BY fe.machine_code ASC;
I would further recommend indexes on: factory_equipment(type, machine_code, machine_name) and echecklist_data(machine_code, date).

You can speed up this query by making it into a LEFT JOIN and selecting only rows which have no match in the echecklist_data table:
SELECT fe.machine_code, fe.machine_name
FROM factory_equipment fe
LEFT JOIN echecklist_data ed ON ed.machine_code = fe.machine_code AND DATE_FORMAT(ed.date, '%Y-%m-%d') = CURDATE()
WHERE fe.type='production' AND ed.machine_code IS NULL
ORDER BY fe.machine_code ASC
Note that if your date column is a DATETIME type, then using DATE(ed.date) = CURDATE() will also be more efficient than DATE_FORMAT(ed.date, '%Y-%m-%d') = CURDATE().

I managed to get it working using laravel's query. Hope it helps others
DB::table('factory_equipment')
->select('factory_equipment.machine_code', 'factory_equipment.machine_name')
->leftjoin('echecklist_data', function ($leftjoin) {
$leftjoin->on('factory_equipment.machine_code', '=', 'echecklist_data.machine_code')
->Where( DB::raw("DATE_FORMAT(echecklist_data.date, '%Y-%m-%d')"), DB::raw("CURDATE()") );
})
->where('factory_equipment.type' , '=', 'production')
->whereNull('echecklist_data.machine_code')
->get();

Related

how to convert inner query in select to eloquent laravel 5.4

I have a query like this..
select *,
case l.user_type
when '0' then
(select CONCAT(first_name,'',last_name) from users where id=l.user_id)
when '1' then
(select party_name from tbl_partys where id=l.user_id)
end as user_name
from tbl_leased_comm l
where l.user_id=$party and l.user_id=$user_id
order by l.updated_at desc
How to convert this query to laravel query
help me, anyone...
Yeah it works for me
as,
DB::table('tbl_leased_comm')
->select(["*",
DB::raw("case tbl_leased_comm.user_type when '0' then (select CONCAT(first_name,'',last_name) from users where id=tbl_leased_comm.user_id) when '1' then (select party_name from tbl_partys where id=tbl_leased_comm.user_id) end as user_name")])
->where('tbl_leased_comm.user_id','=',$party )
->where('tbl_leased_comm.user_id','=',$user_id)
->orderBy('tbl_leased_comm.updated_at', 'desc')
->get();
Thanks a lot..
reference link:Convert mysql query logic to Laravel query builder

Better way to write this query in order to convert it into laravel eloquent

I have prepared this query but unable to convert it to laravel eloquent. I need help to find out if there is a better way to write this query in order.
Below is the query
https://gist.github.com/thedesignerkumar/ef9062efb262ec9c128f4832af32d3e2
SELECT
rooms.location,
rooms.id,
rooms.`name`,
users.name,
IF(
(
bookings.`status` = 'booked' AND(
NOT(
bookings.end_time <= '2017-05-30 12:00:00' OR bookings.start_time >= '2017-05-30 12:35:00'
) OR(
bookings.recurring = 1 AND DATE(bookings.start_time) <= '2017-05-30' AND NOT(
TIME(bookings.end_time) <= '12:00:00' OR TIME(bookings.start_time) >= '12:35:00'
)
)
)
),
'Booked',
'Available'
) AS 'Status',
bookings.recurring,
bookings.start_time,
bookings.end_time
FROM
rooms
LEFT JOIN
bookings
ON
rooms.id = bookings.room_id
LEFT JOIN
users
ON
bookings.user_id = users.id
ORDER BY
STATUS
DESC
,
bookings.start_time ASC,
rooms.id ASC
Eloquent isn't the only method of querying a database in Laravel. Eloquent is just the ORM that comes with Laravel. Eloquent itself is built on the QueryBuilder class that also comes with Laravel.
You can certainly rebuild your query using the query builder but a more efficient method would be to user your existing query like so: DB::select($query).
If you choose to just put your query directly into DB::select() rather than rebuilding it with the QueryBuilder you will be vulnerable to SQL injection attacks. You'll need to handle these prior to building the SQL query.

to accelerate mysql query

I have this query in MySQL. This query is taking too long to run, and I know the problem is the selectors (coalesce ((SELECT ...), I do not know how to speed up a query, via join.
I am hoping some of you SQL gurus will be able to help me.
SELECT
COALESCE(
(SELECT CONCAT(d.PRIJEVOZNIK, ' ', d.VOZAC_TRANSFER)
FROM dokum_zag as d
where d.SIFKNJ='NP' and
d.ID_VEZA=dokum_zag.ID and
d.korisnicko_ime=dokum_zag.korisnicko_ime
),'') as PRIJEVOZNIK,
(RELACIJA_TRANS_VOZ_TRANS) as RELACIJA_TRANS_VOZ,
(PRIJEVOZNIK_POVRATNI_TRANS) as PRIJEVOZNIK_POVRATNI,
(VAUC_KNJIZENO_TRANS) as VAUC_KNJIZENO,
ID_NALOGA,
ID_NALOGA_POV,
ID_VAUCHER,
DOLAZAK, VRIJ_TRANSFER,ODLAZAK,VRIJEME_LETA_POVRAT ,BRDOK, NOSITELJ_REZ, RELACIJA_TRANS, VOZILO_NAZIV, BROJ_NALOGA,BROJ_NAL_POV,BROJ_VAUCHER,BROJ_SOBE,VALIZN,PAX, MPIZN,ID
FROM
dokum_zag
WHERE
korisnicko_ime = '10' and
((DOLAZAK='2015-07-30') or (ODLAZAK='2015-07-30')) and
STORNO <> 'DA' and
SIFKNJ = 'TR' and
((YEAR(DOLAZAK)= '2015') or (YEAR(ODLAZAK)= '2015'))
order by
(CASE WHEN DOLAZAK < '2015-07-30' THEN ODLAZAK ELSE DOLAZAK END) ,
(CASE WHEN DOLAZAK < '2015-07-30' THEN VRIJEME_LETA_POVRAT ELSE VRIJ_TRANSFER END), ID
Without a DB structure, and a description of what you want to extract it's a bit hard to help you.
From a logical point of view, somethings are redundant, for example
((DOLAZAK='2015-07-30') or (ODLAZAK='2015-07-30')) and
...
((YEAR(DOLAZAK)= '2015') or (YEAR(ODLAZAK)= '2015'))
The year part isn't necessary since the year is specified on the first two.
Another thing that might be making the server nuts, is that weird order by clause, since it changes from record to record (test this setting it fixed on a field).
You can also check if your indexes are properly set for all the fields on the external where clause, and those which are not numeric, are not varchars (for example SIFKNJ and STORNO should be char(2) ).
The coalesce part can be solved via an outer join, so it doesn't get calculated on each row. But that depends on what and how you want to extract from the database... (since that subquery has it's own fields on the where section... weird)
Hope this somehow helps
INDEX(korisnicko_ime, SIFKNJ)
(in either order) may help
Turning the correlated subquery into a JOIN may help.
((DOLAZAK='2015-07-30') or (ODLAZAK='2015-07-30')) and
((YEAR(DOLAZAK)= '2015') or (YEAR(ODLAZAK)= '2015'))
is a bit weird. This might help:
( SELECT ...
AND DOLAZAK ='2015-07-30' AND ODLAZAK >= '2015-01-01'
AND ODLAZAK < '2015-01-01' + INTERVAL 1 YEAR
) UNION DISTINCT
( SELECT ...
AND ODLAZAK ='2015-07-30' AND DOLAZAK >= '2015-01-01'
AND DOLAZAK < '2015-01-01' + INTERVAL 1 YEAR
) ORDER BY ...
To help that reformulation, add 2 composite indexes:
INDEX(korisnicko_ime, SIFKNJ, DOLAZAK, ODLAZAK)
INDEX(korisnicko_ime, SIFKNJ, ODLAZAK, DOLAZAK)

Selecting most recent record for each user in codeigniter

i found this query which i believe is going to solve my problem :
SELECT MemberID, ContractID, StartDate, EndDate
FROM member_contracts
WHERE ContractId IN (
SELECT MAX(ContractId)
FROM member_contracts
GROUP BY MemberId
)
this query hass been given by #Mark Byers as an answer to another question.
how can i use this query in CodeIgniter especially the "Where_in" clause.
thanks
You can still use active record library to compile your subquery by using get_compiled_select()
$this->db->select('MAX(ContractId)')
->from('member_contracts')
->group_by('MemberId');
$subquery = $this->db->get_compiled_select();
$this->db->select('MemberID, ContractID, StartDate, EndDate')
->from('member_contracts ')
->where("ContractId IN($subquery)", NULL, FALSE)
->get()
->result();
Also you can rewrite your query with join instead of sub query to get latest record per group and also a compound index on MemberId,ContractId will be useful
SELECT m.MemberID, m.ContractID, m.StartDate, m.EndDate
FROM member_contracts m
JOIN (
SELECT MemberId,MAX(ContractId) ContractId
FROM member_contracts
GROUP BY MemberId
) mm
USING(MemberId,ContractId)
In CI, you can use $this->db->query("yourquery"); to execute a query. Try with
$query=$this->db->query("SELECT MemberID, ContractID, StartDate, EndDate
FROM member_contracts
WHERE ContractId IN (
SELECT MAX(ContractId)
FROM member_contracts
GROUP BY MemberId
)");

Collecting two separate column results from mySQL query

I have two database tables containing similar data - one is the amount of work completed by a process, the other is the amount of errors made by that process.
I would like to display the percentage of right first time work.
My query for counting the number of items by process is:
SELECT Counter.Process, count( Counter.Process ) AS count
FROM Counter WHERE (TIME BETWEEN '2012-07-01 00:00:00' AND '2012-07-06 23:59:59')
GROUP BY Counter.Process
ORDER BY count DESC
The above gives me a list similar to:
Process | count
-------------------
Process A | 40
Process B | 32
Process C | 102
Process D | 23
I have a second table with the same fields in it for recording errors, called 'Errors'.
By using the same query (but changing the table names obviously) I can get a list of processes and errors.
What I want to do is create a PHP web page showing the percentage of errors per process.
My problem is if I create the two queries separately, when I loop through them using a PHP Where loop I don't know how to make the calculation to show the percentage.
I tried using a UNION between the two SQL queries, but I can't get it to show the two different count readings against the process.
Ideally, I'd like a way to show the data like this:
Process | Counter.count | Errors.count
Process A 40 2
Process B 32 0
Process C 102 18
Process D 23 8
I would really appreciate any advice/suggestions as to how I can accomplish this - if you need more info on my database tables, please let me know
Thanks for reading
S
I did not run it so there might be mistakes. you can make the two selects, and left join the results:
select s.process, s.success_count, e.error_count
from
(SELECT Counter.Process as process, count( Counter.Process ) AS success_count
FROM Counter
WHERE (TIME BETWEEN '2012-07-01 00:00:00' AND '2012-07-06 23:59:59')
GROUP BY Counter.Process
) s left join
(SELECT Errors.Process, count( Errors.Process ) AS error_count
FROM Errors
WHERE (TIME BETWEEN '2012-07-01 00:00:00' AND '2012-07-06 23:59:59')
GROUP BY Errors.Process
) e
on (s.Process = e.process)
I personally prefer to run situations like this through PHP as it confuses me less, so I will explain how I think I would do it in PHP (I say 'think' because I might not know something about your database structure or system specifications that would make me thing otherwise):
$rows = array();
$res = mysql_query('SELECT Counter.Process, count( Counter.Process ) AS count
FROM Counter WHERE (TIME BETWEEN '2012-07-01 00:00:00' AND '2012-07-06 23:59:59')
GROUP BY Counter.Process
ORDER BY count DESC');
while ($arr = mysql_fetch_assoc($res))
{
$rows[] = $arr;
}
$res2 = mysql_query('SELECT Errors.Process, count( Errors.Process ) AS count
FROM Counter WHERE (TIME BETWEEN '2012-07-01 00:00:00' AND '2012-07-06 23:59:59')
GROUP BY Errors.Process
ORDER BY count DESC');
while ($arr2 = mysql_fetch_assoc($res2)
{
for ($i=0; $i<count($rows); $i++)
{
if ($arr2["Process"] == $data[$i]["process"])
{
break;
}
//Do your functions with $arr and $data[$i] at this point...
}
}
Doing it without a subquery should be simple:
SELECT
Counter.Process
, COUNT(Counter.Process) AS ProcessCount
, COUNT(Errors.Process) AS ProcessErrors
FROM
Counter
LEFT JOIN
Errors
ON Counter.Process = Errors.Process
WHERE
Counter.TIME BETWEEN '2012-07-01 00:00:00' AND '2012-07-06 23:59:59'
GROUP BY
Counter.Process
You can do an explain on both queries to see which is the best for your data.
-edit: I've changed it to a LEFT JOIN so you don't miss rows if a process has no errors.
You could use a sub-query:
SELECT Counter.Process, count( Counter.Process ) AS Count,
(SELECT count(Errors.Process)
FROM Errors
WHERE Process=Counter.Process
AND (TIME BETWEEN '2012-07-01 00:00:00' AND '2012-07-06 23:59:59')) AS Errors
FROM Counter WHERE (TIME BETWEEN '2012-07-01 00:00:00' AND '2012-07-06 23:59:59')
GROUP BY Counter.Process
ORDER BY count DESC
Regards,
Neil.

Categories