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.
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
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.
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
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
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
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
Related
There is this SQL(from Django) query:
SELECT "id", "name"
FROM "polls_client" INNER JOIN "polls_purchases"
ON ("id" = "client_id")
WHERE "polls_purchases"."product" IN (car, bike)
We need to select from query users who have purchase records only 'car'. I want to do this in one select to the database. How do I do this?
You can group by client and set the condition in the HAVING clause:
SELECT pc.id, pc.name
FROM polls_client pc INNER JOIN polls_purchases pp
ON pc.id = pp.client_id
GROUP BY pc.id, pc.name
HAVING SUM(CASE WHEN pp.product <> 'car' THEN 1 ELSE 0 END) = 0
We need to select from query users who have purchase records only 'car'.
The simplest, most efficient method should be not exists:
SELECT c.*
FROM "polls_client" c
WHERE NOT EXISTS (SELECT 1
FROM "polls_purchases" pp
WHERE c."id" = pp."client_id" AND pp."product" <> 'car'
);
In particular, this can take advantage of an index on polls_purchases(client_id, product).
I would also dissuade your from using double quotes for identifies. They only serve to clutter queries.
I need to find the amount of customers in specific segment
and the amount of customers that have valid mail or cellphone and specific brand from specific segment.
Tables are:
"source" that holds customers+brands
"customers" that holds customers, email, cellphone
"segment" that holds email (that ref to customer)
I have some constrains: I build matrix query so I need to use the case when structure.
This is what I wrote - not good
SELECT
count(*) as CountCustomers,
sum(case when Customerid in
(select distinct customerid from source
where brand like '%abc%')
and (Cellphone not in ('0' ,'-1') or email not like '%#NoEmail.com')
then 1 else 0 end
) as EmailOrSms
from customer
where email in(select distinct email from segment where p=1)
My problem is in the case when I don't know how to write it correctly.
This is the error:
"Cannot perform an aggregate function on an expression containing an aggregate or a subquery"
Hope you understand my question and could help me
thank you very much for your time and effort.
Maybe you can move the subquery to a left join, so there are no issues with aggregate clauses
select
count (*) as CountCustomers,
sum(case when source.customerid is null then 0 else 1 end) as EmailOrSms
from customer
left join source on source.customerid = customer.Customerid and brand like '%abc'
and (Cellphone not in ('0' ,'-1') or email not like '%#NoEmail.com')
where email in(select distinct email from segment where p=1)
this is pseudo code, check the proper syntax
Move that subquery to outer apply/left join
SELECT
count(*) as CountCustomers,
sum(case when s.customerid is not null
and (Cellphone not in ('0' ,'-1') or email not like '%#NoEmail.com')
then 1 else 0 end
) as EmailOrSms
from customer c
left join (
select distinct customerid
from source
where brand like '%abc%'
) s on s.customerid = c.customerid
inner join (
select distinct email
from segment where p=1
) g on g.email = c.email
Both distincts look ugly but perhaps scans are more suitable here than loops. And I don't like this join by email very much.
Help - I have puzzeled on this for some time and am going bats.
In this cutdown query I want to select a single Email address from a contacts list that may contain multiple contacts for that customer.
My problem is that it does not find the Contacts email if I specify TOP 1
(showing NULL for CC.Email)
Note: it returns two records with Contact emails if I specify TOP 5
I think the TOP 1 must be restricting the records to 1 before determining a match?
SELECT C.Code as Customer, C.Name as CustomerName, C. Email, CC.Email, IIF(CC.Email<>'',CC.Email,C.Email) as Email
FROM [dr].[Customer] C
LEFT OUTER JOIN (Select TOP 1 CustomerId, Email
from dr.Contact CC
INNER JOIN dr.ContactDocumentOption CDO
on CDO.TransactionTypeId = 102
AND CDO.ContactId = CC.ContactId
Order by CC.ContactId) CC
on CC.CustomerId = C.CustomerId
WHERE C.Code = 'B82'
Well, it's a bit hard to understand your question, but i guess you have multiple rows of the same customer, but not every row has values in the email column, am I right?
For this you should try including WHERE Email IS NOT NULL in your SQL string, the part with TOP 1 is good, but try to make the SQL statement return values only if it finds an email, using Email IS NOT NULL.
I hope I understood your question and that this helps you.
Have you tried to LEFT JOIN directly the second request instead of using a nested request? And then you take TOP 1 on the main request?
It would be easier to help if you had a SQL fiddle.
You can combine the data from Contacts with [SQL Row_Number() function][1] and additional Partition By clause as follows
Please note instead of TOP, I used Row_number function and used the result in WHERE clause with rn=1 for choosing the top 1
;with cte as (
select
c.Code as Customer, c.Name as CustomerName, c.Email CustomerEmail, cc.Email ContactEmail,
rn = ROW_NUMBER() over (partition by c.CustomerId order by cc.Email desc)
from Customer c
left join Contact cc on c.CustomerId = cc.CustomerId
left join ContactDocumentOption o on o.TransactionTypeId = 102
and o.ContactId = cc.ContactId
where c.Code = 'B82'
)
select Customer, CustomerName, ISNULL(ContactEmail,CustomerEmail) Email
from cte where rn = 1
ISNULL() function is a better way to compare two fields according to one of the field's nullable case
Output is as follows
I have two tables like this
Member and their Purchases
I need the output like this
Member_ID | CountofProducts
(and the Product Value not should be 0 and Purchase Status = 1)
SELECT
MemberName,
(SELECT COUNT(*) AS Count
FROM dbo.Purchases
WHERE MemberName = dbo.Members.MemberName
AND Res_Status = 1) AS Count
FROM
dbo.Members
I can get their total CountofPurchased Products from the above query but I need to avoid their count=0 how to do it ?
You can try something like
SELECT m.MemberName,
COUNT(p.*) Cnt
FROM Members m INNER JOIN
Purchases p ON m.MemberName = p.MemberName
WHERE p.Res_Status = 1
GROUP BY m.MemberName
There is no need for the HAVING clause, as the INNER JOIN will exclude all entries in Members that do not have Purchases.
SELECT m.MemberName, COUNT(p.*) AS CountOfProducts
FROM Members m
INNER JOIN Purchases p ON m.MemberName = p.MemberName
WHERE p.Res_Status = 1
GROUP BY m.MemberName
HAVING COUNT(p.*) > 0
I think the above will somewhat do what you want. The key is that you probably do not even need your subquery, but rather you can handle the query (possibly with greater efficiency) just using a join.
I have four tables: Customer, CustomerCategory, Limit, and LimitCategory. A customer can be in multiple categories and a limit can also have multiple categories. I need to write a query that will return the customer name and limit amount where ALL the customers categories match ALL the limit categories.
I'm guessing it would be similar to the answer here, but I can't seem to get it right. Thanks!
Edit - Here's what the tables look like:
tblCustomer
customerId
name
tblCustomerCategory
customerId
categoryId
tblLimit
limitId
limit
tblLimitCategory
limitId
categoryId
I THINK you're looking for:
SELECT *
FROM CustomerCategory
LEFT OUTER JOIN Customer
ON CustomerCategory.CustomerId = Customer.Id
INNER JOIN LimitCategory
ON CustomerCategory.CategoryId = LimitCategory.CategoryId
LEFT OUTER JOIN Limit
ON Limit.Id = LimitCategory.LimitId
Updated!
Thanks to Felix for pointing out a flaw in my existing solution (3 years after I originally posted it, hehe). After looking at it again, I think this might be correct. Here I'm getting (1) the customers and limits with matching categories, plus the number of matching categories, (2) the number of categories per customer, (3) the number of categories per limit, (4) I then ensure the number of categories for customer and limits is the same as the number of the matches between the customers and limits:
UNTESTED!
select
matches.name,
matches.limit
from (
select
c.name,
c.customerId,
l.limit,
l.limitId,
count(*) over(partition by cc.customerId, lc.limitId) as matchCount
from tblCustomer c
join tblCustomerCategory cc on c.customerId = cc.customerId
join tblLimitCategory lc on cc.categoryId = lc.categoryId
join tblLimit l on lc.limitId = l.limitId
) as matches
join (
select
cc.customerId,
count(*) as categoryCount
from tblCustomerCategory cc
group by cc.customerId
) as customerCategories
on matches.customerId = customerCategories.customerId
join (
select
lc.limitId,
count(*) as categoryCount
from tblLimitCategory lc
group by lc.limitId
) as limitCategories
on matches.limitId = limitCategories.limitId
where matches.matchCount = customerCategories.categoryCount
and matches.matchCount = limitCategories.categoryCount
I don't know if this will work or not, just a thought i had and i can't test it, I'm sures theres a nicer way! don't be too harsh :)
SELECT
c.customerId
, l.limitId
FROM
tblCustomer c
CROSS JOIN
tblLimit l
WHERE NOT EXISTS
(
SELECT
lc.limitId
FROM
tblLimitCategory lc
WHERE
lc.limitId = l.id
EXCEPT
SELECT
cc.categoryId
FROM
tblCustomerCategory cc
WHERE
cc.customerId = l.id
)