Don't you hate it when SQL gives you what you ask for and not what you need? I could use a little help in that area.
What I want is the last contribution date, amount and ref_no (to use elsewhere) in a specific set of campaigns.
I found a helpful script in the Output set builder cookbook, but it is designed around using max ref_no. I need to adjust that to last cont_dt because the converted data is a little out of order. Joy.
What gets me closest to what I want is this:
SELECT a.customer_no, a.ref_no, MAX(a.cont_dt) AS cont_dt
FROM dbo.VS_CONTRIBUTION AS a (NOLOCK)
JOIN VS_FUND b (NOLOCK) on a.fund_no = b.fund_no
WHERE (a.campaign_no in(1, 34, 38, 39, 41, 51, 68, 77, 84, 92, 100, 107, 115, 121, 154, 232, 239, 245, 251, 257, 263, 269, 275, 281, 288, 296, 303, 311, 319, 327, 335, 343, 351, 359, 368, 378, 387, 395, 446)) and (a.recd_amt > 0) AND (b.desig_code IN (1))
GROUP by a.customer_no, a.ref_no
But I believe that is giving me the last transaction by campaign. What do I need to do to get the very last contribution to any of the included campaigns with the ref_no by constituent?
Unknown said: Don't you hate it when SQL gives you what you ask for and not what you need? I could use a little help in that area. What I want is the last contribution date, amount and ref_no (to use elsewhere) in a specific set of campaigns.
Something like this might get you started:
select c.ref_no ,c.customer_no ,c.cont_amt ,c.cont_dt from T_CONTRIBUTION c join ( select co.campaign_no ,max(co.cont_dt) as max_cont_dt from T_CONTRIBUTION co join T_CAMPAIGN ca on ca.campaign_no = co.campaign_no where ca.fyear = 2016 -- for example group by co.campaign_no ) md on c.campaign_no = md.campaign_no and c.cont_dt = md.max_cont_dt order by c.cont_dt desc ,c.ref_no desc
You want one row per campaign that contains data from only the most recent contribution in that campaign, right?
My approach to this would be to have my primary SELECT FROM be for VS_CAMPAIGN, and then join to that using a CROSS APPLY where the subquery does a TOP 1 ORDER BY cont_dt DESC.
If you've never used cross apply/outer apply before, they're basically just like inner/outer joins, except instead of having a table or subquery for the entire join (which you match using an ON clause), you get to join to the entire subquery for every single row in the table you're joining to.
SO it would be something like this:
SELECT a.campaign_no, a.description, topcont.*
FROM VS_CAMPAIGN a
CROSS APPLY ( SELECT TOP 1 *
FROM VS_CONTRIBUTION b
WHERE b.campaign_no = a.campaign_no
ORDER BY b.cont_dt DESC ) AS topcont
Unknown said: If you've never used cross apply/outer apply before, they're basically just like inner/outer joins, except instead of having a table or subquery for the entire join (which you match using an ON clause), you get to join to the entire subquery for every single row in the table you're joining to.
Nice.
Unknown said: We want one line for each customer and the last campaign to which they donated.
The "each customer" changes things. As above, this doesn't de-dupe for multiple contribs on the same date, but could be a start toward whatever you want to do with the data next.
select c.ref_no ,c.customer_no ,c.cont_amt ,c.cont_dt from T_CONTRIBUTION c join ( select co.customer_no ,max(co.cont_dt) as max_cont_dt from T_CONTRIBUTION co join T_CAMPAIGN ca on ca.campaign_no = co.campaign_no where ca.fyear = 2016 -- for example group by co.customer_no ) md on c.customer_no = md.customer_no and c.cont_dt = md.max_cont_dt order by c.cont_dt desc ,c.ref_no desc
Unknown said: This is a quick way of doing this, it might not be the most efficient though.
Also nice; an elegant de-dupe, e.g.
SELECT * FROM ( SELECT co.customer_no ,co.ref_no ,co.cont_amt ,co.cont_dt ,pos = ROW_NUMBER() OVER(PARTITION BY customer_no ORDER BY customer_no, cont_dt DESC) FROM T_CONTRIBUTION co join T_CAMPAIGN ca on co.campaign_no = ca.campaign_no where ca.fyear = 2016 -- for example ) a WHERE a.pos = 1 order by cont_dt desc ,ref_no desc
I've been meaning to learn more about window functions--thanks for a simple example!
Unknown said: I've been meaning to learn more about window functions--thanks for a simple example!
I concur; an enjoyable thread!
Agreed…
Love the detail. I have never used cross apply before. Mwah ha ha….
From: Tessitura Technical Forum [mailto:forums-technical@tessituranetwork.com] On Behalf Of Chris Jensen Sent: Tuesday, April 19, 2016 5:52 PM To: Dot Krebs Subject: Re: [Tessitura Technical Forum] SQL assistance
Nick Reilingh: If you've never used cross apply/outer apply before, they're basically just like inner/outer joins, except instead of having a table or subquery for the entire join (which you match using an ON clause), you get to join to the entire subquery for every single row in the table you're joining to.
Nick Reilingh:
From: Nick Reilingh <bounce-nicholasreilingh4883@tessituranetwork.com> Sent: 4/19/2016 5:28:03 PM
This message was sent automatically to you by www.tessituranetwork.com because you subscribed to the Tessitura Technical Forum. You may reply to this message to post to the Technical forum or visit the site to search, read and post to the forums. In the interest of keeping the forum posts from becoming cluttered, we encourage you to delete previous message text from your reply before sending. Thank you!
Nick,
Your assessment is close.
We want one line for each customer and the last campaign to which they donated.
I’m playing with the cross apply. We’ll see.
Dot
From: Tessitura Technical Forum [mailto:forums-technical@tessituranetwork.com] On Behalf Of Nick Reilingh Sent: Tuesday, April 19, 2016 5:41 PM To: Dot Krebs Subject: Re: [Tessitura Technical Forum] SQL assistance
From: Dot Krebs <bounce-dotkrebs9366@tessituranetwork.com> Sent: 4/19/2016 5:09:05 PM
This is a quick way of doing this, it might not be the most efficient though.
SELECT *
FROM (
SELECT *, pos = ROW_NUMBER() OVER(PARTITION BY customer_no ORDER BY customer_no, cont_dt DESC)
FROM T_CONTRIBUTION
) a
WHERE a.pos = 1
Fernando Margueirat Senior Business Analyst The National Ballet of Canada 470 Queens Quay West Toronto, Ontario M5V 3K4 P: 416 345 9686 x453 F: 416 345 8323
http://national.ballet.ca
From: Tessitura Technical Forum [mailto:forums-technical@tessituranetwork.com] On Behalf Of Dot Krebs Sent: Wednesday, April 20, 2016 10:12 AM To: Fernando Margueirat <FMargueirat@national.ballet.ca> Subject: RE: [Tessitura Technical Forum] SQL assistance
It’s not pretty, but this is what eventually got the job done. Thanks everyone for teaching me something new.
SELECT T_CONTRIBUTION.customer_no, Sum(T_CONTRIBUTION.recd_amt) AS recd_amt, T_CONTRIBUTION.campaign_no, T_CAMPAIGN.description, T_CAMPAIGN.fyear INTO [#don]
FROM T_CONTRIBUTION INNER JOIN T_CAMPAIGN ON T_CONTRIBUTION.campaign_no = T_CAMPAIGN.campaign_no
where T_CAMPAIGN.description like '%individual%'
GROUP BY T_CONTRIBUTION.customer_no, T_CONTRIBUTION.campaign_no, T_CAMPAIGN.description, T_CAMPAIGN.fyear;
SELECT [#don].customer_no, Max([#don].fyear) AS MaxOffyear INTO [#maxfy]
FROM [#don]
GROUP BY [#don].customer_no;
SELECT DISTINCT [#don].customer_no, [#don].recd_amt, [#don].campaign_no, [#don].description
FROM [#maxfy] INNER JOIN [#don] ON ([#maxfy].customer_no = [#don].customer_no) AND ([#maxfy].MaxOffyear = [#don].fyear);
From: Tessitura Technical Forum [mailto:forums-technical@tessituranetwork.com] On Behalf Of Fernando Margueirat Sent: Wednesday, April 20, 2016 10:40 AM To: Dot Krebs Subject: RE: [Tessitura Technical Forum] SQL assistance
Doesn’t this query return more than one record per customer if the customer made contributions to multiple campaigns that match the “like ‘%individual%’” condition? Is that what you were looking for? I understood you were looking for 1 record per customer.
From: Tessitura Technical Forum [mailto:forums-technical@tessituranetwork.com] On Behalf Of Dot Krebs Sent: Wednesday, April 20, 2016 11:54 AM To: Fernando Margueirat <FMargueirat@national.ballet.ca> Subject: RE: [Tessitura Technical Forum] SQL assistance
If you want the information for the last contribution each customer made for any campaigns that contains “individual” in its description, this should work.
SELECT a.customer_no, a.ref_no, a.recd_amt, a.campaign_no, a.description
SELECT aa.customer_no,
aa.ref_no,
recd_amt = SUM(aa.recd_amt) OVER(PARTITION BY aa.customer_no, aa.campaign_no),
aa.campaign_no,
ab.description,
pos = ROW_NUMBER() OVER(PARTITION BY aa.customer_no ORDER BY aa.customer_no, aa.cont_dt DESC)
FROM T_CONTRIBUTION aa
JOIN T_CAMPAIGN ab ON ab.campaign_no = aa.campaign_no
WHERE ab.description like '%individual%'
ORDER BY customer_no
You are welcome.
I was in the same position about a year ago, and I actually was looking for a solution for query very similar to the one Dot asked about, when I discovered them. I became a big fan, because they provide a much simpler way of solving certain types of queries.
From: Tessitura Technical Forum [mailto:forums-technical@tessituranetwork.com] On Behalf Of Nick Reilingh Sent: Wednesday, April 20, 2016 12:40 PM To: Fernando Margueirat <FMargueirat@national.ballet.ca> Subject: RE: [Tessitura Technical Forum] SQL assistance
From: Fernando Margueirat <bounce-fernandomargueirat4910@tessituranetwork.com> Sent: 4/20/2016 2:27:29 PM