Retrieve rows which meets a certain condition - mysql

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.

Related

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

Find customers with similar tastes while excluding certain customers

I have a table documenting purchases from customers, with one row per purchase:
CustomerID | ProductID
1 | 1000
1 | 2000
1 | 3000
2 | 1000
3 | 1000
3 | 3000
... | ...
I am using the following code to find the ten customers with the greatest number of overlapping products with customer #1 (first result is the one with the most overlap etc):
SELECT othercustomers.CustomerID, COUNT(DISTINCT othercustomers.ProductID)
FROM `purchases` AS thiscustomer
JOIN `purchases` AS othercustomers ON
thiscustomer.CustomerID != othercustomers.CustomerID
AND thiscustomer.ProductID = othercustomers.ProductID
WHERE thiscustomer.CustomerID = '1'
GROUP BY othercustomers.CustomerID
ORDER BY COUNT(DISTINCT othercustomers.ProductID) DESC
LIMIT 10
The code yields the expected output (Customer ID + total number of overlapping products with customer #1).
I would now like the query to exclude customers with overlapping purchases who have purchased more than 1000 different products, because these are bulk buyers who purchase the entire stock and whose purchase history therefore has no meaning when searching for customers with a similar taste.
In other words, if customer #500 had bought >1000 different products, I want him/her excluded from the results when searching for customers with a similar taste to that of customer #1 - even if customer #500 has bought all three products that customer #1 had bought and would ordinarily rank first in similarity/overlap.
I suppose some HAVING is in order, but I cannot seem to figure out what the appropriate condition is.
Thanks!
I think that HAVING won't do what you want, since it will only give you the total count of overlaping products, while you want the total count of products for the other customer.
You could filter with a correlated subquery in the WHERE clause:
SELECT othercustomers.CustomerID, COUNT(DISTINCT othercustomers.ProductID)
FROM `purchases` AS thiscustomer
JOIN `purchases` AS othercustomers ON
thiscustomer.CustomerID != othercustomers.CustomerID
AND thiscustomer.ProductID = othercustomers.ProductID
WHERE
thiscustomer.CustomerID = '1'
AND (
SELECT COUNT(DISTINCT ProductID)
FROM `purchases` AS p
WHERE p.CustomerID = othercustomers.CustomerID
) < 1000
GROUP BY othercustomers.CustomerID
ORDER BY COUNT(DISTINCT othercustomers.ProductID) DESC
LIMIT 10
For performance, you want an index on purchases(CustomerID, ProductID).

how to select orders that contains certain sku

I need to find items that contain SKUs starting with AB but would still want the query to return orders that contains AB and others.
order number 12334: sku AB12 & AB24
order number 22356: sku AB523 & KC5145
order number 123556: sku CD5641 & BG521
I expect the query to return order number 12334 and 22356 but not 123556
I would also like to know if an order is purely AB or AB with other items
so the table would return count of orders that contains only AB and count of order that contains AB with other items.
I have two tables: an orders table and line_items
orders table gives order_id to link to line_items table ID
select * from xx.line_items l
left join xx.orders o on l.order_id=o.id
where sku like 'AB%'
You haven't shared much details about the tables and columns for us to provide the full solution, but see if you can build something off of this mock up
select
sku,
(char_length(sku)-char_length(replace(sku,'AB','')))/2 as item_count
from your_table
where sku like '%AB%';
he following query gives you all order IDs with AB items. It shows their AB count and their total count, so you can compare the two. You could also add a CASE WHEN expression to compute a flag showing whether the order is pure AB or not.
After all this simply groups the items per order, counts AB items and dismisses all orders that have no AB item.
select
order_id,
count(*) as count_all,
count(case when sku like 'AB%' then 1 end) as count_ab
from line_items
group by order_id
having count(case when sku like 'AB%' then 1 end) > 0;
You can replace count(case when sku like 'AB%' then 1 end) with sum(sku like 'AB%') in MySQL by the way.
Here is a query building up on the above to count pure AB orders and mixed orders:
select (count_all = count_ab) as ab_only, count(*)
from
(
select count(*) as count_all, count(case when sku like 'AB%' then 1 end) as count_ab
from line_items
group by order_id
having count(case when sku like 'AB%' then 1 end) > 0
) counted
group by (count_all = count_ab);

Select count of customers that purchased at least one from two specific categories in the same month

I'm trying to write a query to count the customers that purchased at least one from order_type=0 and one order_type=1 during the same month in 2014
I have two tables.
The order table that have:
order_id
customer_id
aquisition_date
orders_category table:
order_id
order_type (the type of the orders it may have 0, 1, 2, 3, 5, 8 ...etc )
I tried with this query but it didn't work, I know it's not complete and I missed the month condition!
Select count(user_id) From order
join orders_category
on order.order_id = orders_category.order_id
Where (order_type=0 or order_type=1)
and extract (year from order.aquisition_date)=2014
group by user_id
having count (case when type_id=0 then null else null end) > 0
and count (case when type_id=1 then null else null end) > 0;
I don't know how to find users with at least 1 order from order_type=0 & 1 order of order_type=1, in the same month.
You could use this query, based on what you already had. However, I suggest you change the name of the table order to orders as order is a reserved word:
select count(distinct user_id)
from (
select user_id, month(aquisition_date)
from orders
inner join order_category
on orders.order_id = order_category.order_id
where order_type in (0, 1)
and year(aquisition_date) = 2014
group by user_id, month(aquisition_date)
having count(distinct order_type) = 2
) as base
SQL fiddle
I selected the month also in the sub-select, as it will be interesting to look at the output of that query on its own during your analysis.

Mysql join where value or null

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.