Joining 2 columns of a subqueried table into SQL - sql

I'm trying to match the inventory for the day to the SKU.
Amazon_orders has the SKUs I want to match
Invhistory2 has the SKU, timestamp date (more than 1, here's the issue), and the inventory quantity
I'm trying to create a subquery that averages the inventory for the day, then join the timestamp and SKU to the SKUs on the amazon orders table. Null values are no issue here.
My code looks like this:
(SELECT AVG(quantity) AS qty FROM `perfect-obelisk-289514.inventory_history.invhistory2`) AS qtyok
FROM `perfect-obelisk-289514.reports.flat_file_orders_by_order_datereport` Amazon_Orders
LEFT JOIN (SELECT AVG(quantity) AS qty, sku, CAST(snapshot_date AS DATE) AS invdate
FROM `perfect-obelisk-289514.inventory_history.invhistory2`
GROUP BY sku, invdate) AS table2
ON (
Amazon_Orders.sku = table2.sku AND CAST(LEFT(Amazon_orders.purchase_date,10) AS DATE) = table2.invdate
)
The issue is that I get the same average for each row, it's not joining the quantity using SKU and date.
As you may notice I'm a beginner, looked thoroughly but can't find the solution, any help is appreciated!

Thanks for the help!
I managed to solve it. My code was redundant, I just needed this join:
LEFT JOIN (SELECT AVG(quantity) AS qty,
sku,
CAST(snapshot_date AS DATE) AS invdate
FROM `perfect-obelisk-289514.inventory_history.invhistory2`
GROUP BY sku, invdate) AS table2
ON
(Amazon_Orders.sku = table2.sku AND CAST(LEFT(Amazon_orders.purchase_date,10) AS DATE) = table2.invdate)
This gives me what I need

Related

SQL Previous currency rate

I would like to ask you for help with getting previous currency rate.
Eg. I have two tables (Orders and CurrencyRates) which I want to join. First table encloses orders in EUR, and second encloses currency rate from EUR to USD.
I would like to join it by Order Date. The problem is with order date "10.11.2018" which has null rate from CurrencyRates table. I would like to have here value from previous day rate (in this case "09.11.2018). And for the future date ("18.11.2018") I would like to have value having max(Date) from CurrencyRates table. Is there any way to join it to have expected values?
Thank you for your help!
You can use cross apply:
select o.*, cr.date, cr.rate, cr.currency
from orders o cross apply
(select top (1) cr.*
from currencyrates cr
where cr.date <= o.orderdate
order by cr.date desc
) cr;

Summing a column over a date range in a CTE?

I'm trying to sum a certain column over a certain date range. The kicker is that I want this to be a CTE, because I'll have to use it multiple times as part of a larger query. Since it's a CTE, it has to have the date column as well as the sum and ID columns, meaning I have to group by date AND ID. That will cause my results to be grouped by ID and date, giving me not a single sum over the date range, but a bunch of sums, one for each day.
To make it simple, say we have:
create table orders (
id int primary key,
itemID int foreign key references items.id,
datePlaced datetime,
salesRep int foreign key references salesReps.id,
price int,
amountShipped int);
Now, we want to get the total money a given sales rep made during a fiscal year, broken down by item. That is, ignoring the fiscal year bit:
select itemName, sum(price) as totalSales, sum(totalShipped) as totalShipped
from orders
join items on items.id = orders.itemID
where orders.salesRep = '1234'
group by itemName
Simple enough. But when you add anything else, even the price, the query spits out way more rows than you wanted.
select itemName, price, sum(price) as totalSales, sum(totalShipped) as totalShipped
from orders
join items on items.id = orders.itemID
where orders.salesRep = '1234'
group by itemName, price
Now, each group is (name, price) instead of just (name). This is kind of sudocode, but in my database, just this change causes my result set to jump from 13 to 32 rows. Add to that the date range, and you really have a problem:
select itemName, price, sum(price) as totalSales, sum(totalShipped) as totalShipped
from orders
join items on items.id = orders.itemID
where orders.salesRep = '1234'
and orderDate between 150101 and 151231
group by itemName, price
This is identical to the last example. The trouble is making it a CTE:
with totals as (
select itemName, price, sum(price) as totalSales, sum(totalShipped) as totalShipped, orderDate as startDate, orderDate as endDate
from orders
join items on items.id = orders.itemID
where orders.salesRep = '1234'
and orderDate between startDate and endDate
group by itemName, price, startDate, endDate
)
select totals_2015.itemName as itemName_2015, totals_2015.price as price_2015, ...
totals_2016.itemName as itemName_2016, ...
from (
select * from totals
where startDate = 150101 and endDate = 151231
) totals_2015
join (
select *
from totals
where startDate = 160101 and endDate = 160412
) totals_2016
on totals_2015.itemName = totals_2016.itemName
Now the grouping in the CTE is way off, more than adding the price made it. I've thought about breaking the price query into its own subquery inside the CTE, but I can't escape needing to group by the dates in order to get the date range. Can anyone see a way around this? I hope I've made things clear enough. This is running against an IBM iSeries machine. Thank you!
Depending on what you are looking for, this might be a better approach:
select 'by sales rep' breakdown
, salesRep
, '' year
, sum(price * amountShipped) amount
from etc
group by salesRep
union
select 'by sales rep and year' breakdown
, salesRep
, convert(char(4),orderDate, 120) year
, sum(price * amountShipped) amount
from etc
group by salesRep, convert(char(4),orderDate, 120)
etc
When possible group by the id columns or foreign keys because the columns are indexed already you'll get faster results. This applies to any database.
with cte as (
select id,rep, sum(sales) sls, count(distinct itemid) did, count(*) cnt from sommewhere
where date between x and y
group by id,rep
) select * from cte order by rep
or more fancy
with cte as (
select id,rep, sum(sales) sls, count(distinct itemid) did, count(*) cnt from sommewhere
where date between x and y
group by id,rep
) select * from cte join reps on cte.rep = reps.rep order by sls desc
I eventually found a solution, and it doesn't need a CTE at all. I wanted the CTE to avoid code duplication, but this works almost as well. Here's a thread explaining summing conditionally that does exactly what I was looking for.

SQL / sum on different date ranges with other conditions

I have the following code:
SELECT
day
,product_id
,product_name
,quantity_on_hand
,inventory_condition
FROM
(
SELECT
table1.product_id as product_id
,table1.product_name as product_name
FROM table1
WHERE
product_id = XXXX
)product_table
,
(
SELECT
table2.day as day
,table2.product_id as inv_product_id
,inventory_condition
,sum( table2.quantity) AS quantity_on_hand
FROM table2
WHERE
table2.day = TO_DATE('{RUN_DATE_YYYY/MM/DD}', 'YYYY/MM/DD')
AND table2.inventory_condition = XXX
GROUP BY
table2.day
,table2.product_id
,inventory_conditio
) inv
WHERE
product_id = inv.product_id
this code works great if I want to extract the data for a single day. But I want to extract the data for 3 different days in the same query. I've tried to use a OR() on my condition on table2.day but it will give me the sum of the data for the 3 days all together. I've also tried to do
Sum() over (Partition by table2.day)
But i'm not sure how to use the syntax.
tahks a lot for your help

Revenue year by year SQL Server query

I have the following query which provides me with the item and item details, values, rate and quantity across each location.
I am trying to get the yearly revenue based on the Start and End Date. Example, if the chosen date was 2013-2015. The final result will create 3 columns one for 2013 revenue, one for 2014 revenue and one for 2015 revenue.
I am a newbie and still not an expert in writing queries, but here is what I have currently:
SELECT
department,
item,
itemdesc,
qty1,
qty2,
rate_1,
rate_2,
SUM(mm.days*mm.rate*mm.qty)
FROM
items it
LEFT JOIN
(SELECT
i.days, i.rate, i.days, ii.todate, ii.itemid
FROM
invoiceofitems ii
JOIN
invoices i on i.id = ii.id
WHERE
ii.todate BETWEEN #StartDate and #EndDate) mm ON mm.itemid = it.itemid
GROUP BY
department,
item,
itemdesc,
qty1, qty2,
rate_1, rate_2
ORDER BY
item
However, this does not provide me with a year to year aggregation of invoice revenue that I require.
I know this is possible to achieve via iterating through this. But how would I accomplish this and where would I start on this?
Would I need to know the start and end date of each year and iterate through that and then add a counter to the year until year= EndDate?
I'm extremely confused. Help would be appreciated.
I hope that PIVOT and YEAR help you to solve this problem (some columns are omitted):
;WITH SRC(department,item, ... , rate_2, yr, calculation) AS
(SELECT it.department, it.item, ..., it.rate_2, YEAR(ii.todate) as yr,
(i.days * i.rate *i.qty) as calculation
FROM items it
LEFT JOIN invoiceofitems ii ON ii.itemid = it.itemid
JOIN invoices i ON i.id = ii.id)
SELECT department,item, ..., [2013],[2014],[2015]
FROM SRC
PIVOT
(SUM(calculation) FOR yr IN ([2013],[2014],[2015])) PVT
The YEAR function returns only 'year' part of your date and makes grouping easier. PIVOT just rotates grouped data from rows to columns.

SQL Select only products and prices that have changed in price since a specified date

The tables look like this:
tblProducts (around 200k records)
SKU,Title,CategoryID
100,Apple,0
101,Orange,0
102,Carrot,1
tblCategories
CategoryID,CategoryName
0,Fruit
1,Vegetables
tblPrices (around 10m records)
SKU,BuyPrice,SellPrice,Timestamp
100,1,2,2013-1-1 23:04
100,3,6,2013-1-2 19:04
100,4,8,2013-1-3 21:04
100,4,8,2013-1-4 20:04
100,4,8,2013-1-5 22:04
I need to get the current BuyPrice of all products (the most recent one from tblPrices) and compare it to the latest BuyPrice at the time of X days ago from NOW(). I need only the products that changed in BuyPrice.
This is so I can answer the question, 'what products changed in price over the last X days?'. Given the small set of data above, I would get an empty table for 1 days or 2 days, but for 3 days, I would want to retrieve:
SKU,Title,CategoryName,OldBuyPrice,OldSellPrice,NewBuyPrice,NewSellPrice, NBP/OBP
100,Apple,Fruit, 3, 6, 4, 8, 2.00
and for 4 days:
SKU,Title,CategoryName,OldBuyPrice,OldSellPrice,NewBuyPrice,NewSellPrice, NBP/OBP
100,Apple,Fruit, 1, 2, 4, 8, 4.00
I've been searching for similar solutions on the net, but haven't found one. Any ordering is fine. Thanks in advance!
Sure, this is doable. There's a decent windowing-function version, although there may still be better ways to do this:
WITH Current_Price (sku, buyPrice, sellPrice) as
(SELECT sku, buyPrice, sellPrice
FROM (SELECT sku, buyPrice, sellPrice,
ROW_NUMBER() OVER(PARTITION BY sku
ORDER BY timestamp DESC) as rownum
FROM price) t
WHERE rownum = 1),
Price_Back_Previous_Days (sku, buyPrice, sellPrice) as
(SELECT sku, buyPrice, sellPrice
FROM (SELECT sku, buyPrice, sellPrice,
ROW_NUMBER() OVER(PARTITION BY sku
ORDER BY timestamp DESC) as rownum
FROM price
WHERE timestamp < DATEADD(DAY, -3, CONVERT(DATE, GETDATE()))) t
WHERE rownum = 1)
SELECT p.sku, p.title, c.categoryName,
prev.buyPrice as oldBuyPrice, prev.sellPrice as oldSellPrice,
curr.buyPrice as newBuyPrice, curr.sellPrice as newSellPrice,
CASE WHEN prev.buyPrice = 0
THEN curr.buyPrice
ELSE 1.0 * curr.buyPrice / prev.buyPrice END as 'NBP/OBP'
FROM Product p
JOIN Category c
ON c.categoryId = p.categoryId
JOIN Current_Price curr
ON curr.sku = p.sku
JOIN Price_Back_Previous_Days prev
ON prev.sku = p.sku
AND (prev.buyPrice <> curr.buyPrice
OR prev.sellPrice <> curr.sellPrice)
Which yields the expected
SKU TITLE CATEGORYNAME OLDBUYPRICE OLDSELLPRICE NEWBUYPRICE NEWSELLPRICE NBP/OBP
100 Apple Fruit 1 2 4 8 4
(Have a working SQL Fiddle Example, with an specific date substituted for GETDATE() for future-reasons.)
You'll notice that I'm using -3 (and not -4, as might be expected), because I'm attempting to retrieve the same value for 'as of this date', regardless of when (during the day) the query is run. Obviously the 'current price' can still change (although adding a timestamp parameter there as well could fix that); however, this should make sure that you're looking at a consistent price throughout a given day.
Try this: SQL Fiddle
Select
tt.SKU,tt.BuyPrice,tt.SellPrice,tr.BuyPrice As NewBuyPrice,tr.SellPrice As NewSellPrice, tr.BuyPrice/tt.BuyPrice NNBP
From
(
select SKU, Max(Timestamps) timestamps
from t
Group by t.SKU
) t
Join t tr on t.SKU = tr.SKU AND t.timestamps = tr.Timestamps
Join t tt ON t.SKU = tt.SKU
AND DATEDIFF(D, tt.timestamps, t.timestamps) = 4
AND tt.BuyPrice <> tr.BuyPrice
I tried to create the table oldtpc which will get the products which has maximun of date after X number of days. And another newtpc which has prices with most recent date. And in the on condition between 'oldtpc' and and 'newtpc' I am checking that those dates do not match
select tp.SKU, tp.Title, tc.CategoryName, oldtpc.BuyPrice, oldtpc.Sellprice, newtpc.buyprice, newtpc.Sellprice
from tblProducts tp
join tblCategories tc
on tp.CategoryId= tc.CateogryId
join (select SKU, BuyPrice, SellPrice, max(TimeStamp) as TimeStamp
from tblPrices new
where DATEDIFF ( dd, timestamp, getdate()) < #xdays
group by SKU, BuyPrice, SellPrice ) as newtpcnewtpc
on tp.SKU = newtpc .sku
join (select SKU, BuyPrice, SellPrice, max(TimeStamp) as TimeStamp
from tblPrices old
where DATEDIFF ( dd, timestamp, getdate()) >= #xdays
group by SKU, BuyPrice, SellPrice ) as oldtpc
on oldtpc.SKU = tp.SKU and oldtpc.timestamp <> newtpc.timestamp
PS: some syntax might be wrong, but I think the general idea should work fine