MySQL : exclude rows where id appears in two separate columns of table - mysql

I have a table that looks like this:
| id | order_id | product_id | category_id |name | cost | returned_product_id |
| 3100 | 900 | 0125 | 3 | Foo | 14 | NULL |
| 3101 | 901 | 0145 | 3 | Bar | 10 | NULL |
| 3102 | 901 | 2122 | 3 | Baz | 11 | NULL |
| 3103 | 900 | 0125 | 3 | Foo | -14 | 3100 |
| 3104 | 902 | 0125 | 3 | Foo | 14 | NULL |
| 3105 | 902 | 0125 | 3 | Foo | -14 | 3104 |
| 3106 | 903 | 0125 | 3 | Foo | 14 | NULL |
The id is a single line item of an order where product_id was included. If the product is returned, a new line item is created with a new id. There is one of each product, and it is possible to repurchase a returned item again, and return it again.
I'm joining the table data with data from other tables given certain conditions. As a final condition, I am attempting to exclude any line items that were originally returned. This is in attempt to perform a single query that essentially gives me all product_ids ever purchased and that have not been returned, like this:
select product_id
from orders o,
line_items i
where o.state = 'paid'
and o.id = i.order_id
and i.category_id = 3
and i.product_id not in (select li.returned_product_id
from line_items li
where li.refunded_product_id is not null
and li.product_id = 3)
Even though I have indexes on both the id and returned_product_id, the query above is really slow (thousands of lines), where if my subselect queried for the id, it's fast.

If your query is from the table that you exposed the content, the line:
and i.id not in (select li.returned_product_id
will look in the id of the table and not the id of the product, wright?
That should be
and i.product_id not in (select li.returned_product_id

something like:
select distinct i.product_id from
line_items i left join line_items j
on i.product_id = j.refunded_product_id
where j.refunded_product_id is null
?

Related

How Affect Group By to Other Second Join Table

I have some table like this
table request_buys
| id | invoice | user_id |
| -- | ----------------- | ------- |
| 3 | 20220405/01104298 | 1 |
table traces
| id | request_buy_id | status_id | created_at |
| -- | -------------- | --------- | ------------------- |
| 37 | 3 | 1 | 2022-03-27 14:12:25 |
| 38 | 3 | 2 | 2022-03-28 14:12:25 |
| 39 | 3 | 3 | 2022-03-29 14:12:25 |
| 40 | 3 | 4 | 2022-03-30 14:12:25 |
| 41 | 3 | 5 | 2022-03-31 14:12:25 |
| 42 | 3 | 6 | 2022-04-01 14:12:25 |
table statuses
| id | nama |
| -- | ----------------- |
| 1 | Order Placed |
| 2 | Order Paid |
| 3 | Accepted |
| 4 | Picked by Courier |
| 5 | In Transit |
| 6 | Delivered |
| 7 | Rated |
| 8 | Rejected |
| 9 | Canceled |
and then i try to design query like below
select
request_buys.invoice,
MAX(traces.id) as traces_id,
MAX(statuses.nama) as statuses_nama
from
`request_buys`
inner join `traces` on `request_buys`.`id` = `traces`.`request_buy_id`
inner join `statuses` on `traces`.`status_id` = `statuses`.`id`
where
`user_id` = 1
group by
request_buys.id
and produces output like the following
output
| invoice | traces_id | statuses_nama |
| ----------------- | --------- | ----------------- |
| 20220405/01104298 | 42 | Picked by Courier |
and the output i expect should be like in the table below
expect
| invoice | traces_id | statuses_nama |
| ----------------- | --------- | ----------------- |
| 20220405/01104298 | 42 | Delivered |
I understand my error is in MAX(statuses.nama) which I should change like removing MAX() in statuses.nama
But i just get error like this "SELECT list is not in GROUP BY clause and contains nonaggregated ... this is incompatible with sql_mode=only_full_group_by"
then I tried some to clear the value "ONLY_FULL_GROUP_BY" with a query like the following
SET sql_mode=(SELECT REPLACE(##sql_mode,'ONLY_FULL_GROUP_BY',''))
and the result is like this
output
| invoice | traces_id | statuses_nama |
| ----------------- | --------- | ----------------- |
| 20220405/01104298 | 42 | Order Placed |
and I'm really stuck at this
and how to make trace_id.status_id from the "GROUP BY" result based on request_buys.id still have a relationship with statuses.id
Your problem lies with your misuse of the MAX(statuses.nama) expression. Based on your expected output,you intend to get the statuses.nama which matches the MAX(traces.id), NOT the MAX(statuses.nama) value which returns the highest value in terms of alphabetic order. In this case, the initial letter 'P' > 'D' . I have tweaked your code a bit and tried it on workbench,supposing there are more than one invoice for a particular user.(e.g insert into request_buys values (4,'20230405/01104298',1); insert into traces values (43,4,7,'2022-04-01 14:12:25');) It works as intended.
select invoice, t.id as traces_id, s.nama as statuses_name from request_buys r
join traces t on r.id=t.request_buy_id
join statuses s on t.status_id=s.id
join
(select traces.request_buy_id, MAX(traces.id) as traces_id
from `request_buys`
inner join `traces` on `request_buys`.`id` = `traces`.`request_buy_id`
where
`user_id` = 1
group by
traces.request_buy_id ) join_t
on t.request_buy_id=join_t.request_buy_id and t.id=join_t.traces_id
;
If I'm understanding correctly, you're trying to retrieve the most recent status for each invoice. Using MAX(nama) won't return that result, because it just picks the maximum status name alphabetically.
Assuming you're using MySQL 8.x, use ROW_NUMBER() to sort and rank the statuses for each invoice, by the most recent date first. Then grab the latest one using where rowNum = 1
WITH cte AS (
SELECT rb.id AS request_buy_id
, rb.invoice
, t.id AS traces_id
, s.nama AS statuses_nama
, ROW_NUMBER() OVER(PARTITION BY rb.id ORDER BY t.created_at DESC) AS RowNum
FROM request_buys rb
INNER JOIN traces t ON rb.id = t.request_buy_id
INNER JOIN statuses s ON t.status_id = s.id
WHERE user_id = 1
)
SELECT *
FROM cte
WHERE RowNum = 1
;
Result:
request_buy_id
invoice
traces_id
statuses_nama
RowNum
3
20220405/01104298
42
Delivered
1
db<>fiddle here

Query based on 2 tables where duplicate id's amounts are calculated

I Have 2 tables, table ONE consist of an category_id where each has its own not unique name attribute and each product_id would be unique in this table with no duplicates.
Table TWO consist of unique product_id just like table ONE and rank based on the biggest amount.
Table : ONE
+--------+---------+------------+--------+
| brd_id | cat_id | product_id | name |
+--------+---------+------------+--------+
| 1 | 1 | 333 | w |
| 2 | 1 | 444 | w |
| 3 | 1 | 555 | w |
| 4 | 2 | 666 | y |
| 5 | 2 | 777 | y |
| 6 | 3 | 888 | t |
+--------+---------+------------+--------+
Table: TWO
+--------+---------+------------+--------+--------+
| new_id | type | product_id | rank | amount |
+--------+---------+------------+--------+--------+
| 1 | all | 333 | 1 | 80 |
| 2 | all | 444 | 2 | 70 |
| 3 | all | 555 | 3 | 60 |
| 4 | all | 666 | 4 | 50 |
| 5 | all | 777 | 5 | 40 |
| 6 | all | 888 | 6 | 30 |
+--------+---------+------------+--------+--------+
What i want is to show name from table ONE only once based on the amount from table TWO WHERE in table ONE product_id has the same cat_id.
This i was able to achieve using this query basically straight from database so fields differ:
SELECT DISTINCT `cat`.`brand_name`
FROM `ps_product_brand` AS `cat`
INNER JOIN `ps_product_custom_statistics_weekly` AS `cat_p`
ON `cat_p`.`id_product` = `cat`.`id_product`
WHERE `cat_p`.`type` LIKE '%all%'
AND `cat_p`.`rank` <= 16
ORDER BY `cat_p`.`rank` ASC
PROBLEM
I get duplicate names, so i used DISTINCT which in the end ignores the duplicate values.
I want to calculate the amount where the product_id from table ONE is IN table TWO AND where they have the same category_id in table ONE.
EXPECTED OUTPUT
+---------+--------+
| cat_id | name | amount
+---------+--------+
| 1 | w | 210
| 2 | y | 90
| 3 | t | 30
+--------+---------+
Hope it makes sense. THANKS
I don't have a fiddle to work with, but I think this is right.
It MIGHT have typos.
The general idea is that we do the distinct FIRST nest it. Then, once we get the distinct, then we get the SUM of the distinct elements from the secondary table. The reason we do it this way is because otherwise the SUM (if done in the inside) would join multiple records and get an amount that is too high.
SELECT A.CAT_ID, A.BRAND_NAME,
(SELECT SUM(AMOUNT) FROM ps_product_custom_statistics_weekly P WHERE P.ID_PRODUCT=A.ID_PRODUCT) FROM
(
SELECT DISTINCT `cat`.`cat_id`, `cat`.`brand_name`, `cat_p`.`rank`
FROM `ps_product_brand` AS `cat`
INNER JOIN `ps_product_custom_statistics_weekly` AS `cat_p`
ON `cat_p`.`id_product` = `cat`.`id_product`
WHERE `cat_p`.`type` LIKE '%all%'
AND `cat_p`.`rank` <= 16
GROUP BY `cat`.`cat_id`, `cat`.`brand_name`
) A
ORDER BY A.RANK
Try the following:
SELECT `cat`.`brand_name`, `cat_p`.`amount`, SUM(`cat_p`.`amount`) AS `sum_amount`
FROM `ps_product_brand` AS `cat`
INNER JOIN `ps_product_custom_statistics_weekly` AS `cat_p` ON `cat_p`.`id_product` = `cat`.`id_product`
WHERE `cat_p`.`type` LIKE '%all%' AND `cat_p`.`rank` <= 16
GROUP BY `cat`.`brand_name`
ORDER BY `sum_amount` DESC, `cat`.`brand_name`
Note the difference in numbers between amount and sum_amount where the duplicates are. You might have to increase the rank and possibly add LIMIT to the end.

MySQL - One to Many Join - Return results for only 1 row from "One" table

Sorry....should have said, this is MySQL.
Ok....first and foremost, I don't know if I can actually do what I am looking to do. I have some experience with SQL, but not a ton. Hopefully, someone can help.
I have two tables, one has orders and one has shipments. I can do a join between them and get a proper result.....
Orders Table
Order_ID | Revenue |
1001 | 125.00 |
1002 | 215.31 |
1003 | 654.43 |
Shipments Table
Order_ID | Shipment_ID | Item Count |
1001 | 99001 | 25 |
1001 | 99002 | 5 |
1002 | 99003 | 65 |
1003 | 99004 | 123 |
1003 | 99005 | 20 |
With a straight join on Order_ID, I get back the expected result:
Order_ID | Revenue | Shipment_ID | Item Count |
1001 | 125.00 | 99001 | 25 |
1001 | 125.00 | 99002 | 5 |
1002 | 215.31 | 99003 | 65 |
1003 | 654.43 | 99004 | 123 |
1003 | 654.43 | 99005 | 20 |
I am trying to reconcile revenue and cost in the same output, if possible. I know from a separate table what the cost of each of my shipments was, so that math is simple. However, my revenue is off this way because I have duplication in the revenue column, due to orders going in multiple shipments.
I would like to get something like the following:
Order_ID | Revenue | Shipment_ID | Item Count |
1001 | 125.00 | 99001 | 25 |
1001 | NULL | 99002 | 5 |
1002 | 215.31 | 99003 | 65 |
1003 | 654.43 | 99004 | 123 |
1003 | NULL | 99005 | 20 |
The values for the duplicate revenue numbers could be null, blank, 0, anything other than a value that will calculate. Any ideas?
Thanks in advance!
Matthew
In MySQL you can use the following query:
SELECT s1.Order_ID, s1.Shipment_ID, s1.Item_Count,
IF(s1.Shipment_ID = s2.minS_ID, o.Revenue, 0) AS Revenue
FROM Shipments AS s1
INNER JOIN (
SELECT Order_ID, MIN(Shipment_ID) AS minS_ID
FROM Shipments
GROUP BY Order_ID
) AS s2 ON s1.Order_ID = s2.Order_ID
INNER JOIN Orders AS o ON s1.Order_ID = o.Order_ID
The idea is to perform an additional join with a derived table that contains the minimum Shipment_ID per Order_ID. If the Shipment_ID value of the current row is equal to this value then return Revenue, else return 0.
Demo here
In SQL Server you can use window version of MIN to make the comparison:
SELECT s.Order_ID, Shipment_ID, Item_Count,
CASE
WHEN MIN(Shipment_ID) OVER (PARTITION BY s.Order_ID) = Shipment_ID THEN Revenue
ELSE 0
END AS Revenue
FROM Shipments AS s
INNER JOIN Orders AS o ON s.Order_ID = o.Order_ID

MySQL: Using NULL as wildcard in JOIN statement

I have a table containing perfectly defined items and a second table with potentially vague/greedy orders as NULL would require all available values for this parameter.
items
+-----------------------+
| item_id | color | size |
|---------+-------+------|
| 1 | blue | 8 |
| 2 | red | 6 |
| 3 | green | 7 |
| 4 | black | 6 |
+------------------------+
orders
+-------------------------+
| order_id | color | size |
|----------+-------+------|
| 1 | red | 6 |
| 2 | green | 8 |
| 3 | NULL | 6 |
| 4 | blue | NULL |
| 5 | NULL | NULL |
+-------------------------+
Is there an efficient way to generate a complete list of items needed to fill all orders?
+--------------------+
| order_id | item_id |
|----------+---------|
| 1 | 2 |
| 3 | 2 |
| 3 | 4 |
| 4 | 1 |
| 5 | 1 |
| 5 | 2 |
| 5 | 3 |
| 5 | 4 |
+--------------------+
It seems to me like an INNER JOIN should be able to do this, but something like this obviously doesn't consider the possibility of NULL values as greedy wildcards in the orders table:
SELECT order_id, item_id
FROM orders
INNER JOIN items ON orders.color = items.color AND orders.size = items.size
Any ideas?
Try the following:
SELECT order_id, item_id
FROM orders
INNER JOIN items ON (orders.color IS NULL OR orders.color = items.color)
AND (orders.size IS NULL OR orders.size = items.size)
Let me know if that helps, or if I misunderstood the question.
If you rewrite the conditions for the JOIN you can get the desired result:
SELECT order_id, item_id
FROM orders
JOIN items
ON ((orders.color = items.color OR orders.color IS NULL)
AND (orders.size = items.size OR orders.size IS NULL))
However, the orders table should probably look more like the result of this query than the current orders table.
You could use the IFNULL function for this:
SELECT order_id, item_id
FROM orders
JOIN items ON IFNULL(orders.color, items.color) = items.color
AND IFNULL(orders.size, items.size) = items.size
If the value in orders is null, then it'll use the value from items (and thus match).

mysql increment against another column

Here is my Products table.
+---------+------------+-----------+
| item_id | item_order | item_name |
+---------+------------+-----------+
| 10 | | Item x |
| 12 | | Item b |
| 33 | | Item j |
| 39 | | Item k |
+---------+------------+-----------+
I want to run a sql query in phpMyAdmin so that item_order will increment as item_id number decreases. I want to arrange products on my Featured Products page by item_order. At a later time I'll rearrange numbers in 'item_order' column to give certain products more prominence, but for now here is how table will look after query update...
+---------+------------+-----------+
| item_id | item_order | item_name |
+---------+------------+-----------+
| 10 | 4 | Item x |
| 12 | 3 | Item b |
| 33 | 2 | Item j |
| 39 | 1 | Item k |
+---------+------------+-----------+
How about this?
SELECT *, (
SELECT COUNT(1) FROM Products pd WHERE pd.item_id >= p.item_id
) item_order
FROM Products p