Mysql query enhancement for union - php

I have this query that will be used for reports and i have a lot of data.
As of the moment is this my query that uses of UNION ALL over UNION because i remembered last project i used the UNION is that it dont unite all the datas and i never knew why so i used the UNION ALL
SELECT `item_id`, NULL AS `ob`,SUM(`add`) AS `add` ,NULL AS `dam`,NULL AS `sold`,NULL AS `others`,NULL AS `ac`
FROM (
-- DELIVERED ITEMS
SELECT si.`item_id`,SUM(sdi.`actual_pcs`) AS `add`
FROM `store_inventory` si
LEFT JOIN `store_delivery` sd
ON sd.`store_id` = si.`store_id` AND DATE(sd.`dt_modified`) = '2018-09-01'
AND sd.`status` = "DONE"
LEFT JOIN `store_delivery_items` sdi
ON sdi.`strd_id` = sd.`strd_id` AND sdi.`item_id` = si.`item_id`
WHERE si.`store_id` = '4'
GROUP BY si.`item_id`
UNION ALL
-- OVERRIDE ADD (ADD)
SELECT si.`item_id`,SUM(oi.`qty`) AS `add`
FROM `store_inventory` si
LEFT JOIN `override` o
ON o.`ref_id` = si.`store_id`
AND o.`type` = "STORE"
AND o.`action` = "ADD"
AND DATE(o.`dt_created`) = '2018-09-01'
LEFT JOIN `override_items` oi
ON oi.`item_id` = si.`item_id`
AND oi.`ovr_id` = o.`ovr_id`
WHERE si.`store_id` = '4'
GROUP BY si.`item_id`
UNION ALL
-- CANCELLED SALES (ADD)
SELECT si.`item_id`,SUM(sai.`si_qty`) AS `add`
FROM `store_inventory` si
LEFT JOIN `sales` s
ON s.`sales_store_id` = si.`store_id`
AND s.`cancelled_by` IS NOT NULL
AND DATE(s.`dt_cancelled`) = '2018-09-01'
LEFT JOIN `sales_items` sai
ON sai.`si_sales_id` = s.`sales_id`
AND sai.`si_item_id` = si.`item_id`
WHERE si.`store_id` = '4'
GROUP BY si.`item_id`
UNION ALL
-- PULLOUT REJECTED ADDED BACK TO STORE (ADD)
SELECT si.`item_id`,SUM(poi.`poi_actual`) AS `add`
FROM `store_inventory` si
LEFT JOIN `pullout` p
ON p.`pullout_reason` IN ("STORE TO DAMAGE","STORE TO WAREHOUSE")
AND p.`pullout_status` IN ("REJECT","HEAD-REJECTED")
AND DATE(p.`dt_modified`) = '2018-09-01'
AND p.`pullout_from` = si.`store_id`
LEFT JOIN `pullout_items` poi
ON poi.`poi_item_id` = si.`item_id`
AND poi.`poi_pullout_id` = p.`pullout_id`
WHERE si.`store_id` = '4'
GROUP BY si.`item_id`
) AS add_temp
GROUP BY `item_id`
As you can see i loop all my table from my store inventory items this is only a one day report because i do the loop in php for date ranges. Im wondering if this query can be enhanced more. I tried the left join all over my tables and i got no luck. Is this the right query for this kind of reporting? this is just part my query BTW. I still have this openingbalance,damaged,sold,others query as you can see in my SELECT statement. So i really need an advise regarding with this long query. I already added indeces of my columns to my tables in order to select results faster.

Related

Multiple JOINS using UNION ALL with WHERE clause

I wanted to generate a report. User have to select position name using dropdown menu. This the the query that I've wrote but it's not working and I don't how else need to fix. Any helps appreciated.
SELECT * FROM
(
SELECT COUNT(tapplied.applied_id) as TotalCandidate, exam_code.exam_name
FROM ((exam
INNER JOIN tapplied ON exam.applied_id = tapplied.applied_id)
INNER JOIN exam_code ON exam.exam_id = exam_code.exam_id)
WHERE exam_code.exam_id <> '8'
GROUP BY exam_name
UNION ALL
SELECT COUNT(tapplied.exam_id) as TotalCandidate, level_code.level_name
FROM ((tipt INNER JOIN tapplied ON tipt.applied_id = tapplied.applied_id)
INNER JOIN level_code ON tipt.level_id = level_code.level_id)
GROUP BY level_name
) x
WHERE x.position_name = '{$jaw}'

Using OR, AND operator in single query

I am trying to list the records which meets my condition. As ii am using AND and OR operator together i am not getting the exact report. Here is my query
SELECT o.sales_order_id AS SID, o.reference, o.status, o.last_modified, sol.sales_order_id, sol.item, sol.quantity, sol.selling_price, sol.discount, sol.tax, SUM(sol.tax_amount) AS Tamt, SUM(sol.total) as Total, i.iid, GROUP_CONCAT(DISTINCT i.name) AS iname, l.company, t.tax_id, t.name as tname, t.rate from orders o INNER JOIN before_order_line_items sol ON o.sales_order_id = sol.sales_order_id INNER JOIN leads l ON o.company_id=l.id INNER JOIN items i ON sol.item=i.iid INNER JOIN taxes t ON sol.tax=t.tax_id WHERE o.order_quote='Order' AND o.authorise='Yes' OR o.assigned_to=6 OR o.user_id=6 GROUP BY o.sales_order_id ORDER BY o.sales_order_id DESC
I am storing both orders and quotations in single table Orders, for orders i store it as Order in order_quote column, for Quotations it is Quote
It is not checking order_quote='Order' condition, it displays both orders and quotations.
if i remove OR o.assigned_to=6 OR o.user_id=6 , it gives proper result.
I tried using DISTINCT like this
SELECT DISTINCT o.order_quote=`Order`, .....
But does't work.
UPDATED
SELECT o.sales_order_id AS SID, o.reference, o.status, o.last_modified, sol.sales_order_id, sol.item, sol.quantity, sol.selling_price, sol.discount, sol.tax, SUM(sol.tax_amount) AS Tamt, SUM(sol.total) as Total, i.iid, GROUP_CONCAT(DISTINCT i.name) AS iname, l.company, t.tax_id, t.name as tname, t.rate from orders o INNER JOIN before_order_line_items sol ON o.sales_order_id = sol.sales_order_id INNER JOIN leads l ON o.company_id=l.id INNER JOIN items i ON sol.item=i.iid INNER JOIN taxes t ON sol.tax=t.tax_id WHERE (o.order_quote='Order' AND o.authorise='Yes') AND (o.assigned_to=6 OR o.user_id=6) GROUP BY o.sales_order_id ORDER BY o.sales_order_id DESC
You need to use parentheses. I'm not sure exactly how, but your current where clause is interpreted as:
WHERE (o.order_quote = 'Order' AND o.authorise = 'Yes') OR
(o.assigned_to = 6) OR
(o.user_id = 6)
I would guess that you want something like this:
WHERE (o.order_quote = 'Order' AND o.authorise = 'Yes') AND
(o.assigned_to = 6 OR o.user_id = 6)
But that is mere speculation.
Or perhaps:
WHERE (o.order_quote = 'Order' AND
(o.authorise = 'Yes' OR o.assigned_to = 6 OR o.user_id = 6)

Sub query return more than one field

I have a table structure like this
Table Structure
Here I want to select all the data from bus_routines table with bus details and avaliable_seat which is calculated from buses.number_of_seat - reserved_seats.number_of_reserved_seat - booking.number_of_seat even if the data is not present in booking table and reserved_seats table too where the bus_routines.sector_from=Ktm ,bus_routines.sector_to=Pkr and bus_routines.date=2015-12-15
Relation between them are :
buses and bus_routines --> one to many bus_routines and booking -->
one to many bus_routines and reserved_seats --> one to many
I have tried the following query
SELECT r.* , b.* ,
(
SELECT b.number_of_seat - sum(booking.number_of_seat)-sum(reserved_seats.number_of_reserved_seat)
FROM bus_routines AS r
INNER JOIN buses AS b
ON b.id = r.bus_id
INNER JOIN
(SELECT number_of_seat , bus_routine_id FROM booking GROUP BY booking.bus_routine_id) AS booking
ON booking.bus_routine_id = r.id
INNER JOIN (SELECT number_of_reserved_seat , routine_id FROM reserved_seats GROUP BY reserved_seats.routine_id) AS reserved_seats
ON r.id = reserved_seats.routine_id
WHERE
r.sector_from = "KTM" AND
r.sector_to = "PKR" AND
r.departure_date = "2015-12-15"
) AS avaliable_seat
FROM bus_routines AS r
INNER JOIN buses AS b
ON b.id = r.bus_id
WHERE
r.sector_from = "KTM" AND
r.sector_to = "PKR" AND
r.departure_date = "2015-12-15"
HAVING avaliable_seat > 0
I get the result what I want but the avaliable_seat is same for all the row
I have tried another query too but it give me the single result
SELECT r.* , b.* , b.number_of_seat - sum(booking.number_of_seat)-sum(reserved_seats.number_of_reserved_seat) AS available_seat
FROM bus_routines AS r
INNER JOIN buses AS b
ON b.id = r.bus_id
INNER JOIN
(SELECT number_of_seat , bus_routine_id FROM booking GROUP BY booking.bus_routine_id) AS booking
ON booking.bus_routine_id = r.id
INNER JOIN
(SELECT number_of_reserved_seat , routine_id FROM reserved_seats GROUP BY reserved_seats.routine_id) AS reserved_seats
ON r.id = reserved_seats.routine_id
WHERE
r.sector_from = "KTM" AND
r.sector_to = "PKR" AND
r.departure_date = "2015-12-15"
HAVING available_seat > 0
I also tried another query and it give me Subquery returns more than 1 row . The query is
SELECT r.* , b.* ,
b.number_of_seat - (SELECT sum(number_of_seat) FROM booking GROUP BY booking.bus_routine_id)
- (SELECT sum(number_of_reserved_seat) FROM reserved_seats GROUP BY reserved_seats.routine_id) AS available_seat
FROM bus_routines AS r
INNER JOIN buses AS b
ON b.id = r.bus_id
WHERE
r.sector_from = "KTM" AND
r.sector_to = "PKR" AND
r.departure_date = "2015-12-15"
HAVING available_seat > 0
One approach is to use correlated subqueries to get the reserved_seats and booked_seats for each bus_routine.
Let's assume that this query returns the rows you are wanting to return, it's just missing the available_seat column you want calculated:
SELECT r.*
, b.*
FROM bus_routines r
JOIN buses b
ON b.id = r.bus_id
WHERE r.sector_from = 'KTM'
AND r.sector_to = 'PKR'
AND r.departure_date = '2015-12-15'
To number of "reserved seats" for a given bus_routine, you can query the reserved_seats table, like this:
SELECT IFNULL(SUM(s.number_of_reserved_seat),0)
FROM reserved_seats s
WHERE s.routine_id = '649'
And the number of "booked seats" for a given bus_routine can be returned from the booking table, like this:
SELECT IFNULL(SUM(k.number_of_seat),0)
FROM booking k
WHERE k.bus_routine_id = '649'
We can incorporate the queries to get "reserved seats" and "booked seats" into the first query, as correlated subqueries. In place of the literal '649', with a reference the id from the bus_routine table.
SELECT r.*
, b.*
-- number of booked seats
, ( SELECT IFNULL(SUM(k.number_of_seat),0)
FROM booking k
WHERE k.bus_routine_id = r.id
) AS booked_seats
-- number of reserved seats
, ( SELECT IFNULL(SUM(s.number_of_reserved_seat),0)
FROM reserved_seats s
WHERE s.routine_id = r.id
) AS reserved_seats
-- calculate available seats as
-- (bus number_of_seat) - (booked_seats) - (reserved_seats)
, ( b.number_of_seat
- ( SELECT IFNULL(SUM(k.number_of_seat),0)
FROM booking k
WHERE k.bus_routine_id = r.id
)
- ( SELECT IFNULL(SUM(s.number_of_reserved_seat),0)
FROM reserved_seats s
WHERE s.routine_id = r.id
)
) AS avaliable_seat
FROM bus_routines r
JOIN buses b
ON b.id = r.bus_id
WHERE r.sector_from = 'KTM'
AND r.sector_to = 'PKR'
AND r.departure_date = '2015-12-15'
If there's no requirement to return the booked_seats and reserved_seats columns, those can be omitted from the query. The subqueries to get those values can just appear in the calculation of the available_seat column.
SQL Fiddle demonstration here: http://sqlfiddle.com/#!9/64eaa/7
Please try GROUP_CONCAT for return one column from multiple in subquery.
refer the links.
How to use GROUP_CONCAT in a CONCAT in MySQL
http://www.w3resource.com/mysql/aggregate-functions-and-grouping/aggregate-functions-and-grouping-group_concat.php

mysql query become slow when data reached upto 100000

I build a query a month ago on a website. It was working fine. But after a month I was informed that the website become very slow to load the page.
When I search for the problem, I found that my query is executing very slow to fetch the data from mysql database. Then I check for the database and found that the 4 tables which I was using by joins, have around 216850, 167634, 64000, 931 rows respectively.
I have already have indexed that tables. So, where I'm lacking. Please help guys.
[Edit]
Table1: user_alert
Records: 216850
DB Type: InnoDB
Indexes: id(primary)
Table2: orders
Records: 167634
DB Type: InnoDB
Indexes: id(primary), order_id, customer_id
Table3: user_registration
Records: 64000 around
DB Type: InnoDB
Indexes: id(primary), email_address
Table4: cities
Records: 931
DB Type: InnoDB
Indexes: id(primary)
Query:
SELECT uas.alert_id, uas.user_id, uas.status, ur.first_name, ur.last_name, ur.email_address, o.order_id,
CASE WHEN ct.city_name IS NULL THEN uas.city_name ELSE ct.city_name END AS city_name
FROM `user_alert` uas
LEFT JOIN orders o ON o.customer_id = uas.user_id
LEFT JOIN user_registration ur ON ur.id = uas.user_id
LEFT JOIN `cities` ct ON ct.city_id = uas.city_id
WHERE uas.status = '1'
GROUP BY uas.user_id
ORDER BY uas.create_date DESC
GROUP BY is used to aggregate values up. For example if you wanted the count of orders by a user you could use COUNT(o.order_id).....GROUP BY uas.user_id. There are multiple orders for each user, but the aggregate function is just counting them here. However if you just select o.order_id when you have a GROUP BY uas.user_id it doesn't know which of the possibly many order_id values to return for that user id.
In this case it possibly doesn't matter as it looks like the order table is the only one where there is multiple rows per use. If you want the latest one you could just use MAX(o.order_id) (assuming that the order_id is assigned is order). But if you wanted the order value it becomes more difficult.
SELECT uas.alert_id, uas.user_id, uas.status, ur.first_name, ur.last_name, ur.email_address, MAX(o.order_id) AS LatestOrderId,
IFNULL(ct.city_name, uas.city_name) AS city_name
FROM `user_alert` uas
LEFT JOIN orders o ON o.customer_id = uas.user_id
LEFT JOIN user_registration ur ON ur.id = uas.user_id
LEFT JOIN `cities` ct ON ct.city_id = uas.city_id
WHERE uas.status = '1'
GROUP BY uas.user_id
ORDER BY uas.create_date DESC
If you wanted the (say) value of the latest order then it becomes more difficult.
SELECT uas.alert_id, uas.user_id, uas.status, ur.first_name, ur.last_name, ur.email_address, Sub1.MaxOrderId AS LatestOrderId, o.order_value
IFNULL(ct.city_name, uas.city_name) AS city_name
FROM `user_alert` uas
LEFT JOIN (SELECT customer_id, MAX(order_id) AS MaxOrderId FROM orders GROUP BY customer_id) Sub1 ON Sub1.customer_id = uas.user_id
LEFT OUTER JOIN orders o ON o.customer_id = Sub1.user_id AND o.order_id = Sub1.MaxOrderId
LEFT JOIN user_registration ur ON ur.id = uas.user_id
LEFT JOIN `cities` ct ON ct.city_id = uas.city_id
WHERE uas.status = '1'
ORDER BY uas.create_date DESC
Or doing a bit of a fiddle based on GROUP_CONCAT
SELECT uas.alert_id, uas.user_id, uas.status, ur.first_name, ur.last_name, ur.email_address,
SUBSTRING_INDEX(GROUP_CONCAT(o.order_id ORDER BY o.order_id DESC), ',', 1) AS LatestOrderId,
SUBSTRING_INDEX(GROUP_CONCAT(o.order_value ORDER BY o.order_id DESC), ',', 1) AS LatestOrderValue,
IFNULL(ct.city_name, uas.city_name) AS city_name
FROM `user_alert` uas
LEFT OUTER JOIN orders o ON o.customer_id = uas.user_id AND o.order_id = Sub1.MaxOrderId
LEFT JOIN user_registration ur ON ur.id = uas.user_id
LEFT JOIN `cities` ct ON ct.city_id = uas.city_id
WHERE uas.status = '1'
GROUP BY uas.user_id
ORDER BY uas.create_date DESC

MySQL Query left join repeated records

With a Left Join i have this result.
Here the screen
http://f.cl.ly/items/373Y141r1K131d0n3f1q/Schermata%202013-04-01%20alle%2016.51.18.png
i want to show only record once time, without repeat it, but with a left join all my records are different.
what i have to do for show once all my records?
the query.
SELECT * FROM login_users
LEFT JOIN login_users_seguaci
ON login_users.user_id = login_users_seguaci.following
WHERE name LIKE ""
AND user_id != '1'
ORDER BY data DESC
SELECT x.*, y.*
FROM login_users x
LEFT JOIN
(
SELECT a.*
FROM login_users_seguaci a
INNER JOIN
(
SELECT following, MAX(DATA) max_data
FROM login_users_seguaci
GROUP BY following
) b ON a.following = b.following AND
a.DATA = b.max_date
) y ON x.user_id = y.following
// WHERE ... your condition here ...
ORDER BY t.data DESC

Categories