Update and nested select statement? - sql

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
);

Related

Trouble finding which customer bought iphone or HTC using SQL script

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

Selecting customer IDs who have purchased from a certain channel

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

SQL Query (max of sum per year)

I will try to describe my problem here. Let's say that i have 3 tables:
1. sales
salesID (PK)
productID(FK) - FK that points to the sold product
storeID(FK) - FK that points to the store that sold the product
month - month in which the product was sold
year - year in which the product was sold
amount - amount(of money) for which the product was sold
2. product
productID (PK)
productName
3. store
storeID (PK)
storeName
I need the following query: For every store show the top selling product in that store for the specified year. So the result should look something like:
STORE PRODUCT AMOUNT
store1 product1 XXX amount
store2 product2 YYY amount
store3 product1 XYX amount
Where each amount would be the highest sum of all amounts in that year.
What I can do now is:
SELECT store.storeName
, product.ProductName
, SUM(sales.ammount)
FROM sales
JOIN product ON sales.productID = product.productID
JOIN store ON sales.storeID = store.storeID
GROUP BY store.storeName
, product.ProductName
WHERE sales.year = 'XXXX'
;
Where I get a sum per product and per store. After this I can use the cursor to go thru the entire table row by row and check which one has the highest amount for that store.
But I was wondering if it is possible to do that in a 'simple' query?
I'm using SQL Developer for Oracle 11g database.
And I would appreciate any help.
Not terribly familiar with Oracle, but perhaps something like:
WITH cte AS (SELECT store.storeName
, product.ProductName
, SUM(sales.ammount) as sales_amount
FROM sales
JOIN product ON sales.productID = product.productID
JOIN store ON sales.storeID = store.storeID
WHERE sales.year = 'XXXX'
GROUP BY store.storeName, product.ProductName
)
SELECT a.*
FROM cte a
JOIN (SELECT storeName
, MAX(sales_amount) as Max_Sales
FROM cte
GROUP BY storeName
)b
ON a.storeName = b.StoreName
AND a.sales_amount = b.Max_Sales;
If there's a tie that returns both entries, you can use ROW_NUMBER() instead to limit to one result per storeName .

Get the average with quantity

I have a problem to calculate easily some simple average. My table :
id / user / action / data
1 / a / unit_price / 40
2 / a / quantity / 1
3 / b / unit_price / 70
4 / b / quantity / 2
Unit_price is a price for a user and quantity is quantity. So there i should get :
(40 + 70 + 70) / 3 = 60
If i do an
(AVG(action) WHERE action = unit_price)
I get :
(70+40)/2 = 55
If I do an
(SUM(action) WHERE action = unit_price) / (SUM(action) WHERE action = quantity)
I get :
110 / 3 = 36.6
The easiest way I found is to don't put the unit_price but the global price then make a division in the PHP code to get the unit_price, but I was hoping SQL could do something for me.
select coalesce(sum(quantity * unit_price) /sum(quantity), 0) from
(select
sum(case when action='unit_price' then data else 0 end) as unit_price,
sum(case when action='quantity' then data else 0 end) as quantity
from test
group by user) as a
SqlFiddle
You can use something like this which basically pivots the data to a more usable format and then gets the values that you need:
select avg(unit_price) AvgUnitPrice,
sum(unit_price*quantity)/sum(quantity) AvgPrice
from
(
select user,
max(case when action = 'unit_price' then data end) unit_price,
max(case when action = 'quantity' then data end) quantity
from table1
group by user
) x;
See SQL Fiddle With Demo
Ok, obviously your table design is not optimal, you should have unit_price and quantity as separate columns. But, working with what you have, try this:
SELECT SUM(A.data*B.data)/SUM(B.data) Calculation
FROM ( SELECT user, data
FROM YourTable
WHERE action = 'unit_price') AS A
INNER JOIN (SELECT user, data
FROM YourTable
WHERE action = 'quantity') AS B
ON A.user = B.user
I would join the table to itself in order to get the two records beloning together on one line
SELECT
SUM(unit_price * quantity) / SUM(quantity) AS average_unit_price
FROM
(SELECT
U.data AS unit_price, Q.data AS quantity
FROM
theTable U
INNER JOIN theTable Q
ON U.user = Q.user
WHERE
U.action = 'unit_price' AND
Q.action = 'quantity')
If you have more than two records per user and the ids of the both records are consequtive, then you would have to change the WHERE-clause to
WHERE
U.action = 'unit_price' AND
Q.action = 'quantity' AND
U.id + 1 = Q.id
Note:
If you calculate AVG(unit_price * quantity) you get the average sum per user.
(1*40 + 2*70) / 2 = 90
If you calculate SUM(unit_price * quantity) / SUM(quantity) you get the average unit price.
(1*40 + 2*70) / 3 = 60
Something like this should work; syntax might not be perfect since I didn't try it out, but you get the main idea at least.
SELECT sumUnitPrice.sum / sumQuantity.sum
FROM
(
(SELECT SUM(data) as sum
FROM WhateverTheHellYourTableIsNamed
WHERE action = 'unit_price') sumUnitPrice
(SELECT SUM(data) as sum
FROM WhateverTheHellYourTableIsNamed
WHERE action = 'quantity') sumQuantity
)
Your table design doesn't look good.
Make 2 tables instead:
ITEM
ItemId int not null PK,
Name varchar(200) not null,
UnitPrice decimal (10,2) not null
SALES
SalesId int not null PK,
ItemId int not null FK,
Quantity decimal(10,2)
PK - primary key, FK - foreign key
Average:
select
I.Name, avg(I.UnitPrice * S.Quantity) as avgSales
from
Sales S
join Items I on I.ItemId = S.ItemId
group by
I.Name

select least row per group in SQL

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!