Comma-separated List of Results - sql

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.

Related

Pivoting to append the last 6 payments for 24 loans

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;

How do I list the count of occurrences using the over() function, but only display the occurrences that are listed in the result 3 or more times?

How do I list the amount of times each account number has occurred if I only want to see the ones that have occurred 3 times or more?
I'm using a window OVER() function to get the number of times the account number is listed.
SELECT a.ACCOUNTNUMBER AS [Account Number]
, CONCAT(n.FIRST, ' ', n.MIDDLE, ' ', n.LAST) AS [Member Name]
, l.id AS [Loan ID]
, COUNT(a.ACCOUNTNUMBER)
OVER(partition by a.ACCOUNTNUMBER) as [Number of
Tracking Record]
, n.EMAIL AS [Email]
, n.HOMEPHONE AS [Phone Number]
FROM dbo.account a
INNER JOIN dbo.LOAN l
ON a.ACCOUNTNUMBER = l.PARENTACCOUNT
INNER JOIN dbo.LOANTRACKING lt
ON l.PARENTACCOUNT = lt.PARENTACCOUNT
AND l.ID = lt.ID
INNER JOIN dbo.NAME n
ON a.ACCOUNTNUMBER = n.PARENTACCOUNT
WHERE lt.type = 46
AND l.ProcessDate = CONVERT(VARCHAR(8), dateadd(day,-1, getdate()),
112)
AND lt.ProcessDate = CONVERT(VARCHAR(8), dateadd(day,-1, getdate()),
112)
AND a.CLOSEDATE IS NULL
AND lt.EXPIREDATE IS NULL
GROUP BY a.ACCOUNTNUMBER, n.FIRST, n.MIDDLE, n.LAST, l.id, n.email,
n.HOMEPHONE
ORDER BY [Account Number]
Right now my result is giving me the number of times all of the accounts are listed in the "Number of Tracking Record" column. I want to see only the account numbers that have "3" occurrences or above.
My current result:
My Desired Result:
SELECT * -- edit to include only your relevant columnns
FROM
(
SELECT a.ACCOUNTNUMBER AS [Account Number]
, CONCAT(n.FIRST, ' ', n.MIDDLE, ' ', n.LAST) AS [Member Name]
, l.id AS [Loan ID]
, COUNT(a.ACCOUNTNUMBER)
OVER(partition by a.ACCOUNTNUMBER) as [Number of
Tracking Record]
, n.EMAIL AS [Email]
, n.HOMEPHONE AS [Phone Number]
FROM dbo.account a
INNER JOIN dbo.LOAN l
ON a.ACCOUNTNUMBER = l.PARENTACCOUNT
INNER JOIN dbo.LOANTRACKING lt
ON l.PARENTACCOUNT = lt.PARENTACCOUNT
AND l.ID = lt.ID
INNER JOIN dbo.NAME n
ON a.ACCOUNTNUMBER = n.PARENTACCOUNT
WHERE lt.type = 46
AND l.ProcessDate = CONVERT(VARCHAR(8), dateadd(day,-1, getdate()),
112)
AND lt.ProcessDate = CONVERT(VARCHAR(8), dateadd(day,-1, getdate()),
112)
AND a.CLOSEDATE IS NULL
AND lt.EXPIREDATE IS NULL
GROUP BY a.ACCOUNTNUMBER, n.FIRST, n.MIDDLE, n.LAST, l.id, n.email,
n.HOMEPHONE
) MyQuery
WHERE MyQuery.[Number of Tracking Record] >= 3
ORDER BY [Account Number]
I added an "outer" query around yours, using yours as a subquery, and filtered it for those with just the >=3 condition. And I moved the ORDER BY from your inside query to the outer query, as it's just used for ordering the results and not used in the computation itself.

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)

Adding an additional Inner join with mutiple tables SQL

I'm wondering how to inner join customer name-(CUSTNAM) from table rm001 to this query & have it added to my SSRS Report. Could use help adding this. Thanks
I've tried adding after the "from" saleslineitems as an "and" but it broke the SSRS report.
use n
select distinct a.[SOP Number]
--, [Item Number]
, a.[Customer Number], a.[Created Date from Sales Transaction], a.[Primary Shipto Address Code from Sales Line Item]
, a.[City from Sales Transaction],
,c.city
,case
when b.CITY <> c.city then 'Cities Do Not Match'
when c.city = '' then 'Cities do not Match'
when isnull(c.city,'1') = '1' then 'Cities Do Not Match'
else ''
end as [validate cities]
,b.USERDEF1 as GP_F
, c.f_number as EZ_F
,case
when b.USERDEF1 <> c.f_number then 'Fs do not Match'
when b.USERDEF1 = '' then 'No F in GP'
else ''
end as [validate Fs]
, c.f_expiration
,case
when c.f_expiration <= getdate() then ' F EXPIRED '
when c.f_expiration <= DATEADD(d,15,getDate()) then 'F expiring soon'
--when c.f_expiration >= dateAdd(d,61,getdate()) then 'valid F Expiration'
else ''
end as [valid f date]
--,( select top(1) c.f_number from NBS_BoundBook..contacts where c.f_number = b.userdef1 order by c.uid desc )
--, a.*
from SalesLineItems a
inner join rm00102 b on a.[customer number] = b.CUSTNMBR and a.[Primary Shipto Address Code from Sales Line Item] = b.ADRSCODE
left join NBS_BoundBook..contacts c on Replace(Replace(ltrim(rtrim(b.USERDEF1)),CHAR(10),''),CHAR(13),'') =
( select top(1) Replace(Replace(ltrim(rtrim(c.f_number)),CHAR(10),''),CHAR(13),'') from NBS_BoundBook..contacts
where Replace(Replace(ltrim(rtrim(c.f_number)),CHAR(10),''),CHAR(13),'') = Replace(Replace(ltrim(rtrim(b.USERDEF1)),CHAR(10),''),CHAR(13),'')
and c.city= b.CITY order by c.uid desc )
where [sop type] = 'Order'
and [Created Date from Sales Transaction] >= dateAdd(d,-3, getDate())
and [Item Tracking Option] in ( 'Serial Numbers' )
order by a.[Customer Number]
Something like this should work:
.....
FROM SalesLineItems a
INNER JOIN rm00102 b
ON a.[customer number] = b.CUSTNMBR
AND a.[Primary Shipto Address Code from Sales Line Item] = b.ADRSCODE
INNER JOIN rm001 cust
ON cust.[customer number] = a.[customer number]
LEFT JOIN NBS_BoundBook..contacts c
....

Summing row before in a condition (SQL)

I have a spreadsheet where sales data is interspersed with other data in columns. The column headings for S always end with the sum between I(row before)+P(current row) - I(Current row). I would like the data to sum for each row only. I need to do this with SQL queries.
for example :
if the week is 201520, it will return 'X',
but if it isn't week 201520, then S(Column) = I(week 201547) + P(week 201548) - I(Week 201548)
Currently my queries is like this, but I don't get the logic in query for this case, can anybody help me?
select c.[SHIP TO CODE],Area,[Dealer/Account],
c.[PRODUCT CODE],c.Model,c.Category,c.WEEK,c.I,c.P,c.S
from(select distinct b.[SHIP TO CODE],rp.Model,rp.Category,b.[PRODUCT CODE],b.WEEK,b.QTY,
case when TYPE = 'I' then 'I' end as I,
case when TYPE = 'P' then 'P' end as P,
case when WEEK = '201550' then 'X' end as S from
(
select [SHIP TO CODE],[PRODUCT CODE],WEEK,qty,TYPE from Rekap
a
union
select [Ship-to party],Material,[Week-Ok],[1st G/I Qty],category from
SOT left join
SDate on [Tgl SAP] = [1st G/I Date]
)b
left join [Ref Prod]rp
on b.[PRODUCT CODE] = rp.ProductID
where WEEK is not null
)
c
left join [Ref Dealer]rd
on c.[SHIP TO CODE] = rd.[Ship To Code]
order by [Ship To Code],WEEK ASC
select * into #a from (
select
case when abcd.WEEK = '201520' then 'X' end as S,*
from(select abc.Area,abc.Channel,abc.[Dealer/Account],abc.[PRODUCT CODE],rp.Model,rp.Category,abc.week,abc.I,abc.P
from (select ab.[SHIP TO CODE],rd.Area,rd.Channel,rd.[Dealer/Account],ab.[PRODUCT CODE],
ab.WEEK,case when TYPE = 'I' then isnull(QTY,0) end as I,
case when TYPE = 'P' then isnull(qty,0) end as P
from(
select [SHIP TO CODE],[PRODUCT CODE],WEEK,qty,TYPE from Rekap
a
union all
select [Ship-to party],Material,[Week-Ok],[1st G/I Qty],category from
SOT left join
SDate on [Tgl SAP] = [Billing Date])
ab
left join [Ref Dealer]rd
on ab.[SHIP TO CODE] = rd.[Ship To Code]
)abc
left join
[Ref Prod]rp on rp.ProductID = abc.[PRODUCT CODE]
)abcd
--order by abcd.[Dealer/Account],abcd.WEEK
) a
select Area, Channel, [Dealer/Account], [Product Code], model, category, week,
CASE WHEN I IS NULL THEN 'P' WHEN P IS NULL THEN 'I' END type,
SUM(ISNULL(I,0)+ISNULL(p,0)) amount
into #b
from #a
where week is not null
group by area, channel,[dealer/account], [product code], model, category, week,
CASE WHEN I IS NULL THEN 'P' WHEN P IS NULL THEN 'I' END
order by week
--Grouping all, put into temp table
select Area, Channel, [Dealer/Account], [Product Code], model, category, week,
SUM(ISNULL(I,0)) I, SUM(ISNULL(p,0)) p
into #c
from #a
where week is not null
group by area, channel,[dealer/account], [product code], model, category, week
order by week
select *,(select MAX(week) from #c where a.week>#c.week
--IF NOT NEEDED
--and
--a.area=#c.area and a.channel=#c.channel and a.[dealer/account]=#c.[dealer/account] and a.[product code]=#c.[product code]
--and a.model=#c.model and a.category=#c.category
-- END OF
) lastweek
into #d
from #c a
select a.*, a.P-a.I+b.I s from #d a left join #d b on
a.area=b.area and a.channel=b.channel and a.[dealer/account]=b.[dealer/account] and a.[product code]=b.[product code]
and a.model=b.model and a.category=b.category and
a.lastweek=b.week
order by area, channel,[dealer/account], [product code], model, category, week