I'm trying to understand what's the purpose of the join in this query.
SELECT
DISTINCT o.order_id
FROM
`order` o,
`order_product` as op
LEFT JOIN `provider_order_product_status_history` as popsh
on op.order_product_id = popsh.order_product_id
LEFT JOIN `provider_order_product_status_history` as popsh2
ON popsh.order_product_id = popsh2.order_product_id
AND popsh.provider_order_product_status_history_id <
popsh2.provider_order_product_status_history_id
WHERE
o.order_id = op.order_id
AND popsh2.last_updated IS NULL
LIMIT 10
What bothering me is that provider_order_product_status_history has joined 2 times and I'm not sure the purpose of it. Highly appreciate if someone can help
It's a technique to retrieve the latest order status.
Because of
AND popsh.provider_order_product_status_history_id < popsh2.provider_order_product_status_history_id
and
AND popsh2.last_updated IS NULL
Only those order status that doesn't have any newer status are returned.
For a minimum set example, consider the following status history table:
id status order_id last_updated
--------------------------------
1 A X 1:00
2 B X 2:00
The self join will result in:
id status order_id last_updated id status order_id last_updated
-------------------------------- --------------------------------
1 A X 1:00 2 B X 2:00
2 B X 2:00 NULL NULL NULL
The first row will be filtered out by the IS NULL condition, leaving only the second raw, which is the latest one.
For a 3-row case the self join result will be:
id status order_id last_updated id status order_id last_updated
-------------------------------- --------------------------------
1 A X 1:00 2 B X 2:00
1 A X 1:00 3 C X 3:00
2 B X 2:00 3 C X 3:00
3 C X 3:00 NULL NULL NULL
And only the last one will pass the IS NULL condition, leaving the latest one again.
It looks like an unnecessarily complicated way to do the job, but it actually works quite well as RDBMS engines do joins very efficiently.
BTW, as the query retrieves only order_id, the query is not useful as it is. I guess the OP omitted other fields in the select clause. It should be something like SELECT o.order_id, popsh.* FROM ...
Wait, you have an error:
SELECT
DISTINCT o.order_id
FROM
`order` o,
`order_product` as op
LEFT JOIN `provider_order_product_status_history` as popsh
on op.order_product_id = popshs.order_product_id
** YOU HAVE EXCESS 's' HERE ^
LEFT JOIN `provider_order_product_status_history` as popsh2
ON popsh.order_product_id = popsh2.order_product_id
AND popsh.provider_order_product_status_history_id < popsh2.provider_order_product_status_history_id
WHERE
o.order_id = op.order_id
AND popsh2.last_updated IS NULL
LIMIT 10
Based from my analysis, the query is trying to extract the first o.order_id or first entry (based on provider_order_product_status_history.provider_order_product_status_history_id) of the provider_order_product_status_history. However, the joins semantic used in this query is not recomendable.
Both joins being an inner-kind restrict resultset on conditions. It's like "gimme only values from table 1 that have corresponding row in table 2 on condition1 and at the same time a row in table2 on condition2".
Related
i have 2 table which is one to many
table order
order_id
order_date
1
2021/01/01
2
2021/01/02
3
2021/01/02
table detail order
detail_order_id
order_id
is_finished
1
1
null
2
1
2021/01/03
3
2
2021/01/04
4
2
2021/01/04
5
3
2021/01/05
6
3
2021/01/06
7
3
null
so i wanna data that have condition if some of the detail order rows is_finished column not null, so the status is gonna be not finish.
and if all the detail order rows is_finished column not contain any null value like id 2, so the status is finished
expected result
order_id
status
1
not finish
2
finished
3
not finish
It seems like you don't really need a join since table_detail_order already have order_id and you only want to check is_finished, you might just need a query on 1 table like:
SELECT order_id,
CASE WHEN SUM(is_finished IS NULL)=0
THEN 'Finished' ELSE 'Not finish' END AS 'Status'
FROM table_detail_order GROUP BY order_id;
Demo fiddle
Btw, is_finished IS NULL will return 1 (true) or 0 (false) so in a table it would look like:
order_id
is_finished
is_finished IS NULL
1
null
1
1
2021/01/03
0
2
2021/01/04
0
2
2021/01/04
0
3
2021/01/05
0
3
2021/01/06
0
3
null
1
Therefore SUM(is_finished IS NULL) with GROUP BY order_id; will do something like this:
order_id
SUM(is_finished IS NULL)
1
1+0=1
2
0+0=0
3
0+0+1=1
And that is why CASE WHEN SUM(is_finished IS NULL)=0 ... is considered as finished while otherwise as not finish.
we can solve the problem like this
left join order and order_detail,but has condition order_details.is_finished is null
so we get a result that the joined order_details's is_finished only null
in that case there is no order_details join with order 2
then we regard the result as a temp table,so when joined order_details has data then it is not finished
here is the example data,you can run query in it
select id,
case when order_id>0 then 'not finish' else 'finished' end as status
from (
select o.id,od.order_id from `order` as o
left join order_detail as od
on (o.id=od.order_id and od.is_finished is null)
group by o.id
) as _t
You can try this. This query uses a LEFT JOIN and COUNT. Where the first count counted the NULL values as ZERO and the second count counts all values, then compare the 2 counts, if the first and second count is equal to each other it means that the order details is finished, if not then not finish.
SELECT a.`order_id`,
IF(COUNT(IF(ISNULL(is_finished),0,1))=COUNT(is_finished), 'finished', 'not finish') AS `status` FROM `order` a
LEFT JOIN `detail_order` b ON a.`order_id`=b.`order_id`
GROUP BY a.`order_id` ;
RESULT*
order_id status
-------- ------------
1 not finish
2 finished
3 not finish
I have two tables like this
rooms
id | number
1 | 111
2 | 112
occupied_rooms
id | check_in | check_out | room_id
1 | 2017-10-01 | 2017-10-04 | 1
I want to get all the unoccupied rooms according to date check_in and check_out for this I tried
select r.id
, r.number
from rooms r
left join occupied_rooms o
on r.id = o.room_id
where (o.check_in not between "2017-10-05" and "2017-10-08" )
or (o.check_in >= "2017-10-05" and o.check_out <= "2017-10-08"))
but this query giving me result like this. which is incorrect.
id | number
1 | 111
What is wrong with this query?
Thank you for your any help and suggestions
Just join the two tables on the condition that the id matches and the range of intended stay overlaps with the range in the occupied_rooms table.
SELECT r.*
FROM rooms r
LEFT JOIN occupied_rooms t
ON r.id = t.id AND
('2017-10-02' <= t.check_out AND '2017-10-03' >= t.check_in)
WHERE
t.id IS NULL; -- NULL value indicates the room did not overlap
-- with any existing reservations
You can also check out this great reference question on how to deal with overlapping ranges in SQL queries. It makes the problem you are facing much simpler.
Demo
Your data in table occupied_rooms meets the first condition in "where";
check_in date(2017-10-01) is not between "2017-10-05" and "2017-10-08" and your where is or.
Thus, the result is included this data.
Can you tell us what output you expect?
I have two tables:
booking - records the order detail
id | booking_amount
-------------------
1 | 150
2 | 500
3 | 400
payment - records the payment for order
id | booking_id | amount
------------------------
1 | 1 | 100
2 | 1 | 50
2 | 2 | 100
I want to find all bookings where the payments are not complete. With the above data, we expect the answer to be 2,3, because the sum of payments for booking_id=1 matches the corresponding booking_amount in the booking_table.
To answer your question, you have 2 things you need to think about :
you want the total amount in your table payment by every booking row
you want to join your booking_amount table with payment.
Part 1 is quite simple:
SELECT sum(amount) as TotalP, booking_id FROM payment GROUP BY booking_id
Just a basic query with a simple aggregate function...
For part 2, we want to join booking_amount and payment; the basic JOIN would be:
SELECT * FROM booking b
LEFT JOIN payment p ON b.id = p.booking_id
We do a LEFT JOIN because we may have some booking who are not in the payment table. For those bookings, you will get NULL value. We will use a COALESCE to replace the NULL values by 0.
The final query is this:
SELECT b.id, COALESCE(TotalP, 0), b.booking_amount
FROM
booking b
LEFT JOIN
(SELECT sum(amount) as TotalP, booking_id FROM payment GROUP BY booking_id) as T
ON b.id = T.booking_id
WHERE COALESCE(TotalP, 0) < b.booking_amount
You need to use a outer join to combine your two tables and look for your conditions. Also, you will need to use SUM(..) function to get the sum of the amount for each id in the payment table.
Please try this:
select b.id from booking b
left outer join -- cant be inner join because we lose id:3 in that case.
(
select booking_id, SUM(amount) as Total
from payment group by booking_id
) p on b.id = p.booking_id
where b.booking_amount > Coalesce(Total,0) --Coalesce is required for such values coming NULL, like id:3, they will be assigned as 0.
My goal is to retrieve the recorded purchase price for an item on an accepted purchase order.
Purchase_Orders table contains metadata for the order, such as the order number and its status (e.g., 1 for accepted, 0 for declined).
Purchase_Ord_Contents table contains contents records, which are linked via foreign key to the parent purchase order on a shared index order_number)
For example: I have two orders in my database, one has been accepted and the other has been declined. The data is represented as follows:
=========================================
PURCHASE_ORDERS TABLE
=========================================
id | order_number | order_status
-----------------------------------------
1 PO_100 0
2 PO_101 1
3 PO_102 1
===================================================
PURCHASE_ORD_CONTENTS TABLE
===================================================
id | order_number | purchase_price | sku
---------------------------------------------------
1 PO_100 1.50 APPLE
2 PO_100 1.50 ORANGE
3 PO_101 2.00 APPLE
4 PO_101 2.00 ORANGE
5 PO_102 1.75 BANANA
The query should return rows 3, 4 and 5, since PO_101 was accepted, whereas PO_100 was declined and row 5 is not only the only record for the given SKU, it was also on an accepted order. I've tried a few different approaches, but I always seem to end up either leaving out parts that were on an unaccepted Purchase Order, or retrieving the wrong order_number for the lowest purchase_price.
Here is what I have thus far (not working properly)
SELECT a.*
FROM purchase_ord_contents AS a
JOIN (SELECT sku,
MIN(purchase_price) AS min_price
FROM purchase_ord_contents
GROUP BY sku) AS b
ON ( a.sku = b.sku
AND a.purchase_price = b.min_price )
WHERE a.order_number
IN (
SELECT order_number
FROM purchase_orders
WHERE order_status != 0
)
This query successfully returns the records from the purchase_ord_contents table, however it omits records of the lowest purchase_price that were on a Purchase Order with an order_status of 0.
Any guidance would be greatly appreciated, I am not very well versed in "advanced" SQL queries as you have probably determined by now. Thank you for your time and please do not hesitate to ask if I should provide any further information.
This could be what you are looking for:
SELECT sku, purchase_price, order_number
FROM (
SELECT MIN(purchase_price) AS purchase_price, sku
FROM purchase_ord_contents
JOIN purchase_orders USING (order_number)
WHERE purchase_orders.order_status = 1
GROUP BY sku
) AS min_sku_price -- this is the lowest sale price for each SKU
JOIN purchase_ord_contents USING (sku, purchase_price) -- gets all orders having sold a SKU at its lowest price
JOIN purchase_orders USING (order_number)
WHERE purchase_orders.order_status = 1
Notice this will return several rows for one given SKU if the lowest price for this SKU was offered in several orders.
If I understand correctly I think you want this:
SELECT po.order_number, poc.sku, min(poc.purchase_price)
FROM purchase_orders AS po
JOIN purchase_ord_contents AS poc ON poc.order_number = po.order_number
WHERE po.order_status != 0
GROUP by po.order_number, poc.sku
order by po.order_number, poc.sku
I am struggling with the appropriate query to find duplicates while at the same time respecting the effective start and end dates for the record. Example data below.
ClientName ClientID EffectiveStart EffectiveEnd
A 1 1900-01-01 2100-01-01
A 1 1900-01-01 2100-01-01
B 2 1900-01-01 2012-05-01
C 2 2012-05-01 2100-01-01
D 3 1900-01-01 2012-05-01
E 3 2012-04-30 2100-01-01
F 4 2012-04-15 2100-01-01
The output I am looking for is below.
ClientName ClientID
A 1
D 3
E 3
The logic is that Client A has ID 1 duplicated. Client B and C have a duplicate (2) BUT the date ranges are such that the two duplicates DO NOT overlap each other, which means they should not be considered duplicates. Client D and E have ID 3 duplicated AND the date ranges overlap so it should be considered a duplicate. Client F does not have a duplicate so should not show in the output.
Any thoughts? Questions?
There are two versions. Exists is simpler but likely slower than join. Exists checks for each record if there is an overlapping record per same clientid; it is bound to find at least one, itself, hence group by and having.
select distinct ClientName, ClientID
from Table1
where exists
(
select null
from table1 test1
where test1.clientid = table1.clientid
and test1.EffectiveStart < table1.EffectiveEnd
and test1.EffectiveEnd > table1.EffectiveStart
group by test1.ClientID
having count (*) > 1
)
Join does the same, but as grouping is done on all records its having has to count them all.
select test1.clientid
from table1 test1
join table1 test2 on test1.clientid = test2.clientid
where test1.EffectiveStart < test2.EffectiveEnd
and test1.EffectiveEnd > test2.EffectiveStart
group by test1.clientid
having count (*) > (select count (*)
from table1
where clientid = test1.clientid)
I omitted retrieval of clientname because people hate to see nested queries.
Live test is at Sql Fiddle.
Will need a PK
select c1.name, c2.name, c1.id
from client c1
join client c2 on c1.id = c2.id and c1.PK < c2.PK
where c1.Start > c2.End or c1.End < c2.Start
Determine Whether Two Date Ranges Overlap please give him a +1