11

I have three tables: calls, attachments and notes and I want to display everything that's in the calls table, but also display whether a call has attachments and whether the call has notes. - by determining if there is an attachment or note record with a call_id in it. There could be notes and attachments, or there may not be but I would need to know.

Tables structure:

calls:

call_id  |  title  |  description  

attachments:

attach_id  |  attach_name  |  call_id  

notes:

note_id  |  note_text  |  call_id  

If I write:

SELECT c.call_id
     , title
     , description
     , count(attach_id) 
FROM calls c 
LEFT JOIN attachments a ON c.call_id = a.call_id 
GROUP BY c.call_id
       , title
       , description

to give me a list of all calls and the number of attachments.

How can I also add in a column with the number of notes or a column which indicates that there is notes?

Any ideas?

Thanks.

Misiu
  • 4,738
  • 21
  • 94
  • 198
thegunner
  • 6,883
  • 30
  • 94
  • 143

6 Answers6

21

For the count

SELECT 
     c.call_id, 
     title, 
     description, 
     count(DISTINCT attach_id) AS attachment_count , 
     count(DISTINCT note_id)  AS notes_count 
FROM calls c 
LEFT JOIN attachments a ON c.call_id = a.call_id 
LEFT JOIN notes n ON n.call_id = c.call_id 
GROUP BY c.call_id,title,description

Or for existence (will be more efficient if this is all you need)

SELECT 
     c.call_id, 
     title, 
     description, 
     count(attach_id) AS attachment_count , 
     case
        when exists (select * from notes n WHERE n.call_id = c.call_id) then
            cast(1 as bit)
        else
            cast(0 as bit)
    end as notes_exist
FROM calls c 
LEFT JOIN attachments a ON c.call_id = a.call_id 
GROUP BY c.call_id,title,description
Martin Smith
  • 438,706
  • 87
  • 741
  • 845
  • Wouldn't it be even faster when you'd just select primary key in `exists()` statement instead of all columns `*`? – Robert Koritnik Dec 02 '10 at 11:06
  • 4
    @Robert - No. that is a myth. – Martin Smith Dec 02 '10 at 11:07
  • @Martin: but it is a very good practice especially if you use EF on the client side where it **does make a huge difference**. – Robert Koritnik Dec 02 '10 at 11:10
  • Hey, all good answers guys. This one was posted first and I'll go with your efficient one here Martin - but I'll use the primary key has robert has mentioned. Thanks Guys. – thegunner Dec 02 '10 at 11:14
  • oh and had to change it to "WHERE n.call_id = c.call_id" – thegunner Dec 02 '10 at 11:15
  • @Robert - I've not used EF so have absolutely no idea why it should make any difference. In an existence check it makes absolutely no difference if you use `SELECT 1`, `SELECT NULL`, `SELECT *` or `SELECT PK` though. – Martin Smith Dec 02 '10 at 11:17
  • @Martin: You're right. I've had this issues with OrderBy + Skip + Take paging with all item count in EF. You can take a look here and see what I'm referring to. http://stackoverflow.com/questions/2493900/can-we-control-linq-expression-order-with-skip-take-and-orderby/2509771#2509771 Though it doesn't have much to do with things written here. – Robert Koritnik Dec 02 '10 at 12:09
1
SELECT c.call_id, title, description, a.call_id, n.call_id
FROM calls c 
LEFT JOIN attachments a ON c.call_id = a.call_id 
LEFT JOIN notes n ON c.call_id = n.call_id
GROUP BY c.call_id,title,description, a.call_id, n.call_id

If call id is present in fiels 4 or 5, you know you have an attachement or a note

If you need to number of attachement or note, look at other answers, look at AtaTheDev's post.

VdesmedT
  • 9,037
  • 3
  • 34
  • 50
1

Use distinct in counts

You have to use distinct in counts because your groups have grown by two different entities. So you have to only count distinct values of each. This next query will return both counts as well as bit values whether there are any attachments and notes.

select
    c.call_id, c.title, c.description,
    count(distinct a.attach_id) as attachments_count,
    count(distinct n.note_id) as notes_count,
    /* add these two if you need to */
    case when count(distinct a.attach_id) > 0 then 1 else 0 end as has_attachments,
    case when count(distinct n.note_id) > 0 then 1 else 0 end as has_notes
from calls c
    left join attachments a
    on (a.call_id = c.call_id)
    left join notes n
    on (n.call_id = c.call_id)
group by c.call_id, c.title, c.description
Robert Koritnik
  • 103,639
  • 52
  • 277
  • 404
0

This also works:

SELECT 
    cl.*,
    (SELECT count(1) FROM attachments AS at WHERE at.call_id = cl.id) as num_attachments,
    (SELECT count(1) FROM notes AS nt WHERE nt.call_id = cl.id) as num_notes,
FROM calls AS cl
0

I think it should be something like this

SELECT c.call_id, title, description, count(distinct attach_id) , count(distinct note_id)
FROM calls c
LEFT JOIN attachments a ON c.call_id = a.call_id
LEFT JOIN notes n ON n.call_id = a.call_id
GROUP BY c.call_id,title,description

dabal
  • 410
  • 3
  • 15
0

I have used this simple query. This query allows you to use main tables columns easily without group by.

   Select StudentName,FatherName,MotherName,DOB,t.count  from Student
   left JOIN
   (
    Select StudentAttendance.StudentID,  count(IsPresent) as count 
    from StudentAttendance
    group by StudentID, IsPresent
   ) as t    
  ON   t.StudentID=Student.StudentID
garish
  • 637
  • 12
  • 14