How to select date and previous date - sql-server

I have a table of referral records with a start date and end date. If the person has had previous referrals I need to retrieve the start and end date of the referral previous to the current one.
Any ideas would be greatly appreciated!
Example of what I hope to achieve
Per ID Ref ID Refrl_Start_D Refrl_End_D Prev_Refrl_S Prev_Refrl_S
1 5 10/10/2018 Null 01/02/2018 03/03/2018
2 4 05/04/2018 Null 01/01/2017 01/03/2017
1 3 01/02/2018 03/03/2018 02/01/2017 20/01/2017
1 2 02/01/2018 20/01/2018 Null Null
2 1 01/01/2018 01/03/2018 Null Null
Is have tried adding a Rank calc grouped by person. My plan was to create another table offset it by 1 and join them (i.e. Join referral 5 with 4). However I couldn't figure out how to get the join right.
SELECT DIM_PERSON_ID,
FACT_REFERRAL_ID,
REFRL_START_DTTM,
REFRL_END_DTTM
FROM FACT_REFERRALS;

You can use LAG here
SELECT DIM_PERSON_ID,
FACT_REFERRAL_ID,
REFRL_START_DTTM,
REFRL_END_DTTM
PrevStart = LAG(REFRL_START_DTTM)
OVER(PARTITION BY DIM_PERSON_ID
ORDER BY REFRL_START_DTTM DESC),
PrevEnd = LAG(REFRL_END_DTTM)
OVER(PARTITION BY DIM_PERSON_ID
ORDER BY REFRL_END_DTTMDESC)
FROM FACT_REFERRALS R;

You should be able to construct this with a CTE to calculate a rank/row number for each record based on the referral dates, then join the CTE to itself to get the previous referral. E.g:
WITH RANKED_REFERRALS AS (
SELECT DIM_PERSON_ID,
FACT_REFERRAL_ID,
REFRL_START_DTTM,
REFRL_END_DTTM,
ROW_NUMBER() OVER(PARTITION BY DIM_PERSON_ID ORDER BY REFRL_START_DATE) AS ROWNUM
FROM FACT_REFERRALS
)
SELECT A.DIM_PERSON_ID,
A.ACT_REFERRAL_ID,
A.REFRL_START_DTTM,
A.REFRL_END_DTTM,
B.REFRL_START_DTTM,
B.REFRL_END_DTTM
FROM RANKED_REFERRALS A
LEFT JOIN RANKED_REFERRALS B ON B.DIM_PERSON_ID = A.DIM_PERSON_ID AND B.ROWNUM = A.ROWNUM - 1

Related

SQL - Return first non-empty value for previous days

I'm currently working with an exchange rates table in SQL that has these fields:
| Country | ExchangeRateDt | ExchangeRateValue |
| DK | 202000601 | 0.2 |
| DK | 202000603 | 0.21 |
| HR | 202000601 | 0.10 |
| HR | 202000602 | 0.12 |
For each currency I don't have a value for any day of the year because of bank holidays or simply weekends.
I need to join it with an order table where some orders are placed on weekends and on a specific day I could not have an exchange rate to calculate taxes.
I need to take the first non missing value from the previous days (so in the examples should I have an order for day 2020-06-02 in Denmark I should exchange it using the rate 0.2)
I thought about using a calendar table but I can't manage to get the job done.
Can someone help me?
Thanks in advance,
R
To get the most recent value less than or equal to the current day:
SELECT
<whatever columns you need from order>
,exchange.ExchangeRateValue
FROM
<order table> order
LEFT JOIN
<exchange rate table> exchange
ON exchange.Country = order.Country
AND exchange.ExchangeRateDt =
(
SELECT
MAX(ExchangeRateDt)
FROM
<exchange rate table>
WHERE
Country = order.Country
AND ExchangeRateDt <= order.OrderDt
)
Ensure the clustered index on the exchange rate table is (Country, ExchangeRateDt).
I have this as a left join so you will still return order results if the currency information is somehow missing. You would have to refer to business rules on how to proceed if no exchange rate was available.
You would typically create a calendar table that stores all the days you are interested in, say dates, with each date on a separate row.
You would also probably have a table that lists the countries: I assumed countries.
Then, one option is a lateral join:
select c.country, d.date, t.ExchangeRateValue
from dates d
cross join countries c
outer apply (
select top (1) t.*
from mytable t
where t.country = c.country and t.ExchangeRateDt <= d.date
order by t.ExchangeRateDt desc limit 1
) t
If you don't have these two tables, or can't create them, then one option is a recursive query to generate the dates and a subquery to list the countries. For example, this would generate the data for the month of June:
with dates as (
select '20200601' date
union all
select dateadd(day, 1, date) from dates where date < '20200701'
)
select c.country, d.date, t.ExchangeRateValue
from dates d
cross join (select distinct country from mytable) c
outer apply (
select top (1) t.*
from mytable t
where t.country = c.country and t.ExchangeRateDt <= d.date
order by t.ExchangeRateDt desc limit 1
) t
You should be able to do the mapping between the transation date and the exchange rate date with this query:
select TAB.primary_key, TAB.TransationDate, max(EXR.ExchangeRateDt)
from yourtable TAB
inner join exchangerate EXR
on TAB.Country = EXR.Country and TAB.TransationDate >= EXR.ExchangeRateDt
group by TAB.primary_key, TAB.TransationDate

how to get multiple min values from two SQL tables?

I have two tables, a Members table and a Plan table. They are structured as follows.
member start_date Mplan Pplan version start_dt end_dt
John 20120701 johnplan johnplan 1 20120601 20130531
John 20130201 johnplan johnplan 2 20130601 20140531
John 20130901 johnplan
John 20131201 johnplan
I need to update the start_date on the Members table to be the minimum value present for that member but within the same Plan version.
Example:
20130201 would be changed to 20120701 and 20131201 would change to 20130901.
Code:
UPDATE Members
SET start_date =(
SELECT MIN(start_date) FROM Members a
LEFT JOIN Plan ON Mplan = Pplan AND
start_date BETWEEN start_dt AND end_dt
WHERE member=a.member
AND start_date BETWEEN start_dt AND end_dt
)
Unfortunately this sets every single start_date to 19900101 aka the lowest value in the entire table for that column.
First you need to get the minimum start date of each member for a specific plan. The following will provide you that.
select MIN(start_date) as min_date,a.member as member_name,a.Mplan as plan_name FROM Members a inner JOIN [plan] p ON a.Mplan = p.Pplan AND
start_date BETWEEN p.start_dt AND p.end_dt
group by a.member, a.Mplan
The result will be something like this.
min_date member_name plan_name
2012-07-01 00:00:00.000 John johnplan1
2013-09-01 00:00:00.000 John johnplan2
Use this to update each member's start date for a plan with the lowest start date of the respective plan.
update members
set start_date= tbl.min_date from
(SELECT MIN(start_date) as min_date,a.member as member_name,a.Mplan as plan_name FROM Members a
inner JOIN [plan] p ON a.Mplan = p.Pplan AND
start_date BETWEEN p.start_dt AND p.end_dt
group by a.member, a.Mplan) as tbl
where member=tbl.member_name and Mplan=tbl.plan_name
I created your 2 tables, members and plan, and tested this solution with sample data and it works. I hope it helps.
You really need to convert the dates to Datetime. You will have a greater precision, the possibility to store hours, days and minutes as well as access to date specific functions, international conversion and localization.
If your column is a Varchar(8), then it uses no less space than a Datetime column.
That said, what you are looking for is row_number().
Something like:
SELECT Member, MPlan, Start_Date, Row_Number() OVER (PARTITION BY Member, MPLan ORDER BY Start_Date) as Version
FROM Members
Could you try this ? I didn't test it.
With Member_start_dt as
(
select *, (select start_dt from Pplan where M.start_date <= start_dt AND M.start_date >= end_dt) as Pplan_date
from Members M
),
Member_by_plan as
(
select *, ROW_NUMBER () over (partition by Pplan_date order by start_date) num
from Member_start_dt
)
update M
Set M.start_date = MBP1.start_date
from Members M
inner join Member_by_plan MBP1 ON MBP1.member = M.Member AND num = 1
inner join Member_by_plan MBP2 ON MBP2.member = M.Member AND MBP2.Pplan_date = MBP1.Pplan_date AND MBP2.start_date = M.start_date

get first row for each group

I want to transform this data
account; docdate; docnum
17700; 9/11/2015; 1
17700; 9/12/2015; 2
70070; 9/1/2015; 4
70070; 9/2/2015; 6
70070; 9/3/2015; 9
into this
account; docdate; docnum
17700; 9/12/2015; 2
70070; 9/3/2015; 9
.. for each account I want to have one row with the most current (=max(docdate)) docdate. I already tried different approaches with cross apply and row_number but couldn't achieve the desired results
Use ROW_NUMBER:
SELCT account, docdate, docnum
FROM (
SELECT account, docdate, docnum,
ROW_NUMBER() OVER (PARTITION BY account
ORDER BY docdate DESC) AS rn
FROM mytable ) AS t
WHERE t.rn = 1
PARTITION BY account clause creates slices of rows sharing the same account value. ORDER BY docdate DESC places the record having the maximum docdate value at the top of its related slice. Hence rn = 1 points to the record with the maximum docdate value within each account partition.

SQL Update sequence data based upon date field

I am attempting to update a table that contains deed information. Specifically property ID, sale sequence, and deed date. The program generates the sale sequence data sequentially regardless of the deed date or prior deed information for the property in question.
[property_ID] [sale_number] [sale_deed_date]
1 1 01/15/1990
1 2 06/25/1970
1 3 08/12/1930
What I would like to accomplish is re-sequence sale_number data so they are in chronological order. Similar to this:
[property_ID] [sale_number] [sale_deed_date]
1 1 08/12/1930
1 2 06/25/1970
1 3 01/15/1990
Any help with this would be greatly appreciated.
You can do this by grabbing the correct order in a cte:
;WITH cte AS (SELECT property_ID, sales_number, sales_deed_date, rn = ROW_NUMBER() OVER (PARTITION BY Property_ID ORDER BY sales_deed_date) FROM tablename)
UPDATE t
SET t.sales_number = cte.rn
FROM tablename t
INNER JOIN cte ON t.property_ID = cte.property_ID AND t.sales_deed_date = cte.sales_deed_date

How to query a Master database using Inner Join links to 2 sub-databases that are identical to each other

I have an Inventory table containing Master file info and 2 Movement History tables (Current Year and Last Year).
I want to use a Query to extract Movements from (say) June LAST Year to March THIS Year in Code, Date sequence.
I am relatively new to SQL and have tried to use the following INNER JOIN structure to do this:
SELECT Code, Descrip, Category, MLast.Date, MLast.DocNo, MCurr.Date, MCurr.DocNo
FROM Stock AS S
INNER JOIN MoveTrnArc MLast ON MLast.Stockcode = S.Code
AND MLast.Date >='2011/06/01' AND MLast.Date <='2012/03/31'
INNER JOIN MoveTrn MCurr ON MCurr.Stockcode = S.Code
AND MCurr.Date >='2011/06/01' AND MCurr.Date <='2012/03/31'
ORDER BY S.Code
This creates a Query Table with the following column structure:
Code | Descrip | Category | Date | DocNo | Date | DocNo |
...where the data from the LAST Year table appears in the first Date/DocNo columns and the CURRENT Year data appears in the second Date/DocNo columns.
What must I do to the Query to have each Movement in its own row or is there a better, more efficient Query to achieve this?
Also, I need the Movements listed in Code followed by Date sequence.
use union all instead of joins
select s.Code , s.Descrip , s.Category , t.Date , t.DocNo
from
(
select Stockcode, Date, DocNo from MoveTrnArc
union all
select Stockcode, Date, DocNo from MoveTrn
) t join Stock s on s.Code = t.Stockcode
where t.Date >='2011/06/01' AND t.Date <='2012/03/31'
beside careful with comparing dates, if Date column is type datetime and includes time you have to change t.Date <='2012/03/31' into t.Date <'2012/04/01' to include all the rows from 31st of march,
as '2012/03/31' is casted as '2012/03/31 00:00:00.000'

Resources