Click here to monitor SSC
SQLServerCentral is supported by Red Gate Software Ltd.
 
Log in  ::  Register  ::  Not logged in
 
 
 
        
Home       Members    Calendar    Who's On


Add to briefcase

Stop joined aggregates from multiplying Expand / Collapse
Author
Message
Posted Friday, December 14, 2012 10:17 AM
Forum Newbie

Forum NewbieForum NewbieForum NewbieForum NewbieForum NewbieForum NewbieForum NewbieForum Newbie

Group: General Forum Members
Last Login: Tuesday, March 12, 2013 1:23 PM
Points: 5, Visits: 17
I have three tables. A customer table, an invoice table, and a payment table. I want to pull, in a single query, the credit limit (a single value for the customer, in the customer table), the outstanding balance on invoices (an aggregate from the invoice table) and the total unapplied amounts from payments (an aggregate from the payment table)

SELECT a.CustKey, a.CustID, a.CustName,
ISNULL(a.CreditLimit, 0) 'CreditLimit', a.Hold,
ISNULL(SUM(b.Balance), 0), ISNULL(SUM(c.UnappliedAmt), 0)
FROM tarCustomer a (NOLOCK)
LEFT JOIN tarInvoice b (NOLOCK) ON a.CustKey = b.CustKey
LEFT JOIN tarCustPmt c (NOLOCK) ON a.CustKey = c.CustKey
GROUP BY a.CustKey, a.CustID, a.CustName, ISNULL(a.CreditLimit, 0)

I ran this query, and got incorrect results. There are two matching records in the invoice table, and 11 matching records in the payment table. The value returned for the invoice table was 11 times what it should have been, and the vale returned for the payment table was 2 times what it should have been. So clearly, they're multiplying one another.

Is there any way to get the joins not to result in this multiplication? I feel like there's something obvious that I'm missing.

Thanks,
Lisa
Post #1396744
Posted Friday, December 14, 2012 10:45 AM


SSCarpal Tunnel

SSCarpal TunnelSSCarpal TunnelSSCarpal TunnelSSCarpal TunnelSSCarpal TunnelSSCarpal TunnelSSCarpal TunnelSSCarpal TunnelSSCarpal Tunnel

Group: General Forum Members
Last Login: Yesterday @ 9:39 AM
Points: 4,247, Visits: 9,500
Something like this (untested) should get you started.

with invs
as (
select CustKey
,sum(balance) invbalance
from tarInvoice
group by CustKey
)
,payments
as (
select CustKey
,Sum(UnappliedAmt) UnappliedAmt
from tarCustPmt
group by CustKey
)
select c.CustKey
,c.CustId
,etc etc
,IsNull(invs.invbalance, 0)
,isNull(payments.UnappliedAmt)
from tarCustomer c
left join invs on c.CustKey = invs.CustKey
left join payments on c.CustKey = payments.CustKey

Your problem is that you are doing a many-to-many join. Avoid that by using subqueries which do separate aggregations and then joining on the results.

--Edit: I also recommend that you use meaningful aliases (not a,b,c etc) makes your query easier to read. And do you understand the implications of using the NOLOCK hint?

--Edit 2 - fixed typo


____________________________________________________________________________________________

Help us to help you. For better, quicker and more focused answers to your questions, consider following the advice in this link:

http://www.sqlservercentral.com/articles/Best+Practices/61537/

If you are asking for help and your post does not contain a question, you should expect responses which do not contain any answers. Put a question mark in there somewhere - it's not rocket science.
Post #1396750
Posted Friday, December 14, 2012 11:08 AM
Forum Newbie

Forum NewbieForum NewbieForum NewbieForum NewbieForum NewbieForum NewbieForum NewbieForum Newbie

Group: General Forum Members
Last Login: Tuesday, March 12, 2013 1:23 PM
Points: 5, Visits: 17
Thanks, Phil. I thought NOLOCK prevented table locks, and made the code run faster. Is that not the case?

Also... doing the subqueries with "With X as" instead of doing subqueries... does that prevent SQL from running that query over and over? Is that new?

Thanks again,
Lisa


Phil Parkin (12/14/2012)
Something like this (untested) should get you started.

with invs
as (
select CustKey
,sum(balance) invbalance
from tarInvoice
group by CustKey
)
,payments
as (
select CustKey
,Sum(UnappliedAmt) UnappliedAmt
from tarCustPmt
group by CustKey
)
select c.CustKey
,c.CustId
,etc etc
,IsNull(invs.invbalance, 0)
,isNull(payments.UnappliedAmt)
from tarCustomer c
left join invs on c.CustKey = invs.CustKey
left join payments on c.CustKey = payments.CustKey

Your problem is that you are doing a many-to-many join. Avoid that by using subqueries which do separate aggregations and then joining on the results.

--Edit: I also recommend that you use meaningful aliases (not a,b,c etc) makes your query easier to read. And do you understand the implications of using the NOLOCK hint?

--Edit 2 - fixed typo
Post #1396754
Posted Friday, December 14, 2012 12:54 PM


SSCarpal Tunnel

SSCarpal TunnelSSCarpal TunnelSSCarpal TunnelSSCarpal TunnelSSCarpal TunnelSSCarpal TunnelSSCarpal TunnelSSCarpal TunnelSSCarpal Tunnel

Group: General Forum Members
Last Login: Yesterday @ 9:39 AM
Points: 4,247, Visits: 9,500
Lisa Liel (12/14/2012)
Thanks, Phil. I thought NOLOCK prevented table locks, and made the code run faster. Is that not the case?

Also... doing the subqueries with "With X as" instead of doing subqueries... does that prevent SQL from running that query over and over? Is that new?

Thanks again,
Lisa


NOLOCK will no doubt take away some of your locking pain, but it comes at a price. It also performs what are known as 'dirty' reads - reads of data from uncommitted transactions. If any of those transactions is rolled back just after your query runs, your query will have returned the wrong data and you'll find it difficult to say why. I suggest you do a little more research, just so that you definitely understand the implications.

The "With [subq] as select ..." syntax was new in SQL Server 2005. The technical term for this construction is Common Table Expression (CTE). In the above instance, there will be no performance difference between the solution I quoted and one which uses old-style subqueries. The main reason I use them is that I find the syntax more intuitive. Getting a little more advanced, they can also do some stuff that subqueries cannot (eg, recursion).


____________________________________________________________________________________________

Help us to help you. For better, quicker and more focused answers to your questions, consider following the advice in this link:

http://www.sqlservercentral.com/articles/Best+Practices/61537/

If you are asking for help and your post does not contain a question, you should expect responses which do not contain any answers. Put a question mark in there somewhere - it's not rocket science.
Post #1396791
Posted Friday, December 14, 2012 1:01 PM


SSCrazy Eights

SSCrazy EightsSSCrazy EightsSSCrazy EightsSSCrazy EightsSSCrazy EightsSSCrazy EightsSSCrazy EightsSSCrazy EightsSSCrazy EightsSSCrazy Eights

Group: General Forum Members
Last Login: Yesterday @ 1:17 PM
Points: 8,641, Visits: 8,273
I will second Phil on the NOLOCK. That query hint is by no means a go fast pill. It is a get your data quick as long as you don't care about duplicates, missing rows, rolled back transactions and various other quirks that are nearly impossible to debug.

Here are a few links on the topic that I keep handy.

http://blogs.msdn.com/b/davidlean/archive/2009/04/06/sql-server-nolock-hint-other-poor-ideas.aspx

http://sqlblog.com/blogs/andrew_kelly/archive/2009/04/10/how-dirty-are-your-reads.aspx

http://blogs.msdn.com/b/sqlcat/archive/2007/02/01/previously-committed-rows-might-be-missed-if-nolock-hint-is-used.aspx

http://sqlblogcasts.com/blogs/tonyrogerson/archive/2006/11/10/1280.aspx

Now this isn't to say that the NOLOCK hint does not have its place. It just frequently gets overused/abused in the name of "making it faster". It does indeed make it faster but that speed comes at a cost. Understanding the full ramifications is very important.


_______________________________________________________________

Need help? Help us help you.

Read the article at http://www.sqlservercentral.com/articles/Best+Practices/61537/ for best practices on asking questions.

Need to split a string? Try Jeff Moden's splitter.

Cross Tabs and Pivots, Part 1 – Converting Rows to Columns
Cross Tabs and Pivots, Part 2 - Dynamic Cross Tabs
Post #1396795
Posted Saturday, December 15, 2012 4:30 PM
Forum Newbie

Forum NewbieForum NewbieForum NewbieForum NewbieForum NewbieForum NewbieForum NewbieForum Newbie

Group: General Forum Members
Last Login: Tuesday, March 12, 2013 1:23 PM
Points: 5, Visits: 17
Sean Lange (12/14/2012)
I will second Phil on the NOLOCK. That query hint is by no means a go fast pill. It is a get your data quick as long as you don't care about duplicates, missing rows, rolled back transactions and various other quirks that are nearly impossible to debug.

Here are a few links on the topic that I keep handy.


Thanks, guys. I'll read through the links. The fact is, in my particular context, speed is more important, and things like rolled back transactions are rare enough not to outweigh that. But there may be other circumstances, and I'm glad to know that it isn't as simple as I'd thought.
Post #1396918
Posted Monday, December 17, 2012 7:40 AM


SSCrazy Eights

SSCrazy EightsSSCrazy EightsSSCrazy EightsSSCrazy EightsSSCrazy EightsSSCrazy EightsSSCrazy EightsSSCrazy EightsSSCrazy EightsSSCrazy Eights

Group: General Forum Members
Last Login: Yesterday @ 1:17 PM
Points: 8,641, Visits: 8,273
Lisa Liel (12/15/2012)
Sean Lange (12/14/2012)
I will second Phil on the NOLOCK. That query hint is by no means a go fast pill. It is a get your data quick as long as you don't care about duplicates, missing rows, rolled back transactions and various other quirks that are nearly impossible to debug.

Here are a few links on the topic that I keep handy.


Thanks, guys. I'll read through the links. The fact is, in my particular context, speed is more important, and things like rolled back transactions are rare enough not to outweigh that. But there may be other circumstances, and I'm glad to know that it isn't as simple as I'd thought.


You might consider looking into isolation instead. Something like snapshot isolation sounds like it would be a good fit for what you are describing.

http://msdn.microsoft.com/en-us/library/tcbchxcb%28v=vs.80%29.aspx


_______________________________________________________________

Need help? Help us help you.

Read the article at http://www.sqlservercentral.com/articles/Best+Practices/61537/ for best practices on asking questions.

Need to split a string? Try Jeff Moden's splitter.

Cross Tabs and Pivots, Part 1 – Converting Rows to Columns
Cross Tabs and Pivots, Part 2 - Dynamic Cross Tabs
Post #1397259
« Prev Topic | Next Topic »

Add to briefcase

Permissions Expand / Collapse