How to combine two query to improve performance - mysql

The system that I'm currently working on is a legacy system. The result of the first query have no issue just that the query will go through a loop to retrieve another value using the result from the first query. I try to change the query from "order by id" to "order by date" since I'm having some issue for certain account if the table is order by id. I also tried to change the query because it's currently very slow. I did combine both query together but it takes a long time to execute.
How do I join 2 query together without affecting the performance?
/* This query as I mention has no issue (first query)*/
SELECT
DATE_FORMAT('2016-12-12 00:00:00', '%Y-%m-%d') AS date,
C.id AS account_id,
C.account_no,
C.account_name,
B.amount AS last_topup,
DATE_FORMAT('2016-12-12 23:59:59', '%Y-%m-%d') AS topup_date,
NULL AS balance
FROM (SELECT
account_id,
MAX(date) date
FROM table1
GROUP BY account_id) A
INNER JOIN table1 B
USING (account_id, date)
RIGHT JOIN table2 C ON B.account_id = C.id
ORDER BY C.account_no;
/* Loop query (second query)*/
SELECT
`t`.`balance_after` AS `balance`
FROM table3 `t`
WHERE `t`.`account_id` = '<id from the loop>' AND `t`.`date` <= '2017-07-26
23:59:59'
ORDER BY `t`.`date` DESC;
/* The query that I combined both (takes a long time to execute*/
SELECT
DATE_FORMAT('2016-12-12 00:00:00', '%Y-%m-%d') AS date,
C.id AS account_id,
C.account_no,
C.account_name,
B.amount AS last_topup,
DATE_FORMAT('2016-12-12 23:59:59', '%Y-%m-%d') AS topup_date,
D.balance_after AS balance
FROM (SELECT
account_id,
MAX(date) date
FROM table1
GROUP BY account_id) A
INNER JOIN table1 B
USING (account_id, date)
RIGHT JOIN table2 C ON B.account_id = C.id
RIGHT JOIN table3 D ON B.account_id = D.account_id
WHERE D.date <= "2017-07-30 23:59:59"
ORDER BY C.account_no;

Related

mysql select doesn't include data which is compared by current timestamp

Learning to write sql queries and facing some issues.
The query:
SELECT *
FROM competitions c
INNER JOIN (
SELECT competitionId, SUM(quantity) AS quantity
FROM tickets
GROUP BY competitionId
) t ON t.competitionId = c.id
WHERE
c.winnerId IS NULL
AND t.quantity = c.maxEntries
OR
CURRENT_TIMESTAMP() >= c.endAt;
This returns only one results but should return two results.
CURRENT_TIMESTAMP() >= c.endAt;
Should also include one more record but this statement is skipped for whatever reason...
The competition table records:
The record which is marked in red square do match to the where condition. Why then it wouldn't be included?
Changing from INNER JOIN to LEFT JOIN gives desired results.
The query:
SELECT *
FROM competitions c
LEFT JOIN
(SELECT competitionId, SUM(quantity) AS quantity
FROM tickets GROUP BY competitionId
) t ON t.competitionId = c.id
WHERE
c.winnerId IS NULL AND
(t.quantity=c.maxEntries OR CURRENT_TIMESTAMP() >= c.endAt)

LEFT JOIN with GROUP BY do not return expected LEFT JOIN result

I'm trying to get all my accumulated sales for each store, even if the value is null (no order for the conditions), but the LEFT JOIN only gives me rows that have a correspondence, which does not suit me :
SELECT s.identifierExt as StoreID,
YEAR(o.creation) AS Year,
MONTHNAME(o.creation) AS Month,
MONTH(o.creation) AS IDMonth,
ROUND(SUM(o.price), 2) AS Sales
FROM store s
LEFT JOIN order o ON o.store = s.id
AND (o.creation < '2018-09-13 00:00:00')
AND (o.place NOT IN ('PENDING','CANCELLED'))
AND (o.creation > '2018-01-12 00:00:00')
GROUP BY Year, Month, StoreID
ORDER BY IDMonth, StoreID ASC
Thanks in advance.
Possible it's because of the difference between INNER and OUTER joins. The inner join requires that there is a corresponding row (match on the join condition), the outer join does not have such a requirement: it'll return columns with NULL.
It seems you want an OUTER join:
SELECT s.identifierExt as StoreID,
YEAR(o.creation) AS Year,
MONTHNAME(o.creation) AS Month,
MONTH(o.creation) AS IDMonth,
ROUND(SUM(o.price), 2) AS Sales
FROM store s
LEFT OUTER JOIN order o ON o.store = s.id
AND (o.creation < '2018-09-13 00:00:00')
AND (o.place NOT IN ('PENDING','CANCELLED'))
AND (o.creation > '2018-01-12 00:00:00')
GROUP BY Year, Month, StoreID
ORDER BY IDMonth, StoreID ASC
This way you'll also get stores without orders.
Also see this answer on the difference between inner and outer joins

Inner Join Query with MAX Date Still Returning Multiple Values for Date

I have a query that is to pull at max one entry per day per specified id. The query is returning multiple values per day despite max being specified. I need some assistance tweaking this so that I only get one entry per day. The image shows a snippet of the data that is returned.
SELECT a.*
FROM turtle_derby AS a
INNER JOIN (SELECT turtle_id, DATE(`date`) AS day_date, MAX(`date`) AS maxdate
FROM turtle_derby
GROUP BY turtle_id, day_date) AS groupedtt
ON a.turtle_id = groupedtt.turtle_id
AND a.turtle_id = '175846'
AND a.`date` = groupedtt.maxdate
AND a.`date` > '2018-07-26'
ORDER BY `a`.`date`
Your data has multiple rows are at the same time of the day. If that time is the last time of the day, you'll get all of them. You need to get the maximum of two columns, date and loc_id. See SQL : Using GROUP BY and MAX on multiple columns
SELECT a.*
FROM turtle_derby AS a
INNER JOIN (
SELECT t2.turtle_id, MAX(t2.loc_id) AS max_id
FROM (
SELECT turtle_id, MAX(date) AS maxdate
FROM turtle_derby AS
GROUP BY turtle_id, DATE(date)
) AS t1
INNER JOIN turtle_derby AS t2
ON t1.turtle_id = t2.turtle_id AND t1.maxdate = t2.date
GROUP BY t1.turtle_id, t1.maxdate
) AS groupedtt
ON a.turtle_id = groupedtt.turtle_id
AND a.loc_id = groupedtt.maxloc
WHERE a.turtle_id = '175846'
AND a.`date` > '2018-07-26'
ORDER BY `a`.`date`

Sub Query Not IN With Left Join Alternative?

I have Query Like This
SELECT f.ACCOUNT_ID,
f.TGL,
p.ACCOUNT_EMAILADDRESS
FROM distributor_kokola.forecast f
inner join distributor_kokola.push_distributor p on p.ACCOUNT_ID = f.ACCOUNT_ID
where f.ACCOUNT_ID NOT IN(
select ACCOUNT_ID
from distributor_kokola.forecast
where DATE_FORMAT(TGL, "%Y-%m") = DATE_FORMAT(CURDATE(), "%Y-%m")
group by ACCOUNT_ID
)
group by f.ACCOUNT_ID;
That Sub Query work but To Slow so I change it with Left Join, it work faster
SELECT f.ACCOUNT_ID,
f.TGL,
p.ACCOUNT_EMAILADDRESS
FROM distributor_kokola.forecast f
left join(
select ACCOUNT_ID
from distributor_kokola.forecast
where DATE_FORMAT(TGL, "%Y-%m") = DATE_FORMAT(CURDATE(), "%Y-%m")
group by ACCOUNT_ID
)subb on subb.ACCOUNT_ID = f.ACCOUNT_ID
inner join distributor_kokola.push_distributor p on p.ACCOUNT_ID = f.ACCOUNT_ID
group by f.ACCOUNT_ID;
But, My issue is Still Contain wrong Result,
With Not IN, query 1 select where NOT IN query 2.
How can I get like NOT IN query with left join.
Can Anyone Help Me?
thanks.
You have to add a WHERE clause to filter by the results of your LEFT JOIN. If you add an appropriate WHERE clause WHERE subb.ACCOUNT_ID IS NULL, it should work as expected (since you used a GROUP BY in your subquery, there's no danger of duplicate rows):
SELECT f.ACCOUNT_ID,
f.TGL,
p.ACCOUNT_EMAILADDRESS
FROM distributor_kokola.forecast f
left join(
select ACCOUNT_ID
from distributor_kokola.forecast
where DATE_FORMAT(TGL, "%Y-%m") = DATE_FORMAT(CURDATE(), "%Y-%m")
group by ACCOUNT_ID
) subb on subb.ACCOUNT_ID = f.ACCOUNT_ID
inner join distributor_kokola.push_distributor p on p.ACCOUNT_ID = f.ACCOUNT_ID
WHERE sub.ACCOUNT_ID IS NULL
group by f.ACCOUNT_ID;
Update
The goal of our LEFT JOIN is to find all rows in our forecast table that don't have a matching row in the subquery. Therefore, we need a WHERE clause that removes all rows with a matching row - WHERE sub.ACCOUNT_ID IS NULL fits quite nicely.
SO user #quassnoi has written a wonderful comparison of different methods to achieve this goal.

MySQL Query not displaying correctly

I am having to set up a query that retrieves the last comment made on a customer, if no one has commented on them for more than 4 weeks. I can make it work using the query below, but for some reason the comment column won't display the latest record. Instead it displays the oldest, however the date shows the newest. It may just be because I'm a noob at SQL, but what exactly am I doing wrong here?
SELECT DISTINCT
customerid, id, customername, user, MAX(date) AS 'maxdate', comment
FROM comments
WHERE customerid IN
(SELECT DISTINCT id FROM customers WHERE pastdue='1' AND hubarea='1')
AND customerid NOT IN
(SELECT DISTINCT customerid FROM comments WHERE DATEDIFF(NOW(), date) <= 27)
GROUP BY customerid
ORDER BY maxdate
The first "WHERE" clause is just ensuring that it shows only customers from a specific area, and that they are "past due enabled". The second makes sure that the customer has not been commented on within the last 27 days. It's grouped by customerid, because that is the number that is associated with each individual customer. When I get the results, everything is right except for the comment column...any ideas?
Join much better to nested query so you use the join instead of nested query
Join increase your speed
this query resolve your problem.
SELECT DISTINCT
customerid,id, customername, user, MAX(date) AS 'maxdate', comment
FROM comments inner join customers on comments.customerid = customers.id
WHERE comments.pastdue='1' AND comments.hubarea='1' AND DATEDIFF(NOW(), comments.date) <= 27
GROUP BY customerid
ORDER BY maxdate
I think this might probably do what you are trying to achieve. If you can execute it and maybe report back if it does or not, i can probably tweak it if needed. Logically, it ' should' work - IF i have understood ur problem correctly :)
SELECT X.customerid, X.maxdate, co.id, c.customername, co.user, co.comment
FROM
(SELECT customerid, MAX(date) AS 'maxdate'
FROM comments cm
INNER JOIN customers cu ON cu.id = cm.customerid
WHERE cu.pastdue='1'
AND cu.hubarea='1'
AND DATEDIFF(NOW(), cm.date) <= 27)
GROUP BY customerid) X
INNER JOIN comments co ON X.customerid = co.customerid and X.maxdate = co.date
INNER JOIN customer c ON X.customerid = c.id
ORDER BY X.maxdate
You need to have subquery for each case.
SELECT a.*
FROM comments a
INNER JOIN
(
SELECT customerID, max(`date`) maxDate
FROM comments
GROUP BY customerID
) b ON a.customerID = b.customerID AND
a.`date` = b.maxDate
INNER JOIN
(
SELECT DISTINCT ID
FROM customers
WHERE pastdue = 1 AND hubarea = 1
) c ON c.ID = a.customerID
LEFT JOIN
(
SELECT DISTINCT customerid
FROM comments
WHERE DATEDIFF(NOW(), date) <= 27
) d ON a.customerID = d.customerID
WHERE d.customerID IS NULL
The first join gets the latest record for each customer.
The second join shows only customers from a specific area, and that they are "past due enabled".
The third join, which uses LEFT JOIN, select all customers that has not been commented on within the last 27 days. In this case,only records without on the list are selected because of the condition d.customerID IS NULL.
But tomake your query shorter, if the customers table has already unique records for customer, then you don't need to have subquery on it.Directly join the table and put the condition on the WHERE clause.
SELECT a.*
FROM comments a
INNER JOIN
(
SELECT customerID, max(`date`) maxDate
FROM comments
GROUP BY customerID
) b ON a.customerID = b.customerID AND
a.`date` = b.maxDate
INNER JOIN customers c
ON c.ID = a.customerID
LEFT JOIN
(
SELECT DISTINCT customerid
FROM comments
WHERE DATEDIFF(NOW(), date) <= 27
) d ON a.customerID = d.customerID
WHERE d.customerID IS NULL AND
c.pastdue = 1 AND
c.hubarea = 1
Two of your table columns are not contained in either an aggregate function or the GROUP BY clause. for example suppose that you have two data rows with the same customer id and same date, but with different comment data. how SQL should aggregate these two rows? :( it will generate an error...
try this
select customerid, id, customername, user,date, comment from(
select customerid, id, customername, user,date, comment,
#rank := IF(#current_customer = id, #rank+ 1, 1),
#current_customer := id
from comments
where customerid IN
(SELECT DISTINCT id FROM customers WHERE pastdue='1' AND hubarea='1')
AND customerid NOT IN
(SELECT DISTINCT customerid FROM comments WHERE DATEDIFF(NOW(), date) <= 27)
order by customerid, maxdate desc
) where rank <= 1