MAX Date <= to Date in another table - tsql

We store exchange rates in a table and it is updated a few times a week for the latest exchange rates. I am trying to be able to take a date on a sales order, and assign it the exchange rate that was active at the time of the order. Below is an example of a listing of exchange rates and their effective dates, as well as sales orders, and what should be the effect rate (Exchage rate # time of order, which is what I want to pull in).
currate.effectivedate currate.currentrate
12/2/2012 0.55
12/7/2012 0.52
12/13/2012 0.54
12/19/2012 0.53
12/22/2012 0.56
orderhed.orderdate orderhed.ordernum Exchage Rate # date of order
12/4/2012 105 0.55
12/8/2012 111 0.52
12/9/2012 116 0.52
12/19/2012 120 0.53
12/28/2012 123 0.56
I have below an example of what I have been trying to do, but its not working. First, I can’t think of how to bring the currate.currentrate field in because of the MAX function on currate.effectivedate. Then for t_rate.effectivedate, I am getting just the MAX day of the exchange rate of the table period, not the max date that is <= the orderhed.orderdate. I need the MAX currate.effectivedate that is less than or equal to the orderhed.orderdate in order to get the effective date (and corresponding currentrate.currentrate) that was in effect at the time of the order. I am sorry if I am repeating myself, or if it doesn’t make sense. If it isn’t obvious by my sad little query below, I am extremely new to writing queries so any help I can get is greatly appreciated.
SELECT
orderhed.ordernum,
orderhed.orderdate,
orderdtl.orderline,
orderdtl.unitprice,
orderdtl.docunitprice,
orderdtl.discount,
orderdtl.docdiscount,
t_rate.effectivedate
FROM orderhed LEFT OUTER JOIN
(SELECT rate1.company, MAX(rate1.effectivedate) AS effectivedate, order1.orderdate
FROM currrate as rate1 INNER JOIN
orderhed AS order1 ON rate1.company = order1.company
WHERE (rate1.company = 'lotcol') AND (rate1.currencycode = 'usd')
GROUP BY rate1.company, order1.orderdate)
AS t_rate ON orderhed.company = t_rate.company AND t_rate.orderdate <= orderhed.orderdate
INNER JOIN
orderdtl ON orderhed.company = orderdtl.company
WHERE (orderhed.company = 'lotcol')
Update---
thanks #LastCoder you answer is what nudged me in the right direction. At the end of the day, this is exactly what worked:
SELECT orderhed.ordernum, orderhed.orderdate, orderdtl.orderline, orderdtl.unitprice, orderdtl.docunitprice, orderdtl.discount, orderdtl.docdiscount, t_rate.effectivedate,
t_rate.ordernum AS Expr1, t_rate1.currentrate
FROM orderhed INNER JOIN
(SELECT rate1.company, MAX(rate1.effectivedate) AS effectivedate, order1.orderdate, order1.ordernum
FROM currrate AS rate1 INNER JOIN
orderhed AS order1 ON rate1.company = order1.company AND rate1.effectivedate <= order1.orderdate
WHERE (rate1.company = 'lotcol') AND (rate1.currencycode = 'usd')
GROUP BY rate1.company, order1.orderdate, order1.ordernum) AS t_rate ON orderhed.ordernum = t_rate.ordernum AND orderhed.company = t_rate.company AND
t_rate.orderdate <= orderhed.orderdate INNER JOIN
(SELECT company, currencycode, effectivedate, currentrate
FROM currrate AS currrate_1
WHERE (company = 'lotcol') AND (currencycode = 'usd')) AS t_rate1 ON t_rate.company = t_rate1.company AND
t_rate.effectivedate = t_rate1.effectivedate INNER JOIN
orderdtl ON orderhed.company = orderdtl.company AND orderhed.ordernum = orderdtl.ordernum
WHERE (orderhed.company = 'lotcol')
ORDER BY orderhed.ordernum

SELECT o.orderdate
, ISNULL(r.effectivedate, (
SELECT MAX(effectivedate) FROM t_rate WHERE effectivedate < o.orderdate
)
FROM orderhed o
LEFT JOIN t_rate r ON o.orderdate = r.effectivedate
There's a minimal solution to your issue. You need to match the "orderdate" to the "effectivedate" that is equal to or closest less than it.

Related

How to calculate values from previous month to current month

I want to calculate this table. Everytime that there is a new participant per month it will add the previous value to current value.
month
no_participant
2021-01
10
2021-02
20
2021-03
5
2021-04
17
Something like this, output
month
no_participant
count
2021-01
10
10
2021-02
20
30
2021-03
5
35
2021-04
17
52
Here's my query: I am using Postgres. Thanks to your help
SELECT (TO_CHAR(CSD.SCHEDULED_START_DATETIME, 'YYYY-MM'))AS MONTH,
COUNT(DISTINCT PARTICIPANT_ID) AS PARTICIPANT
FROM TSUP.COURSE_SCHEDULE_DETAIL AS CSD
INNER JOIN TSUP.COURSE_PARTICIPANT AS CP
ON CSD.COURSE_SCHEDULE_ID = CP.COURSE_SCHEDULE_ID
INNER JOIN(
SELECT
MIN(COALESCE(CSD.RESCHEDULED_START_DATETIME, CSD.SCHEDULED_START_DATETIME)) AS SCHEDULED_START_DATETIME,
MAX(COALESCE(CSD.RESCHEDULED_END_DATETIME, CSD.SCHEDULED_END_DATETIME)) AS SCHEDULED_END_DATETIME,
COUNT(CSD.SCHEDULED_START_DATETIME) AS "COUNT"
FROM TSUP.COURSE_SCHEDULE_DETAIL AS CSD
INNER JOIN (
SELECT CP.PARTICIPANT_ID AS "PARTICIPANT",
MIN(COALESCE(CSD.RESCHEDULED_START_DATETIME, CSD.SCHEDULED_START_DATETIME)) AS SCHEDULED_START_DATETIME,
MAX(COALESCE(CSD.RESCHEDULED_END_DATETIME, CSD.SCHEDULED_END_DATETIME)) AS SCHEDULED_END_DATETIME
FROM TSUP.COURSE_PARTICIPANT AS CP
INNER JOIN TSUP.COURSE_SCHEDULE_DETAIL AS CSD
ON CP.COURSE_SCHEDULE_ID = CSD.COURSE_SCHEDULE_ID
INNER JOIN TSUP.COURSE_SCHEDULE AS CS
ON CSD.ID = CS.ID
INNER JOIN TSUP.COURSE AS C
ON CS.COURSE_ID = C.ID
INNER JOIN TSUP.COURSE_CATEGORY AS CC
ON C.COURSE_CATEGORY_ID = CC.ID
INNER JOIN TSUP.EMPLOYEE AS E
ON CP.PARTICIPANT_ID = E.ID
INNER JOIN TSUP.MEMBER_ROLE AS MR
ON E.MEMBER_ROLE_ID = MR.ID
WHERE C.MANDATORY = 'Yes'
AND MR.ROLE_TYPE = 'Dev'
AND CC.CATEGORY = 'JJ'
GROUP BY CP.PARTICIPANT_ID)
TEMP ON CSD.SCHEDULED_START_DATETIME = TEMP.SCHEDULED_START_DATETIME
GROUP BY CSD.RESCHEDULED_START_DATETIME, CSD.SCHEDULED_START_DATETIME,
CSD.RESCHEDULED_END_DATETIME, CSD.SCHEDULED_END_DATETIME
)
TEMP ON CSD.SCHEDULED_START_DATETIME = TEMP.SCHEDULED_START_DATETIME
GROUP BY MONTH
The query you provided is verbose, and also does not seem to exactly line up with the sample data. I will give the following query based on the sample data shown:
SELECT month, no_participant, SUM(no_participant) OVER (ORDER BY month) AS count
FROM yourTable
ORDER BY month;
The above logic uses SUM() as an analytic function.

Correlated subquery in Postgres

I have a query like below to find the stock details of certain products.The query is working fine but i think it is not efficient and fast enough(DB: postgresql version 11).
There is a CTE "result_set"in this code where i need to find the "quantity of a product ordered"(qty_last_7d_from_oos_date) during the period between out of stock and last 7 days before out of stock date.Same like this i have to find the revenue also.
So what i did is wrote a same subquery two times one outputting the revenue and other the quantity which is not an efficient step.So someone have any suggestions on how to rewrite this and make it an efficient code.
WITH final as
(
SELECT product_id,product_name,item_sku,out_of_stock_at
,out_of_stock_at - INTERVAL '7 days' as previous_7_days
,back_in_stock_at
FROM oos_base
)
SELECT product_id,product_name,item_sku,out_of_stock_at,previous_7_days
,back_in_stock_at
,(SELECT coalesce(sum(i.qty_ordered), 0) AS qty_last_7d_from_oos_date
FROM ol.orders o
LEFT JOIN ol.items i ON i.order_id = o.order_id
LEFT JOIN ol.products p ON p.product_id = i.product_id AND i.store_id = p.store_id
WHERE o.order_state_2 IN('complete','processing')
AND f.product_id=p.product_id
AND o.created_at_order :: DATE BETWEEN f.previous_7_days::DATE AND COALESCE(f.out_of_stock_at::DATE,current_date)
)
,( SELECT coalesce(sum(i.row_amount_minus_discount_order), 0) AS rev_last_7d_from_oos_date
FROM ol.orders o
LEFT JOIN ol.items i ON i.order_id = o.order_id
LEFT JOIN ol.products p ON p.product_id = i.product_id AND i.store_id = p.store_id
WHERE o.order_state_2 IN('complete','processing')
AND f.product_id=p.product_id
AND o.created_at_order :: DATE BETWEEN f.previous_7_days::DATE AND COALESCE(f.out_of_stock_at::DATE,current_date)
)
FROM final f
In the above code the CTE "final" gives you two dates "out_of_stock_at" &
"previous_7_days". I want to find the quantity and revenue of a product based on this 2 dates means between "previous_7_days" & "out_of_stock_at".
Below query will give the quantity and revenue of the products but the period between "previous_7_days" & "out_of_stock_at"from the above CTE.
As of now i have used the below code two times to obtain the information of revenue and quantity.
SELECT coalesce(sum(i.qty_ordered), 0) AS qty ,
coalesce(sum(i.row_amount_minus_discount_order), 0)
FROM ol.orders o
LEFT JOIN ol.items i ON i.order_id = o.order_id
LEFT JOIN ol.products p ON p.product_id = i.product_id AND i.store_id = p.store_id
WHERE o.order_state_2 IN('complete','processing')
AND f.product_id=p.product_id
AND o.created_at_order :: DATE BETWEEN f.previous_7_days::DATE AND COALESCE(f.out_of_stock_at::DATE,current_date)

Joining based on dates

I have two sets of data that I need to join.
However there is no ID or similar to join the sets, the only common factor between the two sets are the dates used.
Unfortunately the dates are not 100% identical, typically it’s a few minutes or seconds in difference between the two dates.
What I’m hoping is that it would be possible to join based on the dates, if the difference between date for example is less than 10minutes.
Is this something that could be possible?
Example data:
> EventDate WindSpeed
> 2018-01-09 12:00:18.000 3
> 2018-01-10 12:00:03.000 4
Then join with:
> ReadingDate ReadingValue
> 2018-01-09 12:00:00.000 4,6
> 2018-01-10 12:00:00.000 5
So far I have the two below queries that I’m not able to join by myself, would appreciate any help or hints to get this working:
SELECT tu.Name,
tv.VoyageNo,
tv.ExternalVoyageNo,
tve.VoyageEventCode,
tve.EventDate,
tve.WindSpeed,
tve.WindDirection,
tve.SeaStateCode,
tve.PositionLatitude,
tve.PositionLongitude,
tve.Speed,
tve.DistanceByLog,
tve.DistanceOverGround,
tve.HFOStock,
tve.LSFOStock,
tve.MDOStock,
tve.MGOStock,
tve.DraughxcID,
CASE
WHEN ts.IsBallast = 0
THEN 'Laden'
WHEN ts.IsBallast = 1
THEN 'Ballast'
ELSE 'In Port'
END AS Condition,
tcf.Mt
FROM dbo.xcVoyageEvent tve
INNER JOIN dbo.xcUnit tu ON tve.xcUnitID = tu.xcUnitID
INNER JOIN dbo.xcVoyage tv ON tve.xcVoyageID = tv.xcVoyageID
LEFT JOIN dbo.xcSailing ts ON tve.xcSailingID = ts.xcSailingID
LEFT JOIN dbo.xcCargo tc ON tve.xcVoyageID = tc.xcVoyageID
LEFT JOIN dbo.xcCargoFigure tcf ON tc.xcCargoID = tcf.xcCargoID
WHERE tve.RowDeleted = 0
AND tv.RowDeleted = 0
AND ts.RowDeleted = 0
AND tu.RowDeleted = 0
AND tve.VoyageEventCode IN('Commence Sea Passage', 'Sea Passage
Suspended', 'Sea Passage Resumed', 'End Of Sea Passage', 'xcdailyreport',
'Noon position', 'morning position', 'voyage commenced', 'voyage complete',
'Enter Magallanes Strait', 'Enter Suez Canal', 'All Clear', 'All Fast')
ORDER BY tu.Name,
tve.EventDate;
SELECT tu.name,
tc.Code,
tc.Name,
xc.Name,
xcr.ReadingValue,
xcr.ReadingDate,
xcr.Comment
FROM dbo.xcMeasurement xc
INNER JOIN dbo.xcMeasurementReading xcr ON xc.xcMeasurementID =
xcr.xcMeasurementID
INNER JOIN dbo.xcComponent tc ON xc.xcComponentID = tc.xcComponentID
INNER JOIN dbo.xcUnit tu ON xc.xcUnitID = tu.xcUnitID
WHERE xc.RowDeleted = 0
AND xcr.RowDeleted = 0
AND tu.RowDeleted = 0
AND xc.Consumption = 1
ORDER BY tu.Name,
tc.code,
xcr.ReadingDate
You mentioned that there is a difference between the two dates in the different tables,
if it's just 1 "event" per day, you could join on either Date, or Datetime.
if you have multiple events per day, you'd also have to consider using Datediff in my example i used 600 seconds difference
See my example below
create table #temp1
( EventDate Datetime2, Windspeed int)
create table #temp2
( Readingdate Datetime2, Readingvalue nvarchar(50))
insert into #temp1 Values
('2018-01-09 12:00:18.000', 3),
('2018-01-09 13:10:00.000', 3),
('2018-01-10 12:00:03.000', 4)
insert into #temp2 Values
('2018-01-09 12:00:00.000', '5,6'),
('2018-01-09 13:00:00.000', '6'),
('2018-01-10 12:00:00.000', '5')
SELECT
*,DATEDIFF(S,T2.Readingdate, T1.EventDate) [dif_in_sec]
FROM #temp1 AS T1
INNER JOIN #TEMP2 AS T2 ON CAST(T1.EventDate AS date) = CAST(T2.Readingdate AS DATE)
AND( DATEDIFF(S,T2.Readingdate, T1.EventDate) <= 600
and DATEDIFF(S,T2.Readingdate, T1.EventDate) >= 0)
You might have to tweak with the values
To get rows with EventDate and ReadingDate within 10 minutes add this to your where statement:
ABS(DATEDIFF(MINUTE, EventDate, ReadingDate)) <= 10

Need to add a calculated field using T-SQL

I have a Query that I would like to add a Calculated field to. I need to add Projected Sales for 2013. The calculation would be the current count of months divided by the total year's sales times 12. I have a field for FiscalMonthNum that is not in the query yet. Can someone please show me how I could add the field I need?
SELECT
a.Vendor,
vn.ACNAME AS Vendor_Name,
a.FiscalYear,
a.QtySold,
a.ExtCost
FROM
dbo.S2K_VEND vn
INNER JOIN
(SELECT
sd.IFPRVN AS Vendor,
fc.FiscalYear,
SUM(sd.SBQSHP) AS QtySold,
SUM(sd.SBEPRC) AS ExtCost
FROM
dbo.SalesData sd
LEFT OUTER JOIN dbo.FiscalCalendar fc ON fc.FiscalDate = sd.SBINDT
WHERE
sd.SBTYPE = 'O'
AND
sd.SBINDT > '2011-12-31'
AND
sd.SBCLS NOT IN ('1500')
GROUP BY
sd.IFPRVN,
fc.FiscalYear
)a
ON vn.ACVEND = a.Vendor
GROUP BY
a.Vendor,
vn.ACNAME,
a.FiscalYear,
a.QtySold,
a.ExtCost
Would the following do the trick? Dividing the sales by the maximum month number and then multiplying by 12?
SELECT
a.Vendor,
vn.ACNAME AS Vendor_Name,
a.FiscalYear,
a.QtySold,
a.ExtCost,
a.PredictedQtySold,
a.PredictedExtCost
FROM
dbo.S2K_VEND vn
INNER JOIN
(SELECT
sd.IFPRVN AS Vendor,
fc.FiscalYear,
12 * (SUM(sd.SBQSHP)/MAX(FiscalMonthNumber)) AS PredictedQtySold,
12 * (SUM(sd.SBEPRC)/MAX(FiscalMonthNumber)) AS PredictedExtCost,
SUM(sd.SBQSHP) AS QtySold,
SUM(sd.SBEPRC) AS ExtCost
FROM
dbo.SalesData sd
LEFT OUTER JOIN dbo.FiscalCalendar fc ON fc.FiscalDate = sd.SBINDT
WHERE
sd.SBTYPE = 'O'
AND
sd.SBINDT > '2011-12-31'
AND
sd.SBCLS NOT IN ('1500')
GROUP BY
sd.IFPRVN,
fc.FiscalYear
)a
ON vn.ACVEND = a.Vendor
GROUP BY
a.Vendor,
vn.ACNAME,
a.FiscalYear,
a.QtySold,
a.ExtCost
;

TOP N problem with GROUP BY clause

The problem: I need to find all active [GiftPledges] that have the last three [GiftDetails] have a zero amount.
SELECT gp.PledgeId FROM GiftPledge gp
INNER JOIN GiftDetail gd ON gp.PledgeId = gd.PledgeId
WHERE gp.PledgeStatus = 'A'
GROUP BY PledgeId
HAVING COUNT(PledgeId) >= 3
Now, I have all my [GiftPledges] that have at least three [GiftDetails].
SELECT TOP 3 gdi.Amt FROM GiftDetail gdi
INNER JOIN GiftHeader ghi ON gdi.GiftRef = ghi.GiftRef
WHERE gdi.PledgeId = gp.PledgeId
ORDER BY ghi.GDate DESC
This gives me the three most recent [GiftDetails] associated with a given [GiftPledge]. The problem is that I don't know how to sum the second query and have it be a part of the WHERE clause in the first query.
I found this article about "Top n per Group" and that seems like the direction I need to be headed, but I'm not sure I'm on the right track.
Any help, clarifications or suggestions would be greatly appreciated.
SELECT gp.PledgeId FROM GiftPledge gp
INNER JOIN GiftDetail gd ON gp.PledgeId = gd.PledgeId
WHERE gp.PledgeStatus = 'A'
GROUP BY PledgeId
HAVING COUNT(PledgeId) >= 3
AND
GP.PledgeID in (
SELECT PledgeID From
(
SELECT TOP 3 gp.PledgeID, gdi.Amt FROM GiftDetail gdi
INNER JOIN GiftHeader ghi ON gdi.GiftRef = ghi.GiftRef
WHERE gdi.PledgeId = gp.PledgeId
ORDER BY ghi.GDate DESC
) x_amt
Group By PledgeID
Having SUM(AMT) ) x_sum = 0
something like that anyway.