Retrieve column based on closest date - sql-server

Thanks for your help in advance. I have an invoice table which is similar to below:
INV DATE | ITEM |SELL PRICE |COST PRICE
------------+-------+-----------+----------
30/06/2016 | DOOR1 |10 |5
The above item is sourced from the EU so I have another table (VEN_HIS) which lists a history of supplier prices for that item as below:
DATE |ITEM |CURRENCY |PRICE
------------+-------+-----------+------
17/05/2017 |DOOR1 |EUR |6
01/01/2017 |DOOR1 |EUR |5.8
29/05/2016 |DOOR1 |EUR |5.6
05/03/2016 |DOOR1 |EUR |5.5
What I want to do is join the tables to drop in the correct currency price we would have paid onto the invoice table as at the invoice date in other words the correct price is 5.6
This is my first post and I need to get some FX analysis done quickly so apologies for the formatting, any help would be greatly appreciated.

Its a simple question of Join and where clause.
create table #t1 (INV_DATE DATE,ITEM varchar(100), SELL_PRICE int, COST_PRICE int);
insert into #t1 values
('30/06/2016','DOOR1',10,5)
create table #ven_his (colDATE date, ITEM varchar(100), CURRENCY varchar(100),PRICE float);
insert into #ven_his values
('17/05/2017', 'DOOR1', 'EUR',6 ),
('01/01/2017', 'DOOR1', 'EUR',5.8),
('29/05/2016', 'DOOR1', 'EUR',5.6),
('05/03/2016', 'DOOR1', 'EUR',5.5)
SELECT top 1 a.INV_DATE, a.ITEM, b.CURRENCY, b.PRICE
FROM #t1 a
left join #ven_his b
on a.ITEM = b.ITEM
WHERE b.colDATE <=a.INV_DATE
ORDER BY b.colDATE DESC
output :
#t1
INV_DATE ITEM SELL_PRICE COST_PRICE
---------- ------ ----------- -----------
2016-06-30 DOOR1 10 5
#ven_his
colDATE ITEM CURRENCY PRICE
---------- --------- ------------ ------
2017-05-17 DOOR1 EUR 6
2017-01-01 DOOR1 EUR 5.8
2016-05-29 DOOR1 EUR 5.6
2016-03-05 DOOR1 EUR 5.5
INV_DATE ITEM CURRENCY PRICE
---------- --------- ----------- --------
2016-06-30 DOOR1 EUR 5.6

DECLARE #invoice table (INV_Date date, Item varchar(100),SellPrice numeric(5,2), Costprice numeric(5,2))
INSERT INTO #invoice
SELECT '20160630','DOOR1',10,5
SELECT * FrOm #invoice
DECLARE #ven_his table(Dates date,Item varchar(100),Currency varchar(10),Price numeric(5,2))
INSERT INTO #ven_his
SELECT '20170517','DOOR1','EUR',6
UNION ALL
SELECT '20170101','DOOR1','EUR',5.8
UNION ALL
SELECT '20160529','DOOR1','EUR',5.6
UNION ALL
SELECT '20160305','DOOR1','EUR',5.5
SELECT * FROM #ven_his
SELECT Top 1 i.Item,INV_Date,PRICE,Dates
FROM #invoice i
JOIN #ven_his v
ON i.Item = v.Item
ORDER BY ABS(DATEDIFF(DAY,i.INV_Date,v.Dates))

Use LEAD function to return the correct price for all items:
Create table #Invoice (INVDATE DATE, ITEM NVARCHAR(20), SELLPRICE SMALLMONEY, COSTPRICE SMALLMONEY)
INSERT #INVOICE VALUES ('2016-06-30', 'DOOR1', 10, 5)
--Test with additional invoices
--INSERT #INVOICE VALUES ('2017-02-22', 'DOOR1', 10, 5), ('2016-09-22', 'DOOR1', 10, 5)
CREATE TABLE #VENHIS ([DATE] DATE, ITEM NVARCHAR(20), CURRENCY NVARCHAR(5), PRICE SMALLMONEY)
INSERT #VENHIS VALUES ('2017-05-17', 'DOOR1', 'EUR', 6), ('2017-01-01', 'DOOR1', 'EUR', 5.8),('2016-05-29', 'DOOR1', 'EUR', 5.6), ('2016-03-05', 'DOOR1', 'EUR', 5.5)
SELECT I.*, V.PRICE
FROM #Invoice i
INNER JOIN (SELECT *, [DATE] as STARTDATE, LEAD([DATE]) OVER (PARTITION BY ITEM ORDER BY ITEM, [DATE] ASC) AS EndDate
FROM #VENHIS ) v ON i.ITEM=v.ITEM AND I.[INVDATE] BETWEEN V.STARTDATE AND DATEADD(D,-1,ISNULL(V.ENDDATE, '2099-01-01'))

Related

Repeated data on inserted rows

--demo setup
drop table if exists dbo.product
go
create table dbo.Product
(
ProductId int,
ProductTitle varchar(55),
ProductCategory varchar(255),
Loaddate datetime
)
insert into dbo.Product
values (1, 'Table', 'ABCD', '3/4/2018'),
(1, 'Table', 'ABCD', '3/5/2018'),
(1, 'Table', 'ABCD', '3/6/2018'),
(1, 'Table', 'XYZ', '3/7/2018'),
(1, 'Table', 'XYZ', '3/8/2018'),
(1, 'Table', 'XYZ', '3/9/2018'),
(1, 'Table', 'GHI', '3/10/2018'),
(1, 'Table', 'GHI', '3/11/2018'),
(1, 'Table', 'XYZ', '3/12/2018'),
(1, 'Table', 'XYZ', '3/13/2018')
SELECT
product.productid,
product.producttitle,
product.productcategory,
MIN(product.loaddate) AS BeginDate,
-- ,max(product.LoadDate) as BeginDate1
CASE
WHEN MAX(product.loaddate) = MAX(oa.enddate1)
THEN '12/31/9999'
ELSE MAX(product.loaddate)
END AS EndDate
FROM
dbo.product product
CROSS APPLY
(SELECT MAX(subproduct.loaddate) EndDate1
FROM dbo.product subproduct
WHERE subproduct.productid = product.productid) oa
GROUP BY
productid, producttitle, productcategory
Output:
productid
producttitle
productcategory
BeginDate
EndDate
1
Table
ABCD
2018-03-04 00:00:00.000
2018-03-06 00:00:00.000
1
Table
XYZ
2018-03-07 00:00:00.000
9999-12-31 00:00:00.000
1
Table
GHI
2018-03-10 00:00:00.000
2018-03-11 00:00:00.000
Desired output:
productid
producttitle
productcategory
BeginDate
EndDate
1
Table
ABCD
2018-03-04 00:00:00.000
2018-03-06 00:00:00.000
1
Table
XYZ
2018-03-07 00:00:00.000
2018-03-09 00:00:00.000
1
Table
GHI
2018-03-10 00:00:00.000
2018-03-11 00:00:00.000
1
Table
XYZ
2018-03-12 00:00:00.000
9999-12-31 00:00:00.000
The last two inserted rows repeat the data from Loaddate '3/7/2018'-'3/9/2018', this doesn't happen if any of the new inserted rows doesn't repeat data. The only thing that changes is the LoadDate, giving me incorrect output. how can i get something like that desired output?
Well, first of all, you need to find a sequence number over all your records. If you already have a primary key, that's good. In example you gave us, there's no such column, so let's generate it.
Then, we make pairs with start and end dates for each product's category change. Another thing is to group all these product's category changes.
Finally, we make just a simple group by:
;
with cte as ( select *,
row_number() over(partition by ProductId order by Loaddate) as rn
from product
), cte2 as ( select t1.ProductId,
t1.ProductTitle,
t1.ProductCategory,
t1.Loaddate as BeginDate,
case
when t1.ProductCategory <> t2.ProductCategory
then t1.Loaddate
else coalesce(t2.Loaddate, null)
end as EndDate,
row_number() over(order by t1.ProductId, t1.Loaddate) as rn_overall,
row_number() over(partition by t1.ProductId, t1.ProductCategory order by t1.Loaddate) as rn_category
from cte as t1
left join cte as t2
on t2.ProductId = t1.ProductId
and t2.rn = t1.rn + 1
), cte3 as ( select *,
min(rn_overall) over (partition by ProductId, ProductCategory, rn_overall - rn_category) as product_group
from cte2
)
select ProductId, ProductTitle, ProductCategory,
min(BeginDate) as BeginDate,
case
when max(case when EndDate is null then 1 else 0 end) = 0
then max(EndDate)
else null
end as EndDate
from cte3
group by ProductId, ProductTitle, ProductCategory, product_group
order by ProductId, BeginDate

SQL UPDATE rows from related TOP 1 records

I have a table Products with 2 columns: ProductID, ProductName.
When I sell those products, I store the sale in 2 tables:
SalesHeaders with 3 columns: IDHeader, EmployeeName, Date
SalesRows with 3 columns: IDHeader, IDRow, ProductID
There are 2 employees, John and Mary.
Now I need to add a new column in my table Products called LastMarySaleDate. So I want to update Products.LastMarySaleDate and for that I need to select the most recent record from SalesHeaders where EmployeeName = 'mary' and SalesRows.ProductID = Products.ProductID.
Here are sample data with the expected results
Products (before UPDATE):
ProductID ProductName LastMarySaleDate
--------- ------------ ----------------
A01 Mouse
A02 Keyboard
A03 Speakers
SalesHeaders:
IDHeader EmployeeName Date
-------- ------------ ----------
1 Mary 2020-05-01
2 Mary 2020-05-02
3 John 2020-05-03
SalesRows:
IDHeader IDRow ProductID
-------- ----- ---------
1 1 A01
1 2 A02
2 3 A01
3 4 A02
3 5 A03
Products (after UPDATE):
ProductID ProductName LastMarySaleDate
--------- ------------ ----------------
A01 Mouse 2020-05-02
A02 Keyboard 2020-05-01
A03 Speakers Note: Empty, since Mary never sold this productID
I've tried
UPDATE Products
SET Products.LastMarySaleDate = H.Date
FROM
(SELECT TOP 1 *
FROM SalesHeaders
LEFT OUTER JOIN SalesRows ON SalesHeaders.IDHeader = SalesRows.IDHeader
WHERE SalesHeaders.EmployeeName = 'Mary'
AND SalesRows.ProductID = Products.ProductID
ORDER BY SalesHeaders.Date DESC) AS H
but I can't figure it out. If anyone could help me with this it would be great, thanks!
I think this is what you are looking for:
CREATE TABLE #Product (ProductID varchar(10), ProductName varchar(20), LastMarySaleDate DATE)
INSERT INTO #Product
VALUES ('A01','Mouse', NULL),
('A02','Keyboard',NULL),
('A03','Speakers',NULL)
CREATE TABLE #SalesHeader(IDHeader INT, EmployeeName varchar(20), DT Date)
INSERT INTO #SalesHeader VALUES (1,'Mary','2020-05-01'),
(2,'Mary','2020-05-02'),
(3,'John','2020-05-03')
CREATE TABLE #SalesRows(IDHeader int, IDRow int, ProductID varchar(10))
INSERT INTO #SalesRows VALUES(1,1,'A01'),
(1,2,'A02'),
(2,3,'A01'),
(3,4,'A02'),
(3,5,'A03')
UPDATE #Product
SET LastMarySaleDate = t2.DT
FROM #Product t1
INNER JOIN
(
SELECT ProductID, MAX(dt) DT
FROM #SalesHeader t1
INNER JOIN #SalesRows t2 on t1.IDHeader = t2.IDHeader
WHERE t1.EmployeeName = 'Mary'
GROUP BY ProductID
) t2 on t1.ProductID = t2.ProductID

Want Result from one to many relation table by Group By

I would like to get result by "group by" but not luck. I can see other option but looking best perfomance query.
Thanks in Advance !
CREATE TABLE #Invoice (InvoiceId int, InvoiceDate datetime, NetAmount
decimal(18,2))
CREATE TABLE #Payment (PaymentId int, InvoiceId int, PaidAmount
decimal(18,2))
INSERT INTO #Invoice VALUES (101, '20180212', 5000)
INSERT INTO #Invoice VALUES (102, '20180112', 600)
INSERT INTO #Invoice VALUES (103, '20181211', 1800)
INSERT INTO #Invoice VALUES (104, '20180101', 1000)
INSERT INTO #Invoice VALUES (105, '20180212', 7000)
INSERT INTO #Payment VALUES (101,103,1800)
INSERT INTO #Payment VALUES (102,102,500)
INSERT INTO #Payment VALUES (103,101,2000)
INSERT INTO #Payment VALUES (103,101,3000)
Create this query :
SELECT
INV.InvoiceDate,
SUM(Inv.NetAmount) as NetAmount,
SUM(ISNULL(PY.PaidAmount,0)) As PaidAmount
From #Invoice INV
LEFT JOIN #Payment PY
ON PY.InvoiceId = INV.InvoiceId
GROUP BY
INV.InvoiceDate
Get Result :
InvoiceDate NetAmount PaidAmount
2018-01-01 1000.00 0.00
2018-01-12 600.00 500.00
2018-02-12 17000.00 5000.00 ****Issue: Net values should be 12000 not 17000
2018-12-11 1800.00 1800.00
Expected Result :
InvoiceDate NetAmount PaidAmount
2018-01-01 1000.00 0.00
2018-01-12 600.00 500.00
2018-02-12 5000.00 5000.00
2018-12-11 1800.00 1800.00
This assumes that the expected results the OP has provided is wrong. They have the value 5000 for the NetAmount on 20180212, however there are 2 invoices on that date with the values 5000 and 7000, making 12000. If 5000 is correct, we need details on why the value of invoice 105 (7000) is not to be included.
Anyway, on the assumption the expected results is wrong, this gets the result I believe you are looking for:
WITH CTE AS(
SELECT I.InvoiceId,
I.InvoiceDate,
I.NetAmount,
P.PaymentId,
P.PaidAmount,
ROW_NUMBER() OVER (PARTITION BY I.InvoiceId, I.InvoiceDate ORDER BY (SELECT NULL)) AS RN
FROM #Invoice I
LEFT JOIN #Payment P ON I.InvoiceId = P.InvoiceId)
SELECT C.InvoiceDate,
SUM(CASE RN WHEN 1 THEN C.NetAmount END) AS NetAmount,
ISNULL(SUM(C.PaidAmount),0) AS PaidAmount
FROM CTE C
GROUP BY C.InvoiceDate;

How do i select the free time between start time and end time in sql?

I want to select all the free time other that the mentioned ones for which the room is free.
eg table:
room starttime endtime date
1 | 12pm | 1pm | 2018-02-18 00:00:00.000
1 | 2pm | 3pm | 2018-02-18 00:00:00.000
1 | 3pm | 5pm | 2018-02-18 00:00:00.000
expected output eg:
freetime
6-12,5-10
Is this even possible?
Thanks in advance.
This is for a assignment where the room is booked for a class for particular starttime(date) and endtime(date) and date.
I need to find out the times when the class will be free.
Above free timings was just an example.
Possible solution:
set dateformat ymd
-- Sample data
declare #MyTable
table (room varchar(1), starttime varchar(4), endtime varchar(4), [date] datetime)
insert into #MyTable values ('1', '12pm', '1pm', '2018-02-18 00:00:00.000')
insert into #MyTable values ('1', '2pm', '3pm', '2018-02-18 00:00:00.000')
insert into #MyTable values ('1', '3pm', '5pm', '2018-02-18 00:00:00.000')
select d.[date], r.room, t.freehour
from
(select cast(dateadd(hour, number, 0) as time) as freehour
from master..spt_values where type = 'P' and number between 6 and 22) t,
(select distinct room from #MyTable) r,
(select distinct [date] as [date] from #MyTable) d
where not exists (
select 1
from #MyTable m
where m.[date] = d.[date]
and m.room = r.room
and t.freehour between cast(m.starttime as time)
and dateadd(hour,-1,cast(m.endtime as time))
)

JOIN on multilple rows to SELECT only TOP row in SQL Sever

I have a table which has only 1 row per ID/Date while the other one can have overlapping ID/Dates. I need to JOIN them in such a way that only top row is selected (actually any row from duplicates table is fine!). Its fairly easy to do this in 2 steps (Insert & Update) but I'm looking if it can be done in a single step.
CREATE TABLE #Row1Each (ID VARCHAR(10), Date_ DATETIME, Value FLOAT) INSERT INTO #Row1Each SELECT 'AAPL', '1/10/2015', 100 INSERT INTO #Row1Each SELECT 'MSFT', '1/10/2015', 20
CREATE TABLE #Table1 (ID VARCHAR(10), Date_ DATETIME, Qty FLOAT) INSERT INTO #Table1 SELECT 'AAPL', '1/10/2015', 55000
CREATE TABLE #Duplicates (ID VARCHAR(10), StartDate DATETIME, EndDate DATETIME, Quote FLOAT) INSERT INTO #Duplicates SELECT 'AAPL', '1/2/2015', '12/31/2016', 0.1 INSERT INTO #Duplicates SELECT 'AAPL', '1/4/2015', '10/05/2016', 0.11
/*
AAPL 2015-01-10 00:00:00.000 100
MSFT 2015-01-10 00:00:00.000 20
AAPL 2015-01-10 00:00:00.000 55000
AAPL 2015-01-02 00:00:00.000 2016-12-31 00:00:00.000 0.1
AAPL 2015-01-04 00:00:00.000 2016-10-05 00:00:00.000 0.1
*/
SELECT A.*
, B.Qty
, C.Quote
FROM #Row1Each A
LEFT JOIN #Table1 B ON B.ID = A.ID
AND B.Date_ = A.Date_
LEFT JOIN #Duplicates C ON C.ID = A.ID
AND A.Date_ BETWEEN C.StartDate AND C.EndDate
DROP TABLE #Row1Each DROP TABLE #Table1 DROP TABLE #Duplicates
/* Desired output
AAPL 2015-01-10 00:00:00.000 100 55000 0.1
MSFT 2015-01-10 00:00:00.000 20 NULL NULL
*/
You can do this using APPLY.
SELECT
r.Id,
r.Date_,
r.Value,
t.Qty,
d.Quote
FROM #Row1Each r
LEFT JOIN #Table1 t
ON t.ID = r.ID
AND t.Date_ = r.Date_
OUTER APPLY(
SELECT TOP 1 *
FROM #Duplicates d
WHERE
d.ID = r.ID
AND r.Date_ BETWEEN d.StartDate AND d.EndDate
ORDER BY EndDate DESC -- Returns Top 1 Based on EndDate
)d

Resources