Pivoting to append the last 6 payments for 24 loans - sql

I am building a query in Microsoft sql server where I want to find the active loans as well as some information on them. I have built out a common table expression that gives me the correct population. Now I need to get the last 6 payments starting from today. I have a another common table expression that gives all the payments and the payment data received but I am not sure how to pivot and append the last 6 payments such that I have something like this:
This is what the query and output looks like for the common table expression where I can get c1,...,c6.
SELECT Account,Total,CONVERT(datetime,DateRec) [Date Received]
FROM mars.dbo.vw_PaymentHistory PH
WHERE PH.SourceTyp not like '%fundin%' and PH.SourceTyp not like '%draw%'
which gives this (but much more):
Here is the whole query I am working from:
Declare #monthEnding date = '3/31/2020',
#monthStart date = '3/1/2020';
WITH Active_Loans as (
SELECT
la.Account,
la.LoanStatus,
la.PrinBal,
isnull(b.Amount, 0) [DUPB],
la.PrinBal + isnull(b.Amount, 0) [TotalUPB],
l.NoteOwner,
pt.[Partition],
l.paidoffdate,
la.[First Name],
la.[Last Name],
la.PmtPI,
la.PmtImpound,
la.NextDueDate,
la.MaturityDate,
la.NoteOwner as [Note Owner]
FROM MARS_DW..vw_vw_Loans_ArchiveEOM la
LEFT JOIN MARS_DW..vw_DUPBByLoan b on b.Account = la.Account
AND b.ArchiveDate = la.ArchiveDate
LEFT JOIN MARS..vw_Loans l on l.Account = la.Account
LEFT JOIN Portfolio_Analytics..partition_table pt on pt.Noteowner = l.NoteOwner
WHERE la.MonthEnding = #monthEnding
AND la.isActive = 1
AND la.PaidOffDate is null
AND la.LoanStatus NOT LIKE 'BK Payment Plan'
AND la.LoanStatus NOT LIKE 'Prelim'
AND la.LoanStatus NOT like 'trailing claims'
AND la.Account NOT IN (
SELECT account
FROM MARS..vw_Loans
WHERE servicexferdate <=
DATEADD(dd, - 1, DATEADD(mm, DATEDIFF(mm, 0, #monthStart) + 1, 0))
AND PaidOffDate BETWEEN #monthStart AND DATEADD(dd, - 1, DATEADD(mm, DATEDIFF(mm, 0, #monthStart) + 1, 0))
)
UNION
(
SELECT l.account
,la.LoanStatus
,la.PrinBal
,isnull(b.Amount, 0) [DUPB]
,la.PrinBal + isnull(b.Amount, 0) [TotalUPB]
,l.NoteOwner
,pt.[Partition]
,l.PaidOffDate
,la.[First Name]
,la.[Last Name]
,la.PmtPI
,la.PmtImpound
,la.NextDueDate
,la.MaturityDate
,la.NoteOwner as [Note Owner]
FROM MARS..vw_Loans l
LEFT JOIN MARS_DW..vw_vw_Loans_ArchiveEOM la on la.Account = l.Account
LEFT JOIN MARS_DW..vw_DUPBByLoan b on b.Account = la.Account
LEFT JOIN Portfolio_Analytics..partition_table pt on pt.Noteowner = l.NoteOwner
AND b.ArchiveDate = la.ArchiveDate
WHERE l.servicexferdate < #monthEnding
AND l.PaidOffDate > #monthEnding
AND la.MonthEnding = #monthEnding
AND la.LoanStatus NOT like 'trailing claims'
AND la.LoanStatus NOT like 'Inactive - REO/FC'
AND pt.[Partition] IS NOT NULL
)
)
,
payments as
(
SELECT Account,Total,CONVERT(datetime,DateRec) [Date Received]
FROM mars.dbo.vw_PaymentHistory PH
WHERE PH.SourceTyp not like '%fundin%' and PH.SourceTyp not like '%draw%'
)
SELECT
rptpop.Account
, rptpop.[First Name]
, rptpop.[Last Name]
, '$' + CONVERT (VARCHAR (12), rptpop.PmtPI+rptpop.PmtImpound, 1) as PITI
,'$' + CONVERT (VARCHAR (12), rptpop.TotalUPB, 1) as [Total UPB]
, CONVERT(VARCHAR(10),rptpop.NextDueDate,101) as [Next Due Date]
, CONVERT(VARCHAR(10),rptpop.MaturityDate,101) as [Maturity Date]
, rptpop.[Note Owner]
FROM Active_Loans as rptpop
LEFT JOIN payments as pmt on pmt.Account = rptpop.Account
WHERE
rptpop.Partition = 'GAEA'
AND rptpop.LoanStatus = 'Current'
AND rptpop.[Last Name] NOT LIKE '%CRE%'
AND pmt.[Date Received] BETWEEN #monthStart AND #monthEnding
EDIT:
Based on the answer below I was able to do this:
payments as
(
SELECT ROW_NUMBER() OVER(Partition By Account ORDER BY CONVERT(datetime,DateRec) DESC) AS [RowNumber], Total, Account
FROM mars.dbo.vw_PaymentHistory
)
,
get_payment1 as
(
SELECT * FROM payments
where RowNumber = 1
)
Which gives me numbers but what I do not understand is whether 1.) This is indeed correct and 2.) Assuming it is correct how do is it getting the most recent date? Perhaps its the order by clause?

I see a couple of way to solve the problem. I can share the approach using pseudo code.
Create Payments CTE , something like SELECT ROW_NUMBER() OVER(Partition By Account ORDER BY ReceivedDate DESC) . Then create 6 CTE's that use the above CTE with Row_Number 1 thru 6. Then simply use those using Left Join in your query, joining on Account#. This will add c1-c6 columns.
A second approach can be to use the same Payments CTE but instead of multiple CTEs, find a way to use UNPIVOT unpivot (Amount for Rows in (1,2,3,4,5,6) ) u;

Related

Avoid SQL Pivot returning duplicate rows

I have the following SQL script which returns duplciate values in PIVOT. How do I combine those duplicate records to one row.
Please check the below image for the results set.
SELECT *
FROM (SELECT X.stockcode,
X.description,
X.pack,
X.location,
X.lname,
X.qty,
Y.stockcode AS StockCode2,
y.periodname,
Y.months,
Y.saleqty
FROM (SELECT dbo.stock_items.stockcode,
dbo.stock_items.description,
dbo.stock_items.pack,
dbo.stock_loc_info.location,
dbo.stock_locations.lname,
dbo.stock_loc_info.qty
FROM dbo.stock_locations
INNER JOIN dbo.stock_loc_info
ON dbo.stock_locations.locno = dbo.stock_loc_info.location
LEFT OUTER JOIN dbo.stock_items
ON dbo.stock_loc_info.stockcode = dbo.stock_items.stockcode
WHERE ( dbo.stock_items.status = 's' )) AS X
LEFT OUTER JOIN (SELECT dbo.dr_invlines.stockcode,
( 12 + Datepart(month, Getdate()) - Datepart(month, dbo.dr_trans.transdate) ) % 12 + 1 AS Months,
Sum(dbo.dr_invlines.quantity) AS SaleQty,
dbo.period_status.periodname
FROM dbo.dr_trans
INNER JOIN dbo.period_status
ON dbo.dr_trans.period_seqno = dbo.period_status.seqno
LEFT OUTER JOIN dbo.stock_items AS STOCK_ITEMS_1
RIGHT OUTER JOIN dbo.dr_invlines
ON STOCK_ITEMS_1.stockcode = dbo.dr_invlines.stockcode
ON dbo.dr_trans.seqno = dbo.dr_invlines.hdr_seqno
WHERE ( STOCK_ITEMS_1.status = 'S' )
AND ( dbo.dr_trans.transtype IN ( 1, 2 ) )
AND ( dbo.dr_trans.transdate >= Dateadd(m, -6, Getdate()) )
GROUP BY dbo.dr_invlines.stockcode,
Datepart(month, dbo.dr_trans.transdate),
dbo.period_status.periodname) AS Y
ON X.stockcode = Y.stockcode) z
PIVOT (Sum(saleqty) FOR [months] IN ([1],[2],[3],[4],[5],[6])) AS pivoted
EDIT: I missed the root-cause of your issue being the inclusion of the periodname column causing the percieved duplication. I am leaving this in place as general solution showing CTE usage, because it could still be useful if you then want to do extra filtering/transformation of your pivot results
One way is to take the results of the pivot query and run it through a SELECT DISTINCT query.
An example of wrapping your pivot query as a CTE and using it to feed a SELECT DISTINCT below (please note: untested, but parses as valid in my SSMS)
WITH PivotResults_CTE (
stockcode,
description,
pack,
location,
lname,
qty,
StockCode2,
periodname,
months,
saleqty
)
AS (
SELECT *
FROM (
SELECT X.stockcode
,X.description
,X.pack
,X.location
,X.lname
,X.qty
,Y.stockcode AS StockCode2
,y.periodname
,Y.months
,Y.saleqty
FROM (
SELECT dbo.stock_items.stockcode
,dbo.stock_items.description
,dbo.stock_items.pack
,dbo.stock_loc_info.location
,dbo.stock_locations.lname
,dbo.stock_loc_info.qty
FROM dbo.stock_locations
INNER JOIN dbo.stock_loc_info ON dbo.stock_locations.locno = dbo.stock_loc_info.location
LEFT OUTER JOIN dbo.stock_items ON dbo.stock_loc_info.stockcode = dbo.stock_items.stockcode
WHERE (dbo.stock_items.STATUS = 's')
) AS X
LEFT OUTER JOIN (
SELECT dbo.dr_invlines.stockcode
,(12 + Datepart(month, Getdate()) - Datepart(month, dbo.dr_trans.transdate)) % 12 + 1 AS Months
,Sum(dbo.dr_invlines.quantity) AS SaleQty
,dbo.period_status.periodname
FROM dbo.dr_trans
INNER JOIN dbo.period_status ON dbo.dr_trans.period_seqno = dbo.period_status.seqno
LEFT OUTER JOIN dbo.stock_items AS STOCK_ITEMS_1
RIGHT OUTER JOIN dbo.dr_invlines ON STOCK_ITEMS_1.stockcode = dbo.dr_invlines.stockcode ON dbo.dr_trans.seqno = dbo.dr_invlines.hdr_seqno WHERE (STOCK_ITEMS_1.STATUS = 'S')
AND (
dbo.dr_trans.transtype IN (
1
,2
)
)
AND (dbo.dr_trans.transdate >= Dateadd(m, - 6, Getdate()))
GROUP BY dbo.dr_invlines.stockcode
,Datepart(month, dbo.dr_trans.transdate)
,dbo.period_status.periodname
) AS Y ON X.stockcode = Y.stockcode
) z
PIVOT(Sum(saleqty) FOR [months] IN (
[1]
,[2]
,[3]
,[4]
,[5]
,[6]
)) AS pivoted
)
SELECT DISTINCT *
FROM
PivotResults_CTE
;
Also note, your sql included in the above may look slightly different to your original but that is only because i ran it through a reformatter to ensure i understood the structure of it.
In other words, the basic CTE wrapper for your pivot query is:
WITH PivotResults_CTE (
Field1,
Field2,
...
)
AS (
YOUR_PIVOT_QUERY_HERE
)
SELECT DISTINCT *
FROM
PivotResults_CTE
;

SQL Access Sentence

I been trying to use this sentences that i made in SQL Server Management Studio, on access with no luck, after hours googling, i think they use different languages, can someone help me turn these in Access language?
select c.id_client 'Client ID',
c.client_name 'First Name'
c.client_lname 'Last Name'
from client c join bills b
on c.id_client = b.id_client join bill_detail d
on d.bill_num=b.bill_num
where month (b.date)=3
and year(b.date)= year(getdate())
group by c.id_client, c.client_name,c.client_lname
having d.price > (select avg(prirce) from bill_detail)
select s.id_seller 'Seller ID',
s.seller_name 'First Name',
s.seller_lname 'Last Name',
avg(quantity*d.price),
from sellers s join bills b
on v.id_seller=b.id_seller join bill_detail d
on b.bill_num = d.bill_num
group by s.id_seller, s.seller_name, s.seller_lname
having avg (quantity*d.price) < (select avg(quantity*d.price) from
bill_detail)
select date 'Date',
sum(quantity) 'Tickets Sold',
sum(quantity*d.price) 'Total Amount Sold',
avg(quantity*d.price) 'Average Amount Sold',
from bills b join bill_detail d
on b.bill_num = d.bill_num
group by date
Sry, they were in spanish, hence the awfull english. And thanks in advance.
Useful references:
Converting Access Queries to SQL Server
T-SQL Equivalents for Microsoft Access VBA Functions
I think these will work.. (it's been a while)
SELECT
client.id_client [client id]
, client.client_name [first name]
, client.client_lname [last name]
FROM (client
JOIN bills ON client.id_client = bills.id_client)
JOIN bill_detail ON bill_detail.bill_num = bills.bill_num
WHERE MONTH(bills.date) = 3
AND YEAR(bills.date) = YEAR(DATE())
GROUP BY
client.id_client
, client.client_name
, client.client_lname
HAVING d.price > (
SELECT
AVG(prirce)
FROM bill_detail
)
;
For some reason Access likes parentheses in the from clause. No idea why. There should be MONTH() & YEAR() . Don't recall if Access like table aliases, so I removed them and don't use single quotes for column labels (don't do this is SQL Server either).
SELECT
sellers.id_seller [seller id]
, sellers.seller_name [first name]
, sellers.seller_lname [last name]
, AVG(quantity * bill_detail.price)
FROM (sellers
JOIN bills ON v.id_seller = bills.id_seller)
JOIN bill_detail ON bills.bill_num = bill_detail.bill_num
GROUP BY
sellers.id_seller
, sellers.seller_name
, sellers.seller_lname
HAVING AVG(quantity * bill_detail.price) < (
SELECT
AVG(quantity * bill_detail.price)
FROM bill_detail
)
;
Aside from table & column aliases changes these 2 should be ok.
SELECT
datex [date]
, SUM(quantity) [tickets sold]
, SUM(quantity * bill_detail.price) [total amount sold]
, AVG(quantity * bill_detail.price) [average amount sold]
FROM (bills
JOIN bill_detail ON bills.bill_num = bill_detail.bill_num)
GROUP BY
datex
;
having d.price > (select avg(prirce) from bill_detail)
I think you should change it to avg(price) instead of avg(prirce).
(Must be spelling mistake only)

Comma-separated List of Results

I have a fairly basic query that essentially returns a SUM total for invoice line net, for each customer, where there was a certain discount given.
As part of this, I want to return the invoice numbers that each discount applied to, as a comma separated list.
This is essential as it's being fed into another piece of software that only accepts the data in this format.
I could format in Excel as this is where the data will end up, however I'd rather do it in the query directly.
I'm getting my head muddled trying to use the FOR XML PATH function to do this.
Below is the current query, which returns one row per Discount Group, Customer, Discount % given with the sum totals
Field for invoice number is invoice_header.ih_number
SELECT variant_discount_group.vdg_node_path AS 'Discount Group' ,
customer_detail.cd_statement_name AS 'Customer Name' ,
invoice_line_item.ili_discount_percent AS 'Discount' ,
SUM(( CASE WHEN invoice_header.ih_credit = 1 THEN -1
ELSE 1
END ) * invoice_line_item.ili_qty) AS 'Qty' ,
SUM(( CASE WHEN invoice_header.ih_credit = 1 THEN -1
ELSE 1
END ) * invoice_line_item.ili_net) AS 'Total Net'
FROM invoice_header
JOIN invoice_line_item ON invoice_line_item.ili_ih_id = invoice_header.ih_id
JOIN variant_detail ON variant_detail.vad_id = invoice_line_item.ili_vad_id
JOIN variant_setting ON variant_setting.vas_vad_id = variant_detail.vad_id
JOIN customer_detail ON customer_detail.cd_id = invoice_header.ih_cd_id
LEFT JOIN variant_discount_group ON variant_discount_group.vdg_id = variant_setting.vas_vdg_id
WHERE invoice_header.ih_datetime BETWEEN #DateFrom
AND #DateTo
AND ISNULL(variant_discount_group.vdg_node_path, '') LIKE #VDGroup
+ '%'
AND invoice_line_item.ili_discount_percent BETWEEN #DiscFrom
AND #DiscTo
GROUP BY variant_discount_group.vdg_node_path ,
customer_detail.cd_statement_name ,
invoice_line_item.ili_discount_percent
ORDER BY variant_discount_group.vdg_node_path ,
customer_detail.cd_statement_name ,
invoice_line_item.ili_discount_percent
try this
WITH cte
AS ( SELECT variant_discount_group.vdg_node_path AS [Discount Group] ,
customer_detail.cd_statement_name AS [Customer Name] ,
invoice_line_item.ili_discount_percent AS [Discount] ,
( CASE WHEN invoice_header.ih_credit = 1 THEN -1
ELSE 1
END ) * invoice_line_item.ili_qty AS [Qty] ,
( CASE WHEN invoice_header.ih_credit = 1 THEN -1
ELSE 1
END ) * invoice_line_item.ili_net AS [Total Net] ,
invoice_header.ih_number AS [invoice]
FROM invoice_header
JOIN invoice_line_item ON invoice_line_item.ili_ih_id = invoice_header.ih_id
JOIN variant_detail ON variant_detail.vad_id = invoice_line_item.ili_vad_id
JOIN variant_setting ON variant_setting.vas_vad_id = variant_detail.vad_id
JOIN customer_detail ON customer_detail.cd_id = invoice_header.ih_cd_id
LEFT JOIN variant_discount_group ON variant_discount_group.vdg_id = variant_setting.vas_vdg_id
WHERE invoice_header.ih_datetime BETWEEN #DateFrom
AND #DateTo
AND ISNULL(variant_discount_group.vdg_node_path, '') LIKE #VDGroup
+ '%'
AND invoice_line_item.ili_discount_percent BETWEEN #DiscFrom
AND
#DiscTo
)
SELECT a.[Discount Group] ,
a.[Customer Name] ,
a.[Discount] ,
SUM(a.[Qty]) AS Qty ,
SUM(a.[Total Net]) AS [Total Net] ,
SUBSTRING(( SELECT ', ' + CONVERT(VARCHAR, b.[invoice])
FROM (SELECT DISTINCT
[invoice],
[Discount Group],
[Customer Name],
[Discount]
FROM cte) AS b
WHERE a.[Discount Group] = b.[Discount Group]
AND a.[Customer Name] = b.[Customer Name]
AND a.[Discount] = b.[Discount]
FOR
XML PATH('')
), 2, 1000) AS [List of invoices]
FROM cte AS a
GROUP BY a.[Discount Group] ,
a.[Customer Name] ,
a.[Discount]
ORDER BY a.[Discount Group] ,
a.[Customer Name] ,
a.[Discount]
I don't know about performance issues but you could create a function returning a VARCHAR. Call that function in your select statement and as parameter give your reference for your invoicenumbers.
in the Function you would have a cursor for all rows of Invoicenumbers and iterate over it and concatenate into the return value.
This is probably quick and dirty and ressource intensive.

grouping monthly with joins

I a having issues grouping the query below into monthly aggregate.
Table is cross joined with a table to pick up the rate and inner joined with another that contains just dates to show nulls for dates where data doesnt exist in the table (Client Request)
It works fine with the daily grouping which is below. Please how can I group it monthly.
Select * from(select [Letter_Date] [Date],Council
SUM([Total_Corr])*[Rate][Total]
FROM Correspondence
cross join
Input_Variable_Price
where [Revenue_Name] = 'Correspondence'
group by [Letter_Date],Council)AS ED
RIGHT JOIN
(Select '21'[No],b_date,[Revenue_Name][Report],[Unit],[Rate]
From Blank_dates
cross join
Input_Variable_Price
where [Revenue_Name] = 'Correspondence') AS BD
ON ED.Date = BD.[b_date]
Cheers
I would use the following: add in any other aggregations you need to the SELECT, and whatever items in the GROUP BY that you require.
Select DATEADD(month, DATEDIFF(month, 0, [Date]), 0) AS StartOfMonth, SUM(Total)
from
(
select [Letter_Date] [Date],Council,
SUM([Total_Corr])*[Rate] [Total]
FROM
Correspondence
cross join
Input_Variable_Price
where [Revenue_Name] = 'Correspondence'
group by [Letter_Date],Council
)AS ED
RIGHT JOIN
(
Select
'21'[No],
b_date,
[Revenue_Name][Report],
[Unit],
[Rate]
From
Blank_dates
cross join
Input_Variable_Price
where [Revenue_Name] = 'Correspondence'
) AS BD ON
ED.Date = BD.[b_date]
GROUP BY DATEADD(month, DATEDIFF(month, 0, [Date]), 0)

Calculate difference between two columns sql

in my SQL Query i am Calculating column values by using CASE WHEN THEN ELSE END Condition.
Now i want to find difference between computed column and normal column
My Query
SELECT T.Ticket [Ticket],
TT.TicketType [Ticket Type],
T.Dependency [Dependency],
CASE
WHEN (
SELECT TOP 1 f1.UpdatedOn
FROM TicketTypeFollowUp AS f1
WHERE f1.UpdatedOn < T.UpdatedOn
AND f1.Ticket = 61423
ORDER BY f1.UpdatedOn DESC
) IS NULL
THEN Ticket.TicketRaisedOn
ELSE (
SELECT TOP 1 f1.UpdatedOn
FROM TicketTypeFollowUp AS f1
WHERE f1.UpdatedOn < T.UpdatedOn
AND f1.Ticket = 61423
ORDER BY f1.UpdatedOn DESC
)
END [Start Date],
T.UpdatedOn [End Date],
L.EmpName [Updated By]
FROM dbo.TicketTypeFollowUp T
LEFT JOIN dbo.Ticket
ON T.Ticket = Ticket.Code
LEFT JOIN dbo.TicketType TT
ON T.TicketType = TT.Code
LEFT JOIN LoginUser L
ON T.UpdatedBy = L.Code
WHERE Ticket = 61423
Query Result
Ticket Type Dependency Start Date End Date Updated By
61423 FLM AGS 2013-01-22 15:50:08.757 2013-01-22 18:35:50.893 Kedar S
I want one more column which displays End Time - Start Time I have used CASE hence i am unable to compute difference.
Database is SQL SERVER 2008
with lastUpdate(ticket, lastUpdate) as (
select ticket,
max(updateOn)
from TicketTypeFollowUp
group by ticket)
SELECT T.Ticket [Ticket],
TT.TicketType [Ticket Type],
T.Dependency [Dependency],
coalesce(lastUpdate, ticketRaisedOn) [Start Date],
T.UpdatedOn [End Date],
datediff(mi, coalesce(lastUpdate, ticketRaisedOn), T.UpdatedOn) durationMins,
L.EmpName [Updated By]
FROM dbo.TicketTypeFollowUp T
LEFT JOIN dbo.Ticket
ON T.Ticket = Ticket.Code
LEFT JOIN dbo.TicketType TT
ON T.TicketType = TT.Code
LEFT JOIN LoginUser L
ON T.UpdatedBy = L.Code
left join lastUpdate lu on t.ticket = lu.ticket
WHERE Ticket = 61423
You can use your current query as a CTE or a derived table. Here is an example using a CTE:
;WITH CTE AS
(
-- your current query here
)
SELECT *, DATEDIFF(MINUTE,[Start Date],[End Date]) [End Time - Start Time]
FROM CTE