Mysql join where value or null - mysql

I've got a table PROD
ID NAME
1 Apple
2 Banana
And the relative table PRICES , with global prices (ID_USER is Null)
or per-user prices (ID_USER)
PROD_ID USER_ID PRICE
1 null 10
1 5 8
Now, i need a query that finds all products and the relative prices,
the catch is that i'm trying to retrieve the user price only if there is one, else retrieve the global price
SELECT PROD.* , PRICES.* FROM PROD
LEFT JOIN PRICES ON PROD.ID_PROD = PRICES.ID_PROD
WHERE PRICES.USER_ID IS NULL OR PRICES.USER_ID = 5
This query returns 2 rows (the prod joined with the 2 prices)
Is there a way to retrieve the exact price for the product in just one query ?
thanks !!
EDIT: In the join i need the per-user price row (the last one) only if the row exists , else i need to retrieve the row with the global price, is that possible ?

SELECT
prod.*,
COALESCE(p_user, p_default) As Price
FROM
Prod INNER JOIN (SELECT
PROD_ID,
MAX(CASE WHEN USER_ID=5 THEN Price END) p_user,
MAX(CASE WHEN USER_ID IS NULL THEN Price END) p_default
FROM
Prices
GROUP
BY PROD_ID) m_uid
ON Prod.ID = m_uid.Prod_ID
Please see fiddle here.

Related

Select price record if record for a customer exists, otherwise select general price record

Suppose i have the following MySQL prices table:
ID
item
from_date
to_date
customer
price
1
1
2021-10-10
2021-12-14
NULL
103.25
2
1
2021-12-15
NULL
NULL
100.25
3
5
2022-01-01
NULL
NULL
201.50
4
1
2022-01-05
NULL
3
80.75
The idea is that for there will be a price record for each item and if there are special price deals for a customer, also a record for that item with a different price. Looking for the price, the price should be taken from the record that is for the item and that specific customer. if there is none, it should return the default price which is valid for all customers.
So, I am trying to get the price that is valid at today's date (2022-02-02). Having those records selected, if there is a most recent record for my specific customer (3) it takes precedence, return that (record ID 4). If there is none for this customer, return the most recent price (record ID 2)
So, today = 2022-02-02, my item is 1 AND my customer is 3. Using the table above the result should be:
ID
item
from_date
to_date
customer
price
4
1
2022-01-05
NULL
3
80.75
If the record with ID = 4 is not there the result should be:
ID
item
from_date
to_date
customer
price
2
1
2021-12-15
NULL
NULL
100.25
I tried several select queries but still don’t see what i should do and google didn’t help me much.
Any help would be appreciated greatly!
Assuming that there is no record for the same item with the same from_date, then this could do it.
SELECT * FROM YourTable WHERE item = '1' AND ((from_date = '2022-02-02' AND customer = '3') OR (from_date < '2022-02-02' AND (customer <> '3' OR customer IS NULL))) ORDER BY from_date DESC, ID DESC LIMIT 1
The query selects all records for item = 1, with either the requested from_date from the customer you wish, or from other customer (or null) from previous dates.
The order by DESC will sort the records by the latest from_date and ID (if there is the same date) and the LIMIT 1 will show only the first record of the query.
If you wish to show the latest price with no customer (if the requested record for the customer is not found), omit the customer <> '3'
SELECT * FROM YourTable WHERE item = '1' AND ((from_date = '2022-02-02' AND customer = '3') OR (from_date < '2022-02-02' AND customer IS NULL)) ORDER BY from_date DESC, ID DESC LIMIT 1
Ok, i think i wasn't in the right mind when i asked this question. See things much clearer now.
I solved it via this simple query:
SELECT ISP.gross_price, ISP.disc_amount, ISP.disc_percentage, I.Name FROM item_salesprices ISP
JOIN Items I ON I.ID = ISP.item
WHERE I.Item=‘ZZ1’
AND (ISP.Customer=(SELECT ID FROM Customers WHERE Customer=‘Cust3’) OR ISP.Customer IS NULL)
AND (ISP.from_date <= NOW() OR ISP.from_date IS NULL) AND (ISP.to_date >= NOW() OR ISP.to_date IS NULL)
ORDER BY ISP.Customer DESC, ISP.from_date DESC LIMIT 1
The result is that if there's a price record for item = 1 and customer = 3, it will output only that row. Thus, record ID = 4, price = 80.75.
If there's not, it will output the default price row for item = 1 (and essentially customer IS null). Thus, record ID = 2, price = 100.25.

Delete Duplicate values from specific column in mysql table based on query to other column

I am having one table in SQL Database where I record customer wise sales for specific products. I have monthly target for each product like as below
Product A - 50 pcs
Now in my table I am seeing customer wise sales and the monthly product sale target which is common.
Customer Product MonthlyTargetQty
Customer A Product 1 50
Customer B Product 1 50
Customer C Product 1 50
Customer D Product 1 50
I want to keep only distinct value in MonthlyTargetQty Column and do not want to delete Product name which is repeating in Product Column. Please help with a query
How I want it is : -
Customer Product MonthlyTargetQty
Customer A Product 1 50
Customer B Product 1 0
Customer C Product 1 0
Customer D Product 1 0
You seem to want:
select customer, product,
(case when row_number() over (partition by product order by customer) = 1 then monthlytargetqty end) as monthlytargetqty
from t
order by product, customer;
This uses row_number() to define the first row for each customer and then a case expression to keep the value you want on that row. Note that the order by is consistent with the partition by/order by for row_number().
EDIT:
If you want to update the existing table -- which seems like a really bad idea to me -- you can do:
update t join
(select product, min(customer) as min_customer
from t
group by product
) tt
on t.product = tt.product and t.customer <> tt.min_customer
set monthlytargetqty = 0;
from the comment it seems you want update I added with update
with cte as
(
select customer, product,
(case when row_number() over (partition by product order by customer) = 1 then monthlytargetqty end) as monthlytargetqty
from t
)
update a
set a.MontylyTargetQty= b.monthlytargetqty
from ProductAnalysisTable a join cte on
a.customer=cte.customer and a.product=b.product
btw 1st part is sir #gordon so accept his answer

MySQL JOIN and group by so there is one ID per row

Two tables: products, all IDs are unique, and stock, where there can be same ID several times. I need to compare the quantities where the quantity in product table doesn't match the total quantity in the stock table.
products
ID quantity
1 4
2 6
3 2
stock:
ID quantity
1 1
1 3
2 5
3 2
How can I get a result where there is a single ID per row? Expected result:
ID quantity as products quantity as stock
2 6 5
You can have a subquery that computes the total stocks, and LEFT JOIN it to your products table:
SELECT
products.ID,
products.quantity AS `quantity as products`,
total_stock.quantity AS `quantity as stock`
FROM
products
LEFT JOIN
-- We compute total quantities from `stock`
(SELECT
stock.ID, sum(stock.quantity) AS quantity
FROM
stock
GROUP BY
stock.ID
) AS total_stock
ON total_stock.ID = products.ID
WHERE
-- We want to find only discrepancies.
-- We use NOT <=> to safely check nulls.
NOT (total_stock.quantity <=> products.quantity)
ORDER BY
products.ID ;
I've assumed this schema (with a REFEFENCES constraint):
CREATE TABLE products
(
ID INTEGER PRIMARY KEY,
quantity INTEGER NOT NULL
) ;
CREATE TABLE stock
(
ID INTEGER NOT NULL REFERENCES products(ID),
quantity INTEGER NOT NULL
) ;
You can find your example data (together with a few extra data to care for nulls) and this solution at dbfiddle here
You can also change the WHERE clause to:
-- We use coalesce to convert nulls to 0 (we assume *don't know* means *don't have*)
coalesce(total_stock.quantity,0) <> coalesce(products.quantity, 0)
depending on your use-case.
dbfiddle here
References:
MySQL NULL-safe equal
MySQL COALESCE

Comparing day and week prices and getting cheapest

I'm trying to get the lowest price of an object. Problem is there can be daily and weekly prices. So when searching for the cheapest price i have to multiply the day price time 7 and compare to the week price to get the cheapest.
It can also happen that an object has week prices only or day prices only (or no prices at all).
BTW: It has to be such a subselect query, cause i have some more WHERE queries following later.
Pricetable
id price type oid
1 10 d 1
2 12 d 2
3 70 w 1
4 80 w 2
Objects
id name
1 house1
2 house2
This is what i'm using but its not working correctly. When the day price*7 is bigger that the week price it still gives me the day price.
SELECT p.oid, p.price, p.id, p.type FROM Pricetable p INNER JOIN (
SELECT oid, MIN(IF(type="w",price, price*7)) AS price, id, type
FROM Pricetable
GROUP BY oid
) p2 ON p.oid = p2.oid AND p.id= p2.id
Your query should work to get the minimum price. However, it should be written as:
SELECT oid, MIN(CASE WHEN type = 'w' THEN price ELSE 7*price END) AS price
FROM Pricetable
GROUP BY oid ;
If you want other values from row with the minimum price, then you need more logic. How about this?
SELECT pt.*oid, MIN(CASE WHEN type = 'w' THEN price ELSE 7*price END) AS price
FROM Pricetable pt
WHERE pt.id = (SELECT pt2.id
FROM PriceTable pt2
WHERE pt2.oid = pt.oid
ORDER BY (CASE WHEN pt2.type = 'w' THEN pt2.price ELSE 7*pt2.price END)
LIMIT 1
);
You'll need a sub-select with some sort of ranking to get the cheapest price.
Check https://dba.stackexchange.com/questions/13703/get-the-rank-of-a-user-in-a-score-table That should get you on the right track.
I don't have access to MySQL right now, and it's a bit different than MSSQL. But that link should set you on the path.

Retrieve rows which meets a certain condition

I would like to start by explaining what my query should do.
At my store,we sell products A,B,C and D(Product ID)
Let's say I am interested in only those transactions where Item A was sold
This is how i wrote my query
Select [Transaction_No],[Product ID]
from [MystoreDB$Transaction lines]
where Date = '01-Jan-2016'
and (Product ID) = 'A'
The query executes without any errors,and I get the results only filtered to Product ID A.
But if I really look into the filtered transactions, I can see that there were other products bought in the same transaction(Product B was bought as well)
But the query only filtered 'the rows' with Product A
For Instance
There were total of 4 transactions done on 1-Jan-2016
Transaction 1 had
Product A + B
Transaction 2 had
Product A only
Transaction 3 had
Product A + C
Transaction 4 had
Product A only
At the end I want my query to retrieve only 2 transactions
Which is Transaction 2 and 4(since only product A was purchased)
I will ignore Transactions 1 and 3 since another product was purchased along with product A
What I want to find out is all transactions that had only Product A.
This means, the customer only bought product A and no other products.
Not sure how to get this.
I am using MYSQL for the DB engine
SELECT
Transaction_No
FROM
Transactions
WHERE
Date = '01-Jan-2016'
GROUP BY
Transaction_No
HAVING
COUNT(CASE WHEN Product_Id = 'A' THEN Product_Id END) = COUNT(*)
Doing a group by with conditional aggregation will give you the desired result and as there are no sub selects etc it should preform faster than a NOT EXISTS solution.
Edit Per Your Comment:
To test to see if a customer bought both Product A & B but no other products you would have to add a couple of additional constraints in your HAVING clause. Test that COUNT of A > 0 and COUNT of B > 0 and then that the COUNT of A & B is the same as the COUNT of All Products.
SELECT
Transaction_No
FROM
Transactions
WHERE
Date = '01-Jan-2016'
GROUP BY
Transaction_No
HAVING
COUNT(CASE WHEN Product_Id = 'A' THEN Product_Id END) > 0
AND COUNT(CASE WHEN Product_Id = 'B' THEN Product_Id END) > 0
AND COUNT(CASE WHEN Product_Id IN ('A','B') THEN Product_Id END) = COUNT(*)
Add this to your WHERE clause:L
AND [Transaction_No] NOT IN (
SELECT [Transaction_No]
FROM [MystoreDB$Transaction lines]
where [Product ID] <> 'A'
)
to exclude customers who bought some other product.