tags:

views:

66

answers:

4

I have following two tables:

Person {PersonId, FirstName, LastName,Age .... } 
Photo {PhotoId,PersonId, Size, Path}

Obviously, PersonId in the Photo table is an FK referencing the Person table.

I want to write a query to display all the fields of a Person , along with the number of photos he/she has in the Photo table.

A row of the result will looks like

24|Ryan|Smith|28|6

How to write such query in tsql?

Thanks,

A: 
select Person.*, count(PhotoId) from Person left join Photo on Person.PersonId = Photo.PersonId
Jesse Dhillon
+1  A: 
SELECT P.PersonId, FirstName, LastName,Age, COUNT(PhotoId) AS Num
FROM Person P 
     LEFT OUTER JOIN PHOTO PH ON P.PersonId =  PH.PersonId
GROUP BY P.PersonId, FirstName, LastName,Age
Martin Smith
-1 you are not using Group by properly. Why exactly are you grouping by all of those fields? you only need to group by the PersonID. And what is your reasoning behind a left outer join. You should rewrite that.
JonH
@JonH - LOL! I take it you've only ever used MySQL and not a product requiring standard SQL? And have you any cogent reason why I shouldn't use a LEFT JOIN here?
Martin Smith
This is what I had too, but what if the Person table has a dozen fields? Adding ALL the fields to the GROUP BY clause a good idea? Is there other way to do this? Thanks!
sean717
@Martin Smith - Actually no I do not use mysql. And I or any dba would stand by my comment. You do not need to group by all of those fields. Go post that on sqlteam.com see what they think
JonH
@sean717 - absolutely not a good idea at all, and @Martin Smith should stop LOLing and learn how to properly write SQL statements. Group by is being used incorrectly and that is why several people have -1 the answer. If you find yourself groupping by a ton of columns like @Martin Smith has, you can assume you are doing something incorrectly. Look at my answer for help.
JonH
@JonH - In standard SQL if you have a GROUP BY then the only things allowed in the `Select` list are Grouped By columns, aggregates, or literals. Pretty basic stuff.
Martin Smith
@Martin - A personId is what makes the row unique, so when I group by PersonId I expect at most one row per person, do you agree? If so why does your query group by 3 additional fields that are not needed. My point is your guessing game won't always work. Correct if you have a group by the only things allowed in the select list are the aggregates and the columns. But again think about how long and winded your example became. Look at how the op @sean717 said `this is what i had too, but what if the person table has a dozen fields`. What is your solution to keep adding fields to the group?
JonH
@Martin Smith - thanks for the -1 rep point, shows you really can't take criticism correctly. But then again I'm just a MySQL developer?
JonH
@JonH - No the -1 was because your answer was actually incorrect. Which is a considerably less arbitrary reason than your -1 to me was for. Regarding your point about grouping by PersonId of course the optimiser will take into account that PersonId is the PK but those are just the syntax rules.
Martin Smith
@Martin Smith - Sorry I agree with @JonH. -1 from me to sorry.
@Martin Smith "Regarding your point about grouping by PersonId of course the optimiser will take into account that PersonId is the PK but those are just the syntax rules." This has nothing to do with the optimizer, what it has to do is properly using Group By. Joe Celko would smack you across the face if you had those types of queries in production code.
JonH
@Martin Smith - I see you edited your comment. The point of stackoverflow is to move the answers that are incorrect down the thread. That is why I negated you. Don't be so defensive about your rep points. The site is for asking questions and getting the right answers. Answers that are right sit at the top, and those that aren't go to the bottom. That way if someone searching for something similiar instantly gets the correct answer. I'm sorry if it offended you and I won't bother you or this post again.
JonH
@JonH - Not offended at all. I find it quite funny how confident you are in displaying your ignorance. I'm still waiting to hear why I should rewrite the `left outer join` as well when your answer clearly has a `left join` in it? Surely you don't think they are 2 different things?
Martin Smith
INTERNET FIGHT!!!!!!!!
Abe Miessler
+1 as this answer will give 0 for photocount when a person has no photos. The other answers, as given, will give null for photocount when a person has no photos
Shannon Severance
@Martin Smith - Go here and do some reading http://weblogs.sqlteam.com/jeffs/archive/2005/12/14/8546.aspxSpecifically for you `I can’t say how many times I see SELECT's written this way and it is simply wrong. You should only be grouping on CustomerID, and not on all those other columns. Push the grouping down a level, into a derived table`
JonH
@JonH - It is not "wrong". SQL is a declarative language. It is simply a matter of taste. It would only be "wrong" if it produced the wrong results or poorer execution plans.
Martin Smith
@Martin Smith - What happens when the OP wants 100 fields is that what your group by is going to do? Dividing by 0 isn't wrong either, its simply undefined. The behavior is what matters when dealing with problems.
JonH
@Abe Miessler - Definately not interested in an internet fight. Just trying to help @Martin Smith understand SQL's group by.
JonH
@Shannon Severance - The op did not ask to handle that but that is really simple. If you look at BOL there is COALESCE, or you can simply case it out and check if it is NULL. If it is set it to 0. The main point is the actual query, the rest is just extras. I've edited my answer to handle that Shannon.
JonH
@JonH - In the 100 field case I might use a CTE or your approach but that wasn't what the questioner asked. I'm still interested in your issue with the `LEFT OUTER JOIN`?
Martin Smith
@Martin Smith - that was a fudge on my part. However, the OUTER is optional. CTE would be a better candidate then what you have.
JonH
+2  A: 
    SELECT 
       p.PersonId, 
       p.FirstName, 
       p.LastName, 
       p.Age,
       CASE WHEN 
            t.ThePhotoCount IS NULL THEN 0 ELSE t.ThePhotoCount END AS TheCount
       --the above line could also use COALESCE
   FROM 
       Person p 
   LEFT JOIN
         (SELECT 
             PersonId,
             COUNT(*)  As ThePhotoCount
         FROM 
             Photo 
         GROUP BY PersonId) t
    ON t.PersonId = p.PersonID
JonH
-1 There is no PersonID field in the derived table t but you are joining on it. syntax error.
Martin Smith
@Martin Smith - Please read the question correctly, there is a PersonID in the table Photo, simply include it. Definately not worth negating a correct answer.
JonH
@JonH - At the time of the down vote the answer was not correct. Now it is I have rescinded it.
Martin Smith
@Martin Smith - That shows a lot of guts from you. Thank you @Martin Smith.
JonH
+2  A: 

You need a subquery in order to avoid having to repeat all the columns from Person in your group by clause.

SELECT
  p.PersonId,
  p.FirstName,
  p.LastName,
  p.Age,
  coalesce(ph.PhotoCount, 0) as Photocount
FROM
  Person p
  LEFT OUTER JOIN 
     (SELECT PersonId, 
      COUNT(PhotoId) as PhotoCount 
      FROM Photo 
      GROUP BY PersonId) ph
  ON p.PersonId = ph.PersonId 
Jose Basilio
@Jose, that is what my solution I posted nearly 11 minutes ago does. Explain that to @Martin Smith.
JonH
@JonH - your query wouldn't have worked as it was written at the time that @Martin commented and as It stands now, it doesn't include the Count in the results.
Jose Basilio
@Jose, Sorry added.
JonH