MS SQL - counts grouped by month - sql-server

I have a table [tblCalls] containing a date and a telephone number.[logdate] & [telephone]
I'm trying to produce an output similar to the following:
Date Mobiles Landlines
Nov 2016 28 47
Dec 2016 65 98
Jan 2017 11 17
... and so on
Using the following:
SELECT MONTH(logdate) as myDate,
SUM(CASE WHEN telephone LIKE '07%' THEN 1 ELSE 0 END) mobiles,
sum(CASE WHEN telephone NOT LIKE '07%' THEN 1 ELSE 0 END) landlines
FROM tblCalls
WHERE username='myusername' AND YEAR(logdate)='2016'
GROUP BY MONTH(logdate)
I can produce this:
myDate Mobiles Landlines
1 28 47
2 65 98
3 11 17
4 09 14
5 32 8
... and so on
My question is in 2 parts.
1. How do I combine the month and year together and span years (not just 2016).
2. I have another table [tblUsersLogs] which also contains a date [logdate] and a username, I'd like include a count from this table on the number of entries for a specific user grouped by months in the year to produce something like this...
Date Mobiles Landlines UsernameCount
Nov 2016 28 47 50
Dec 2016 65 98 44
Jan 2017 11 17 45
... and so on
Is this possible?
UPDATE (some sample data)
[tblCalls]
logDate telephone
2017-01-04 12:18:36.243 01507443000
2017-01-04 11:23:17.313 07880507000
2017-01-04 11:23:16.760 01216286000
2017-01-04 11:23:15.837 07541360000
2017-01-04 11:23:15.570 01970611000
[tblUserLogs]
logDate username
2017-01-04 12:23:51.530 usera
2017-01-04 12:23:38.350 usera
2017-01-04 12:23:08.530 userb
2017-01-04 12:22:45.020 userc
2017-01-04 12:22:35.437 usera
Hope that helps

Not clear on UserNameCount so I made an assumption and removed the WHERE
Select Date = Format(myDate,'MMM yyyy')
,mobiles = sum(CASE WHEN telephone LIKE '07%' THEN 1 ELSE 0 END)
,landlines = sum(CASE WHEN telephone NOT LIKE '07%' THEN 1 ELSE 0 END)
,UserNamesCount = count(Distinct Usernames)
From tblCalls
Where Year(logdate)=2016
Group By Format(myDate,'MMM yyyy')
Order By Month(myDate)

All you need to do is to change your dates all to the same day of the month and then leave the formatting to your presentation/reporting layer:
SELECT dateadd(m,datediff(m,0,logdate),0) as myDate -- This will return the first day of the month.
,SUM(CASE WHEN telephone LIKE '07%' THEN 1 ELSE 0 END) mobiles
,SUM(CASE WHEN telephone NOT LIKE '07%' THEN 1 ELSE 0 END) landlines
FROM tblCalls
WHERE username = 'myusername' -- Avoid using functions in your WHERE criteria.
AND logdate >= '20160101' -- Doing so makes your filtering much less efficient.
AND logdate < '20170101' -- For more info, google 'SARGability'.
GROUP BY dateadd(m,datediff(m,0,logdate),0)
If you absolutely have to format within your SQL query, you can use:
left(datename(month,logdate),3) + ' ' + cast(year(logdate) as nvarchar(4))

Related

How to get Month wise data from SQL using PIVOT

SELECT * FROM (
SELECT YEAR(CreateDate) [Year], MONTH(CreateDate) [Month], DATENAME(MONTH,CreateDate) [Month Name],COUNT(id) [Total],
CASE
WHEN PaymentStatus = 1 THEN 'Pending'
WHEN PaymentStatus = 2 THEN 'Commited'
WHEN PaymentStatus = 3 THEN 'Confirmed'
WHEN PaymentStatus = 4 THEN 'Canceled'
WHEN PaymentStatus = 5 THEN 'Failed'
ELSE ''
END AS PaymentStatus,count(ID) as counts
FROM [dbo].[BankPaymentRequest]
GROUP BY YEAR(CreateDate), MONTH(CreateDate), DATENAME(MONTH, CreateDate), PaymentStatus) as asd
PIVOT( SUM(counts)
FOR PaymentStatus IN ([Pending],[Commited],[Confirmed],[Canceled],[Failed])) AS MNamePivot
I want Year-Month wise data from this query with respective [Pending],[Commited],[Confirmed],[Canceled],[Failed] transaction count. Basically i want Month wise Transaction count for Pending,Commited,Confirmed,Canceled and Failed.
I am using Sql Server 2016
Now i am getting exact data but Months are listed twice or thrice.
I want data like below format
Year Month Total Pending Commited Confirmed Canceled Failed
2016 Jan 34 1 4 63 840 157
2016 Feb 34 8 4 62 8 15
2016 Mar 65 1 4 63 840 157
2016 Dec 56 8 4 62 8 15
2017 Jan 78 1 4 63 840 157
2017 Feb 89 8 4 62 8 15
2017 Mar 67 1 4 63 840 157
2017 Dec 8 4 62 8 15 345
Please help me for such query or any alternative query.
You could try putting your existing query inside of a Common Table Expression and then GROUP BY the year and month while summing the numbers.
WITH preselect AS
(
SELECT * FROM (
SELECT YEAR(CreateDate) [Year], MONTH(CreateDate) [Month], DATENAME(MONTH,CreateDate) [Month Name],COUNT(id) [Total],
CASE
WHEN PaymentStatus = 1 THEN 'Pending'
WHEN PaymentStatus = 2 THEN 'Commited'
WHEN PaymentStatus = 3 THEN 'Confirmed'
WHEN PaymentStatus = 4 THEN 'Canceled'
WHEN PaymentStatus = 5 THEN 'Failed'
ELSE ''
END AS PaymentStatus,count(ID) as counts
FROM [dbo].[BankPaymentRequest]
GROUP BY YEAR(CreateDate), MONTH(CreateDate), DATENAME(MONTH, CreateDate), PaymentStatus) as asd
PIVOT( SUM(counts)
FOR PaymentStatus IN ([Pending],[Commited],[Confirmed],[Canceled],[Failed])) AS MNamePivot
)
SELECT [YEAR],[MONTH],SUM(TOTAL)'TOTAL',SUM(Pending)'Pending'
,SUM(Commited)'Commited',SUM(Confirmed)'Confirmed'
,SUM(Canceled)'Canceled',SUM(Failed)'Failed'
FROM preselect
GROUP BY [YEAR],[MONTH]

Group by and sum based on column values without sum() over()?

We have a table [Kpis] that looks like the following:
RawId EmpId Date Hour Min KpiValue KpiName
106 ABC123 20160310 8 0 3 Kpi1
124 ABC123 20160310 8 0 65 Kpi1
121 ABC123 20160310 8 15 12 Kpi2
109 ABC109 20160310 8 0 34 Kpi2
112 ABC908 20160310 9 5 3 Kpi1
118 ABC907 20160310 8 30 24 Kpi1
115 ABC123 20160310 8 15 54 Kpi1
I would like to group by EmpId, KpiName, Date, Hour. So, for example, with this data, Kpi1 for EmpId ABC123 at Hour 8 would be 122.
So I tried using the CASE statement, but the result is incorrect. I haven't checked the actual totals in the result, but the sums should be correct. It's the format of the result that's incorrect; every empid has two rows: one for Kpi1 and one for Kpi2.
select empid,
case kpiname when 'Kpi1' then sum(kpivalue) end as 'Kpi1',
case kpiname when 'Kpi2' then sum(kpivalue) end as 'Kpi2'
from
[Kpis]
where kpiname in ('Kpi1', 'Kpi2')
and date = 20160310 and hour = 8
group by empid, kpiname, hour
How can I use the Case statement to fix the results?
Thanks.
Put the case inside your sum, such that you for each KpiName only sums the relevant values.
SELECT
EmpId,
[Hour],
SUM(
CASE
WHEN KpiName = 'Kpi1' THEN KpiValue
ELSE 0
END
) Kpi1,
SUM(
CASE
WHEN KpiName = 'Kpi2' THEN KpiValue
ELSE 0
END
) Kpi2
FROM
Kpis
GROUP BY
EmpId,
[Hour]
This produces this output
EmpId Hour Kpi1 Kpi2
ABC109 8 0 34
ABC123 8 122 12
ABC907 8 24 0
ABC908 9 3 0
SUM fucntion have to be outside of CASE:
select empid,
sum(case kpiname when 'Kpi1' then kpivalue end) as 'Kpi1',
sum(case kpiname when 'Kpi2' then kpivalue end) as 'Kpi2'
from
[Kpis]
where kpiname in ('Kpi1', 'Kpi2')
and date = 20160310 and hour = 8
group by empid, kpiname, hour
You can also do this with the PIVOT functionality, which I believe is what you're actually trying to accomplish.
SELECT
*
FROM (
SELECT
EmpId,
KpiName,
[Hour],
KpiValue
FROM
Kpis
) SourceTable
PIVOT (
SUM(KpiValue)
FOR KpiName
IN ([Kpi1],[Kpi2])
) PivotTable
Which gives this output. Note the NULLs as opposed to the zeros, correctly showing the lack of data.
EmpId Hour Kpi1 Kpi2
ABC109 8 NULL 34
ABC123 8 122 12
ABC907 8 24 NULL
ABC908 9 3 NULL

How do I round up a date?

I have a table that contains the contract information for our customers. I need to round up the contract maturity date to a certain day of the month depending on the contract date itself. For example, if the contract date is 01-05-2016 I need to round it up to 01-10-2016. If the contract date is 01-11-2016 I need to round it up to 01-20-2016. And finally, if the contract date is 01-21-2016 I need to round it up to 01-30-2016. These round up dates match our billing cycles and I need all of our contracts to fall within one of these billing cycles. All dates are a DATETIME data type. Any help would be appreciated.
Maybe something like this?
DECLARE #testData TABLE(TestDate DATE);
INSERT INTO #testData VALUES({d'2016-02-05'}),({d'2016-02-12'}),({d'2016-02-21'});
SELECT TestDate
,CASE WHEN DAY(TestDate) BEtWEEN 1 AND 10 THEN 1
WHEN DAY(TestDate) BEtWEEN 11 AND 20 THEN 2
ELSE 3 END AS BillingCycle
,CASE WHEN DAY(TestDate) BEtWEEN 1 AND 10 THEN CAST(CAST(YEAR(TestDate) AS CHAR(4))+REPLACE(STR(MONTH(TestDate),2),' ','0')+'01' AS DATE)
WHEN DAY(TestDate) BEtWEEN 11 AND 20 THEN CAST(CAST(YEAR(TestDate) AS CHAR(4))+REPLACE(STR(MONTH(TestDate),2),' ','0') +'11' AS DATE)
ELSE CAST(CAST(YEAR(TestDate) AS CHAR(4))+REPLACE(STR(MONTH(TestDate),2),' ','0')+'28' AS DATE) END AS BillingCycleDate
FROM #testData
The result:
TestDate BillingCycle BillingCycleDate
2016-02-05 1 2016-02-01
2016-02-12 2 2016-02-11
2016-02-21 3 2016-02-28
You would do much better to avoid casting dates from strings. The long way is something like this though I'm sure it could be shortened and obfuscated. You did not specify what to do with dates after the 28th though:
dateadd(
day,
case day(contract_dt)
when 1 then 9
when 2 then 8
...
when 10 then 0
when 11 then 9
when 12 then 8
...
when 20 then 0
when 21 then 7
when 22 then 6
...
when 28 then 0
when 29 then -1
when 30 then -2
when 31 then -3
end,
contract_dt
)
Here is one of the more compact forms I alluded to:
dateadd(day, case
when day(contract_dt) <= 20
then 10 - day(contract_dt) % 10
else 28 - day(contract_dt)
end, contract_dt)
EDIT: I presume based on your acceptance of the other answer that you want to fall back to the 28th for the outliers and so I edited the above accordingly.

SQL query sorted by year, counting values per year

I have this SQL query, which gives me the numbers of orders within a date range, and that is OK:
SELECT CardName, ItemNumber, COUNT(*) AS antal, year(date) as aarstal, SUM(Numbers) AS total, sum(case when year(date)=2012 then 1 else 0 end) as newtest
FROM tblOrders
WHERE (Publisher = 3) AND (Date > '01-01-2012 00:00:00') AND (Date < '30-09-2014 23:59:59') AND (Status = 3) and ItemNumber!=9130 and ItemNumber!=9180 and ItemNumber!=9170 and ItemNumber!=9190
GROUP BY ItemNumber, CardName, year(date)
This gives me ie
CompanyA 0 76 2012 10900 76
CompanyA 0 42 2013 6300 0
CompanyB 0 1 2012 100 1
CompanyB 0 7 2013 1100 0
CompanyC 0 1 2014 300 0
CompanyD 0 7 2014 700 0
CompanyE 0 2 2012 300 2
CompanyE 0 1 2013 200 0
From this, I can see that CompanyA bought 10900 units in 2012, from a total of 76 orders that year, and in 2013, they bought 6300 units. For sales reasons, we would like (or need) this output as
CompanyA 0 76 2012 10900 42 2013 6300
CompanyB 0 1 2012 100 7 2013 1100
CompanyC 0 1 2014 300 0 2013 0
CompanyD 0 7 2014 700 0 2013 0
CompanyE 0 2 2012 300 1 2013 200
So you could see if a customer is going up or down in sales. I've tried various things, but I don't know if there is any possibility to have the Numbers column SUM'ed up per year in a query like this, any suggestions?
PS: The
sum(case when year(date)=2012 then 1 else 0 end) as newtest
was just for a test, I tried something like SUM(Numbers case...) but that was not accepted!
EDIT: I just made a demo in SQL Fiddle, just to give a better understanding of my problem:
http://sqlfiddle.com/#!3/072a1/5
In there, you can see that my customer A has bought a total of 300 in 2012 (on 2 orders), 700 in 2013 (on 2 orders), and finally 200 in 2014 (on 1 order). The pivot examples I've found only counts the number of orders, and not the value of my Numbers - could somebody edit the SQL Fiddle, making that one work, then I'm sure I could learn and adapt the solution into my "real" database?
/Tommy

Why some dates give worse performance than other in MS SQL Server

I have a query in MS SQL Server asking for name and some date-related information, depending on two dates, a start- and an enddate.
The problem is, I´m not always getting the same performance. Whenever I request something between the dates;
2010-07-01 00:00:00.000 and
2011-07-21 23:59:59.999
the performance is excellent. I get my result within mseconds. When I request something between these dates, for example,
2011-07-01 00:00:00.000 and
2011-07-21 23:59:59.999
the performance is.. less than good, taking between 20-28 seconds for each query. Do note how the dates giving good performance is more than a year between, while the latter is 20 days.
Is there any particular reason (maybe related to how DATETIME work) for this?
EDIT: The query,
SELECT ENAME,
SUM(CASE DATE WHEN 0 THEN 1 ELSE 0 END) AS U2,
SUM(CASE DATE WHEN 1 THEN 1 ELSE 0 END) AS B_2_4,
SUM(CASE DATE WHEN 2 THEN 1 ELSE 0 END) AS B_4_8,
SUM(CASE DATE WHEN 3 THEN 1 ELSE 0 END) AS B_8_16,
SUM(CASE DATE WHEN 4 THEN 1 ELSE 0 END) AS B_16_24,
SUM(CASE DATE WHEN 5 THEN 1 ELSE 0 END) AS B_24_48,
SUM(CASE DATE WHEN 6 THEN 1 ELSE 0 END) AS O_48,
SUM(CASE DATE WHEN 7 THEN 1 ELSE 0 END) AS status,
AVG(AVG) AS AVG,
SUM(DATE) AS TOTAL
FROM
(SELECT ENAME,
(CASE
WHEN status = 'Öppet' THEN 7
WHEN DATE < 48 THEN
(CASE WHEN DATE BETWEEN 0 AND 2 THEN 0
WHEN DATE BETWEEN 2 AND 4 THEN 1
WHEN DATE BETWEEN 4 AND 8 THEN 2
WHEN DATE BETWEEN 8 AND 16 THEN 3
WHEN DATE BETWEEN 16 AND 24 THEN 4
WHEN DATE BETWEEN 24 AND 48 THEN 5
ELSE - 1 END)
ELSE 6 END) AS DATE,
DATE AS AVG
FROM
(SELECT DATEDIFF(HOUR, cases.date, status.date) AS DATE,
extern.name AS ENAME,
status.status
FROM
cases INNER JOIN
status ON cases.id = status.caseid
AND status.date =
(SELECT MAX(date) AS Expr1
FROM status AS status_1
WHERE (caseid = cases.id)
GROUP BY caseid) INNER JOIN
extern ON cases.owner = extern.id
WHERE (cases.org = 'Expert')
AND (cases.date BETWEEN '2009-01-15 09:48:25.633'
AND '2011-07-21 09:48:25.633'))
AS derivedtbl_1)
AS derivedtbl_2
GROUP BY ENAME
ORDER BY ENAME
(parts of) The tables:
Extern
-ID (->cases.owner)
-name
Cases
-Owner (->Extern.id)
-id (->status.caseid)
-date (case created at this date)
Status
-caseid (->cases.id)
-Status
-Date (can be multiple, MAX(status.date) gives us date when
status was last changed)
I would have thought a statistics issue.
When you are only selecting the most recent dates these may be unrepresented in the statistics yet as the threshold has not yet been reached that would trigger auto updating.
See this blog post for an example.

Resources