Help with difficult 'group by' clause - tsql

need some your help with a query.
I have a table Managers (ManagerId, ManagerName)
I have a table Statuses (StatusId, StatusName)
(There's about 10 statuses in that table)
I have a table Clients (ClientId, ClientName, ManagerId, StatusId, WhenAdded)
(WhenAdded is a datetime type)
It's obvious that field 'ManagerId' refers to a table 'Managers' and field 'StatusId' refers to a table 'Statuses'.
User wants to get some statistics about Managers over a period of time (from startDate to endDate using field 'WhenAdded') in the following table.
Columns:
ManagerName, NumberOfClients, NumberOfClientsWithStatus1, NumberOfClientsWithStatus2, NumberOfClientsWithStatus3 and so on.
Number of columns with name NumberOfClientsWithStatusI where i is a number of statuses equal to number of rows in table 'Statuses'.
How can I do that?
t-sql, sql server 2008 r2 express edition.

SELECT
ManagerName,
COUNT(*) AS NumberOfClients,
COUNT(CASE WHEN S.StatusId = 1 THEN 1 ELSE NULL END) AS NumberOfClientsWithStatus1,
COUNT(CASE WHEN S.StatusId = 2 THEN 1 ELSE NULL END) AS NumberOfClientsWithStatus2,
COUNT(CASE WHEN S.StatusId = 3 THEN 1 ELSE NULL END) AS NumberOfClientsWithStatus3,
...
FROM
Clients C
JOIN
Managers M ON C.ManagerId = M.ManagerId
JOIN
Statuses S ON C.StatusId = S.StatusId
WHERE
M.WhenAdded BETWEEN #startDate AND #endDate
GROUP BY
M.ManagerName
Note: there is no clean way to add arbritrary numbers of status columns in SQL (not just SQL Server) because its a fixed column output. You'd have to change the query for status, unless you deal with this in the client
Edit, after comment
SELECT
ManagerName,
COUNT(*) AS NumberOfClients,
COUNT(CASE WHEN S.StatusId = 1 THEN 1 ELSE NULL END) AS NumberOfClientsWithStatus1,
COUNT(CASE WHEN S.StatusId = 2 THEN 1 ELSE NULL END) AS NumberOfClientsWithStatus2,
COUNT(CASE WHEN S.StatusId = 3 THEN 1 ELSE NULL END) AS NumberOfClientsWithStatus3,
...
FROM
Managers M ON C.ManagerId = M.ManagerId
LEFT JOIN
Clients C
LEFT JOIN
Statuses S ON C.StatusId = S.StatusId
WHERE
M.WhenAdded BETWEEN #startDate AND #endDate
GROUP BY
M.ManagerName

If you know that statuses table will always contain a limited number of statuses, you can do this:
SELECT M.ManagerName,
COUNT(C.ClientId) NumberOfClients,
SUM(CASE WHEN S.StatusId= 1 THEN 1 ELSE 0 END) NumberOfClientsWithStatus1,
SUM(CASE WHEN S.StatusId= 2 THEN 1 ELSE 0 END) NumberOfClientsWithStatus2,
...
FROM Clients C
JOIN Managers M on M.ManagerId = C.ManagerId
JOIN Statuses S on S.StatusId = C.StatusId
WHERE C.WhenAdded BETWEEN startDate AND endDate
GROUP BY ManagerName

Related

Using Where clause in between Inner Join Query

I am looking to fetch records and I have come through a scenario in which i have to include additional where clauses between the select query using inner join.
select stp.sales_person as "Sales_Person",
max(case when stp.jan_month is null then 0 else stp.jan_month end) as "January",
select sum(so.amount_total) from sale_order so inner join res_users ru on ru.id=so.user_id
where date(so.confirmation_date) > '2017-01-01' and date(so.confirmation_date) < '2017-01-30',
max(case when stp.feb_month is null then 0 else stp.feb_month end) as "February",
max(case when stp.march_month is null then 0 else stp.march_month end) as "March",
max(case when stp.dec_month is null then 0 else stp.dec_month end) as "December"
from sales_target_record stp
inner join res_partner rp on rp.name=stp.sales_person inner join res_users ru on ru.partner_id = rp.id inner join crm_team ct on ru.sale_team_id = ct.id
where ct.name = 'Direct Sales' group by stp.sales_person
I have to insert columns like i tried with sum but is not working as its a join query
You have a syntax issue in your query if this is truly SQL Server
select
stp.sales_person as Sales_Person,
max(case
when stp.jan_month is null
then 0
else stp.jan_month
end) as January,
--This needed parenthese since it's a subquery. Though, it's uncorrelated
( select sum(so.amount_total)
from sale_order so
inner join res_users ru on ru.id=so.user_id
where cast(so.confirmation_date as date) > '2017-01-01' and cast(so.confirmation_date as date) < '2017-01-30'
--here you need to add something like stp.someColumn = so.SomeColumn to correlate it to the outer query
) as SomeNewColumnUnCorrelated,
max(case
when stp.feb_month is null
then 0
else stp.feb_month
end) as February,
max(case
when stp.march_month is null
then 0
else stp.march_month
end) as March,
max(case
when stp.dec_month is null
then 0
else stp.dec_month
end) as December
from
sales_target_record stp
inner join
res_partner rp on
rp.name=stp.sales_person
inner join
res_users ru on
ru.partner_id = rp.id
where
ct.name = 'Direct Sales'
group by
stp.sales_person

SQL Query handing Jaspersoft Studio Read Fields

I have this current query. I have tested it on 2 of the same verions of Jaspersoft Studio. When I hit read fields, one instance of the program gives me the columns I want however the other instance is stuck in a loop. I have checked the error logs and there is nothing for me to troubleshoot.
I had theorized it was the query but why would it work in one instance and not the next.
Where can I look to find out what is happening? What are my options?
SELECT
LOCATIONS.ID AS LOCATIONID,
LOCATIONS.NAME AS LOCATIONNAME,
PRODUCTS.REFERENCE,
PRODUCTS.CODE AS BARCODE,
PRODUCTS.NAME AS ITEM_NAME,
STOCKDIARY.ATTRIBUTESETINSTANCE_ID,
ATTRIBUTESETINSTANCE.DESCRIPTION AS ATTINSTANCEDESC,
PRODUCTS.CATEGORY,
TICKETLINES.UNITS,
CATEGORIES.NAME AS CATEGORYNAME,
STOCKDIARY.DATENEW,
STOCKDIARY.REASON,
SUM(CASE WHEN STOCKDIARY.UNITS <0 AND STOCKDIARY.DATENEW >= $P{start_date} AND (STOCKDIARY.DATENEW) <= $P{end_date} THEN STOCKDIARY.UNITS ELSE 0 END) AS UNITSOUT,
SUM(CASE WHEN STOCKDIARY.UNITS >=0 AND STOCKDIARY.DATENEW >= $P{start_date} AND (STOCKDIARY.DATENEW) <= $P{end_date} THEN STOCKDIARY.UNITS ELSE 0 END) AS UNITSIN,
SUM(CASE WHEN STOCKDIARY.DATENEW <= $P{end_date} THEN STOCKDIARY.UNITS ELSE 0 END) AS STOCKTOTALNEW,
SUM(CASE WHEN STOCKDIARY.DATENEW <= $P{start_date} THEN STOCKDIARY.UNITS ELSE 0 END) AS STARTINGBALANCE,
SUM(TICKETLINES.UNITS) AS UNIT
FROM TICKETLINES,
STOCKDIARY
LEFT JOIN
LOCATIONS ON STOCKDIARY.LOCATION = LOCATIONS.ID
INNER JOIN
PRODUCTS ON PRODUCTS.ID = STOCKDIARY.PRODUCT
LEFT JOIN
CATEGORIES ON PRODUCTS.CATEGORY = CATEGORIES.ID
LEFT JOIN
ATTRIBUTESETINSTANCE ON STOCKDIARY.ATTRIBUTESETINSTANCE_ID = ATTRIBUTESETINSTANCE.ID
WHERE products.name LIKE "%"
GROUP BY LOCATIONS.NAME , PRODUCTS.REFERENCE , STOCKDIARY.ATTRIBUTESETINSTANCE_ID , ATTRIBUTESETINSTANCE.DESCRIPTION
ORDER BY PRODUCTS.NAME

how to select top 10 without duplicates

Using SQL Server 2012
I need to select TOP 10 Producer based on a ProducerCode. But the data is messed up, users were entering same Producers just spelled differently and with the same ProducerCode.
So I just need TOP 10, so if the ProducerCode is repeating, I just want to pick the first one in a list.
How can I achieve that?
Sample of my data
;WITH cte_TopWP --T
AS
(
SELECT distinct ProducerCode, Producer,SUM(premium) as NetWrittenPremium,
SUM(CASE WHEN PolicyType = 'New Business' THEN Premium ELSE 0 END) as NewBusiness1,
SUM(CASE WHEN PolicyType = 'Renewal' THEN Premium ELSE 0 END) as Renewal1,
SUM(CASE WHEN PolicyType = 'Rewrite' THEN Premium ELSE 0 END) as Rewrite1
FROM ProductionReportMetrics
WHERE YEAR(EffectiveDate) = 2016 AND TransactionType = 'Policy' AND CompanyLine = 'Arch Insurance Company'--AND ProducerType = 'Wholesaler'
GROUP BY ProducerCode,Producer
)
,
cte_Counts --C
AS
(
SELECT distinct ProducerCode, ProducerName, COUNT (distinct ControlNo) as Submissions2,
SUM(CASE WHEN QuotedPremium IS NOT NULL THEN 1 ELSE 0 END) as Quoted2,
SUM(CASE WHEN Type = 'New Business' AND Status IN ('Bound','Cancelled','Notice of Cancellation') THEN 1 ELSE 0 END ) as NewBusiness2,
SUM(CASE WHEN Type = 'Renewal' AND Status IN ('Bound','Cancelled','Notice of Cancellation') THEN 1 ELSE 0 END ) as Renewal2,
SUM(CASE WHEN Type = 'Rewrite' AND Status IN ('Bound','Cancelled','Notice of Cancellation') THEN 1 ELSE 0 END ) as Rewrite2,
SUM(CASE WHEN Status = 'Declined' THEN 1 ELSE 0 END ) as Declined2
FROM ClearanceReportMetrics
WHERE YEAR(EffectiveDate)=2016 AND CompanyLine = 'Arch Insurance Company'
GROUP BY ProducerCode,ProducerName
)
SELECT top 10 RANK() OVER (ORDER BY NetWrittenPremium desc) as Rank,
t.ProducerCode,
c.ProducerName as 'Producer',
NetWrittenPremium,
t.NewBusiness1,
t.Renewal1,
t.Rewrite1,
c.[NewBusiness2]+c.[Renewal2]+c.[Rewrite2] as PolicyCount,
c.Submissions2,
c.Quoted2,
c.[NewBusiness2],
c.Renewal2,
c.Rewrite2,
c.Declined2
FROM cte_TopWP t --LEFT OUTER JOIN tblProducers p on t.ProducerCode=p.ProducerCode
LEFT OUTER JOIN cte_Counts c ON t.ProducerCode=c.ProducerCode
You should use ROW_NUMBER to fix your issue.
https://msdn.microsoft.com/en-us/library/ms186734.aspx
A good example of this is the following answer:
https://dba.stackexchange.com/a/22198
Here's the code example from the answer.
SELECT * FROM
(
SELECT acss_lookup.ID AS acss_lookupID,
ROW_NUMBER() OVER
(PARTITION BY your_distinct_column ORDER BY any_column_you_think_is_appropriate)
as num,
acss_lookup.product_lookupID AS acssproduct_lookupID,
acss_lookup.region_lookupID AS acssregion_lookupID,
acss_lookup.document_lookupID AS acssdocument_lookupID,
product.ID AS product_ID,
product.parent_productID AS productparent_product_ID,
product.label AS product_label,
product.displayheading AS product_displayheading,
product.displayorder AS product_displayorder,
product.display AS product_display,
product.ignorenewupdate AS product_ignorenewupdate,
product.directlink AS product_directlink,
product.directlinkURL AS product_directlinkURL,
product.shortdescription AS product_shortdescription,
product.logo AS product_logo,
product.thumbnail AS product_thumbnail,
product.content AS product_content,
product.pdf AS product_pdf,
product.language_lookupID AS product_language_lookupID,
document.ID AS document_ID,
document.shortdescription AS document_shortdescription,
document.language_lookupID AS document_language_lookupID,
document.document_note AS document_document_note,
document.displayheading AS document_displayheading
FROM acss_lookup
INNER JOIN product ON (acss_lookup.product_lookupID = product.ID)
INNER JOIN document ON (acss_lookup.document_lookupID = document.ID)
)a
WHERE a.num = 1
ORDER BY product_displayheading ASC;
You could do this:
SELECT ProducerCode, MIN(Producer) AS Producer, ...
GROUP BY ProducerCode

Obtaining certain value from child table

I have two tables:
Customer (Parent)
Agreement (Child)
I need to display a certain value in my query if one of the many agreement statusID's has a certain value (e.g. written off). The join between the two tables is CustomerID.
So if a customer has 3 agreements and 2 agreements have a statusID of 1 and one has 5, I need to display a certain value. I only want to return one row in this query rather than the 3 which would occur in a typical join
Any suggestions?
select
CustomerID,
max(case when StatusId = 1 then 1 else 0 end) as HasStatus1,
max(case when StatusId = 2 then 1 else 0 end) as HasStatus2
--etc.
from Customer
left join Agreement
group by Customer.CustomerID
This will return a single row per customer due to the group by, with flags indicating if they have any agreements in each status of concern - if you're looking this up for a single CustomerID you'd obviously throw a where statement in there, and you could remove the group by as well (you'd have to remove CustomerID from the result set though of course).
Taking into account your comment, you'd want something like:
;with grouped as (
select
CustomerID,
max(case when StatusId = 1 then 1 else 0 end) as HasStatus1,
max(case when StatusId = 2 then 1 else 0 end) as HasStatus2,
max(case when StatusId = 5 then 1 else 0 end) as HasStatus5
--etc.
from Customer
left join Agreement
group by Customer.CustomerID
)
select
CustomerID,
case
when HasStatus5 = 1 then 5
when (HasStatus1 = 1 OR HasStatus2) and <no other status>) then 1
--etc.
else <Can't return StatusId here because there might be more than one... so whatever your default actually is> END as Result
from grouped

Show Rows Data Into Column Wise

i wants to show rows data into columns. suppose if rows type increased then number of columns also increased. Information as follow:
Please see picture to get idea
You can use COUNT with CASE WHEN:
SELECT t.Name AS Type,
COUNT(*) AS NumberOfCase,
COUNT(CASE WHEN s.Name = 'Resolved' THEN 1 END) AS Resolved,
COUNT(CASE WHEN s.Name = 'Pending' THEN 1 END) AS Pending,
COUNT(CASE WHEN s.Name = 'Waiting' THEN 1 END) AS Waiting
FROM Type t
LEFT JOIN "Case" c
ON c.CaseType = t.TypeId
LEFT JOIN "Status" s
ON c.CaseStatus = s.StatusId
GROUP BY t.Name;
SqlFiddleDemo