Mysql how to put a limit on a table with join - mysql

I want to LIMIT timber orders query to 10 for pagination. The problem i'm having is the LEFT JOIN timber_order_products is counted with the LIMIT. The LIMIT should only apply to timber_orders.
$data = $DB2->query("
SELECT o.order_id
, o.order_status
, o.customer_method
, o.payment_method
, o.customer_notes
, o.order_date
, p.name product_name
, p.price product_price
FROM timber_orders o
LEFT
JOIN timber_order_products p
ON p.order_id = o.order_id
ORDER
BY order_id DESC
LIMIT 10
");
How do i LIMIT results only from a specific table? The LIMIT 10 should only apply to timber_orders. I can't group by because i need the timber_order_products rows.

You can use a subquery:
SELECT o.order_id , o.order_status, o.customer_method, o.payment_method,
o.customer_notes, o.order_date,
p.name as product_name, p.price as product_price
FROM (select o.*
from timber_orders o
order by order_id desc
limit 10
) o LEFT JOIN
timber_order_products p
on p.order_id = o.order_id
ORDER BY o.order_id DESC
LIMIT 10

Avoiding using a sub query, I would use GROUP_CONCAT. Then the products name and price can be split off in the code that displays the data:-
SELECT o.order_id
, o.order_status
, o.customer_method
, o.payment_method
, o.customer_notes
, o.order_date
, GROUP_CONCAT(CONCAT_WS('~', p.name, p.price)) AS product_name_price
FROM timber_orders o
LEFT OUTER JOIN timber_order_products p
ON p.order_id = o.order_id
GROUP BY o.order_id
, o.order_status
, o.customer_method
, o.payment_method
, o.customer_notes
, o.order_date
ORDER BY order_id DESC
LIMIT 10

Related

Why SQL where greater than is doesn't work?

I tried to select data where total greater than 3, but is not work, how to fix it?
SQL
SELECT p.image, p.id, p.name, sum(od.qty) AS total, sum(od.price * od.qty) AS nilai
FROM products p, order_details od, orders o
WHERE p.id = od.product_id
AND o.id = od.order_id
AND o.status = "Finished"
AND total > 6
GROUP BY p.id
ORDER BY nilai DESC
LIMIT 10
result
Total 6 still there. Any Advice?
Use having group by :
SELECT p.image, p.id, p.name, sum(od.qty) AS total, sum(od.price * od.qty) AS nilai
FROM products p, order_details od, orders o
WHERE p.id = od.product_id
AND o.id = od.order_id
AND o.status = "Finished"
GROUP BY p.id
HAVING sum(od.qty) > 3
ORDER BY nilai DESC
LIMIT 10

Sub query in join with 3rd table

My Current Join Query
select c.email
, c.name
, o.created
, o.customerId
, o.code as order_code
, o.totalValue
,'cash' as description
, o.WalletCash as amount
from Order o join Customer c on o.customerId = c.id
where o.WalletCash > 0
and o.created BETWEEN '2022-03-16' AND '2022-03-16'
I want to make join or sub query with below query
select customerId from CustomerWallet where customerId =100
The Customer, CustomerWalletand Order tables are all associated by CustomerID.
How join 3rd table in join query in mysql?
You can alias the first query and use that with the second table.
select * from (select c.email
, c.name
, o.created
, o.customerId
, o.code as order_code
, o.totalValue
,'cash' as description
, o.WalletCash as amount
from Order o join Customer c on o.customerId = c.id
where o.WalletCash > 0
and o.created BETWEEN '2022-03-16' AND '2022-03-16') tab t join CustomerWallet c on t.order_code = c.order_code and c.order_code = like "%abc%"
or you can join all three tables together.
select c.email
, c.name
, o.created
, o.customerId
, o.code as order_code
, o.totalValue
,'cash' as description
, o.WalletCash as amount
from Order o join Customer c on o.customerId = c.id
join CustomerWallet w on w.order_code = order_code
where o.WalletCash > 0
and o.created BETWEEN '2022-03-16' AND '2022-03-16'
and w.order_code like "%abc%"
If you want to restrict the first query's result to customer IDs found in the second query, then use IN:
select c.email
, c.name
, o.created
, o.customerId
, o.code as order_code
, o.totalValue
,'cash' as description
, o.WalletCash as amount
from Order o join Customer c on o.customerId = c.id
where o.WalletCash > 0
and o.created BETWEEN '2022-03-16' AND '2022-03-16'
and c.id in
(
select customerId
from CustomerWallet
where order_code like '%abc%'
);
I see that there is an order_code in CustomerWallet. There is a code in your order table, too. Maybe you want to consider this in your lokup. Probably:
and (o.code, c.id) in
(
select cw.order_code, cw.customerId
from CustomerWallet cw
where cw.order_code like '%abc%'
);
IN and EXISTS are appropriate for such lookups. As opposed to a join they just ensure a match exists. With joins you could involuntarily produce duplicate rows in case the second itself returns the join criteria multifold.

How to get unique records with join tables and without using group by

I want to get unique record from 2 tables without using Group By instead of i want to use DISTINCT because when i used group by it does not return last updated records.
I used following query with Group By.
SELECT au.id, o.order_id,o.payment_method, o.order_date,au.article_id FROM `orders` as o INNER JOIN austpost_api_response as au ON o.order_id = au.order_id where o.shipment_status = 1 AND au.active_status = 1 AND o.order_status = 0 group by o.order_id
ORDER BY `au`.`id` DESC
It's returns unique order_id but not getting last updated order_id
For example...
Order number 1212 has four article_id id 45,76,47,48 but once i used this with group by it's display unique order id but not getting 48 last updated record instead of it's return 45.
is it possible to get with DISTINCT keyword.
You can use row_number() function if your mysql version 8.0+
select * from
(
SELECT au.id, o.order_id,o.payment_method, o.order_date,au.article_id,row_number() over(partition by o.order_id order by o.order_date desc) as rn
FROM `orders` as o INNER JOIN austpost_api_response as au ON o.order_id = au.order_id where o.shipment_status = 1 AND au.active_status = 1 AND o.order_status = 0
)A where rn=1
OR You can try using correlated subquery
SELECT au.id, o.order_id,o.payment_method, o.order_date,au.article_id
FROM `orders` as o INNER JOIN austpost_api_response as au ON o.order_id = au.order_id
where o.shipment_status = 1 AND au.active_status = 1 AND o.order_status = 0 and o.order_date in
(
select max(o.order_date) from `orders` o1 where o.order_id = o1.order_id and o1.order_status = 0
)
If I understand correctly, you have multiple rows for the responses for each order -- and you want the most recent one per order.
You want to filter the data, not aggregate it. I think this does what you want:
SELECT au.id, o.order_id, o.payment_method, o.order_date, au.article_id
FROM orders o INNER JOIN
austpost_api_response au
ON o.order_id = au.order_id
WHERE o.shipment_status = 1 AND
au.active_status = 1 AND
o.order_status = 0 AND
au.id = (SELECT MAX(au2.id)
FROM austpost_api_response au2
WHERE au2.order_id = au.order_id AND
au2.active_status = 1
)
ORDER BY au.id DESC;

Mysql sub select with SUM() and group by

I am trying to create a query that does a calculation in a subquery that requires the SUM function and a group by. My query returns the error "Subquery returns more than 1 row". Essentially I am trying to return the amount "Due" for each order. If the order total is greater than the sum of total_collected (for that order_id) from the payments table there will be amount due. Here is the query:
SELECT o.order_id
, o.server
, o.subtotal
, o.discount
, o.tax, o.total
, (SELECT (o.total - SUM(p.total_collected))
from orders o
join payments p
on o.order_id = p.order_id
group by p.order_id) as 'Due'
FROM orders o
join payments p
on o.order_id = p.order_id
WHERE...;
I cannot include 'p.order_id' in the sub select because it should only contain one column. I understand why I am getting the error, I just don't know how to get the sub select to only perform the SUM on a per order_id basis.
Without changing the structure much, I think the subquery is looking at all of the data in the orders/payments tables. I think you need to filter it down to look only at the relevant order_id like so.
(I also added a SUM around the order total because I am pretty sure that would give a different error without it.)
SELECT o.order_id
, o.server
, o.subtotal
, o.discount
, o.tax
, o.total
, (SELECT (SUM(o2.total) - SUM(p.total_collected))
from orders o2
JOIN payments p
ON o2.order_id = p.order_id
WHERE o2.order_id = o.order_id) as 'Due'
FROM orders o
WHERE...;
Although, if you adjust this so that it uses a join instead of a subquery, I think you will get better performance. Something like this:
SELECT o.order_id
, o.server
, o.subtotal
, o.discount
, o.tax
, o.total
, o.total - c.Collected AS 'Due'
FROM orders o
JOIN (
SELECT p2.order_id, SUM(p2.total_collected) AS 'Collected'
FROM payments p2
GROUP BY p2.order_id) AS c
ON o.order_id = c.order_id
WHERE...;
You do not need sub-query:
SELECT
o.order_id,
o.server,
o.subtotal,
o.discount,
o.tax,
o.total,
o.total - ifnull(sum(p.total_collected),0) As Due
FROM orders AS o
LEFT JOIN payments AS p ON o.order_id = p.order_id
WHERE ...
GROUP BY o.order_id

Looking for an alternative to a T-SQL Sub-Query

Account
=======
int AccountId PK
Order
=====
int OrderId PK
int AccountId FK
DateTime Date
int Status
For each account I want to know the most recent order that has a status of 1 (Success) otherwise the most recent order that has a status of 0 (Unsuccessful). Date is not unique.
I've got this working with a correlated sub-query in a view like this...
SELECT
a.AccountId,
(SELECT TOP 1
o.orderId
FROM
tbl_order o
WHERE
o.Accountid = a.AccountId
AND
o.Status IN (0, 1)
ORDER BY
o.Status DESC, o.Date DESC
) AS OrderId
FROM
tbl_account a
...but its slow.
Is there a better way?
You could use a CTE with ROW_NUMBER function:
WITH cte AS(
SELECT a.AccountId, o.OrderId, o.Date, o.Status
, RN = ROW_NUMBER()OVER(Partition By a.AccountId ORDER BY o.Status DESC, o.Date DESC)
FROM Account a
LEFT OUTER JOIN [Order] o ON a.AccountId = o.AccountId
)
SELECT AccountId, OrderId
FROM cte
WHERE RN = 1
Here's a fiddle: http://sqlfiddle.com/#!3/4e1e3/4/0
Try this:
SELECT
a.AccountId,
o.OrderId
FROM
tbl_account a OUTER APPLY
(
SELECT TOP 1 o.orderId
FROM tbl_order o
WHERE o.Accountid = a.AccountId
AND o.Status IN (0, 1)
ORDER BY o.Status DESC, o.Date DESC
) AS o