I need to find which customer bought phones. If the customer bought iPhone only or iPhone and HTC then I need data for iPhone only. But if the customer bought only HTC I would need data for HTC.
Original Data
Expected output table
My code does not work and I'm not sure how to use the where clause. Could you please direct me on where I'm making a mistake?
Below is my code:
select Cust_ID, Year, Month, Product from custTable where Item_ID = (
Case
when Item_ID in ('iPhone','HTC') then 'iPhone'
else 'HTC'
End )
You can use not exists:
select o.*
from original o
where o.product = 'iPhone' or
(o.product = 'HTC' and
not exists (select 1
from original o2
where o2.cust_id = o.cust_id and
o2.product = 'iPhone'
)
);
This is almost a direct translation of your conditions.
Try this:
select v1.Cust_ID,
v1.Year,
v1.Month,
substring(min(Product),4,1000) as Product
from (
select Cust_ID,
Year,
Month,
case when Product in ('iPhone') then '1. iPhone'
when Product in ('HTC') then '2. HTC'
else Product
end as Product
from custTable
) v1
group by v1.Cust_ID,
v1.Year,
v1.Month
Or this:
select v1.Cust_ID,
v1.Year,
v1.Month,
v1.Product
from (
select Cust_ID,
Year,
Month,
Product,
ROW_NUMBER() over(partition by Cust_ID, Year, Month order by case when Product = 'iPhone' then 1 when Product = 'HTC' then 2 else 999 end) as rn
from custTable
) v1
where v1.rn = 1
Related
I have an order table (product, qty_required) and a stock/bin location table (product, bin_location, qty_free) which is a one->many (a product may be stored in multiple bins).
Please, please (pretty please!) Does anybody know how to:
When producing a picking report, I only want to return the first x bins for each product ordered THAT SATISFIES the qty_required on the order.
For example
An order requires product 'ABC', QTY 10
Product 'ABC' is in the following locations (this is listed using FIFO rules so oldest first):
LOC1, 3 free
LOC2, 4 free
LOC3, 6 free
LOC4, 18 free
LOC5, 2 free
so. on the report, I'd ONLY want to see the first 3 locations, as the total of those (13) satisfies the order quantity of 10...
Ie:
LOC1, 3
LOC2, 4
LOC3, 6
Use sum(qty_free) over(partition by product order by placement_date desc, bin_location) to calculate running sum and filter rows by your threshold in outer query (select from select). Added location in order by to exclude sum of all locations where placement was in the same day.
with s as (
select st.*,
sum(qty_free) over(partition by product order by placement_date asc, bin_location) as rsum
from stock st
)
select
o.product,
s.bin_location,
s.qty_free,
o.qty_requested
from orders o
left join s
on o.product = s.product
and s.rsum <= o.qty_requested
UPD: Since that turned out that your SQL Server version is so old that there's no analytic function in it, here's another less performant way to do this (maybe need some fixes, didn't tested on real data).
And fiddle with some setup.
with ord_key as (
select stock.*,
/*Generate order key for FIFO*/
row_number() over(order by
placement_date desc,
bin_location asc
) as sort_order_key
from stock
)
, rsum as (
/*Calculate sum of all the items before current*/
select
b.product,
b.bin_location,
b.placement_date,
b.qty_free,
coalesce(sum(sub.item_sum), 0) as rsum
from ord_key as b
left join (
/*Group by partition key and orderby key*/
select
product,
sort_order_key,
sum(qty_free) as item_sum
from ord_key
group by
product,
sort_order_key
) as sub
on b.product = sub.product
and b.sort_order_key > sub.sort_order_key
group by
b.product,
b.bin_location,
b.placement_date,
b.qty_free
)
, calc_quantities as (
select
o.product,
s.placement_date,
s.bin_location,
s.qty_free,
s.rsum,
o.qty_requested,
case
when o.qty_requested > s.rsum + s.qty_free
then s.qty_free
else s.rsum + s.qty_free - o.qty_requested
end as qty_to_retrieve
from orders o
left join rsum s
on o.product = s.product
and s.rsum < o.qty_requested
)
select
s.*,
qty_free - qty_to_retrieve as stock_left
from calc_quantities s
order by
product,
placement_date desc,
bin_location desc
I'm trying to pull a list of customer IDs who have purchased online only and another list of those who have purchased both online and in-store (no customers for in-store only). I currently have a table that looks like this:
-------------------------------------------------------
**customerid** **shipped_dt** **channel**
1 2018-10-31 online
1 2018-11-01 store
2 2018-11-01 store
3 2018-11-01 online
3 2018-11-02 online
In this case, for the list with those who have purchased both online and in-store, I was customerid 1. For the list of customers that are online only, I want customerid 3. How exactly would I go about writing a code for this? I'm still learning SQL so I'm not too knowledgeable about the proper syntax and abilities within SQL.
I only want a return of customerid and the channel that they purchased through.
Thank you!
If you have a separate list of customers, you might want to use exists:
For example, to get customers who have purchased in both places:
select c.*
from customers c
where exists (select 1 from t where t.customer_id = c.customer_id and t.channel = 'online'
) and
exists (select 1 from t where t.customer_id = c.customer_id and t.channel = 'store'
) ;
This can have some advantages over aggregation:
exists and not exists can make direct use of an index on (customer_id, store).
You can get customers who purchased in neither channel.
You can get additional information about the customer, if that is desirable.
Just a minor alternative
Example
Select customerid
,Channel = min(Channel)
From YourTable
Group By customerid
Having min(Channel)=max(channel)
and min(Channel)='online'
Note: If you remove and min(Channel)='online' you would see customers who purchased through only one channel
Returns
customerid Channel
3 online
For the case of 'online' only customers use not exists to exclude those who have purchased in 'store':
select distinct customerid
from tablename t
where not exists (
select 1 from tablename where customerid = t.customerid and channel = 'store'
)
For the case of 'online' and 'store':
select distinct customerid
from tablename t
where
(select count(distinct channel) from tablename where customerid = t.customerid) = 2
You can combine the above queries into this:
select distinct customerid, 'online' channel
from tablename t
where not exists (
select 1 from tablename where customerid = t.customerid and channel = 'store'
)
union all
select distinct customerid, 'both' channel
from tablename t
where
(select count(distinct channel) from tablename where customerid = t.customerid) = 2
See the demo
Sorry if the title seems confusing, it was the best I could come up with.
I can work with both excel(Dax since its a power query) and sql:
I have a situation where there are two product types being purchased, Type_A and Type_B.
I want to calculate a count of how many unique Loc_ID have purchased a "Type_A" Product type, AFTER purchasing a "Type_B" Product type.
From my example there are a total of 3 unique Loc_ID which would fall in this filter: Loc_01, Loc_02, and Loc_04
Any help is greatly appreciated
Try this (it works good if each loc_id purchased both type of products as in your example.
select count(*)
from
(select loc_id , max(date_purchased) dt
from table t where product_type = 'type_a'
group by loc_id) a,
(select loc_id , max(date_purchased) dt
from table t where product_type = 'type_b'
group by loc_id) b
where a.loc_id=b.loc_id and a.dt>b.dt;
This will work even if certain loc_id did not purchase both type of products
Try this:-
Select count(a.loc_id) as cnt_locations
from
your_table_name a
inner join
(
Select a.loc_id,b.date_purchased,b.Product_type
from
(
Select loc_id, min(date_purchased) as date_purchased
from
your_table_name
group by loc_id
) a
inner join
your_table_name b
on a.loc_id=b.loc_id and a.date_purchased =b.date_purchased
where Product_type ='Type_B'
) b
on
a.loc_id=b.loc_id
where a.date_purchased >b.date_purchased and a.Product_type ='Type_A'
I am trying to select the min price of each condition category. I did some search and wrote the code below. However, it shows null for the selected fields. Any solution?
SELECT Sales.Sale_ID, Sales.Sale_Price, Sales.Condition
FROM Items
LEFT JOIN Sales ON ( Items.Item_ID = Sales.Item_ID
AND Sales.Expires_DateTime > NOW( )
AND Sales.Sale_Price = (
SELECT MIN( s2.Sale_Price )
FROM Sales s2
WHERE Sales.`Condition` = s2.`Condition` ) )
WHERE Items.ISBN =9780077225957
A little more complicated solution, but one that includes your Sale_ID is below.
SELECT TOP 1 Sale_Price, Sale_ID, Condition
FROM Sales
WHERE Sale_Price IN (SELECT MIN(Sale_Price)
FROM Sales
WHERE
Expires_DateTime > NOW()
AND
Item_ID IN
(SELECT Item_ID FROM Items WHERE ISBN = 9780077225957)
GROUP BY Condition )
The 'TOP 1' is there in case more than 1 sale had the same minimum price and you only wanted one returned.
(internal query taken directly from #Michael Ames answer)
If you don't need Sales.Sale_ID, this solution is simpler:
SELECT MIN(Sale_Price), Condition
FROM Sales
WHERE Expires_DateTime > NOW()
AND Item_ID IN
(SELECT Item_ID FROM Items WHERE ISBN = 9780077225957)
GROUP BY Condition
Good luck!
I want to update prices of those products thats not been purchased by 1 year. How do I do that?
My current query is:
UPDATE product
SET price = price * 0.9
WHERE date_purchase > SYSDATE - 365
AND pid IN ([How do i select the items thats not been purchased in 1year??]);
I have 2 tables:
Product => pid, p_name, etc... (pid = product id, p_name = product name)
Purchase => pid, date_purchase, etc
I'd go with a NOT EXISTS as it makes the requirement more transparent.
update product
set price = price * 0.9
where not exists
(select 1 from PURCHASE pchase
WHERE pchase.pid = PRODUCT.pid
and pchase.date_purchase > add_months(sysdate,-12))
of course you would want to consider what to do with products that have only been just introduced (eg a week old) and never sold.
I think this might come close
update product
set price = price * 0.9
where pid NOT IN (
select pr.pid
from product pr
left outer join purchase pu
on pu.pid = pr.pid
where (( pu.date_purchase != null)
AND pu.date_purchase < (SYSDATE - 365))
or pu.pid == null
);