SQL Server: better / faster way of combining multiple counts in select - sql

I am using the below stored procedure in order to combine multiple counts.
This works fine so far and returns the following XML, i.e. a count of records for each of the last 6 months.
As I am pretty new to SQL and all these counts are done on the same table I was wondering if there is a better / faster way to achieve the same.
Example result (XML):
<ranks>
<groupCount>18</groupCount>
<groupCount>15</groupCount>
<groupCount>21</groupCount>
<groupCount>13</groupCount>
<groupCount>15</groupCount>
<groupCount>19</groupCount>
</ranks>
My stored procedure:
BEGIN
SET NOCOUNT ON;
SELECT COUNT(*) AS groupCount
FROM Log_PE
WHERE CONVERT(DATE, dateEsc, 120) >= CONVERT(DATE, CONVERT(VARCHAR(6), GETDATE(), 112) + '01', 112)
UNION ALL
SELECT COUNT(*) AS groupCount
FROM Log_PE
WHERE CONVERT(DATE, dateEsc, 120) >= CONVERT(DATE, CONVERT(VARCHAR(6), DATEADD(month, -1, GETDATE()), 112) + '01', 112)
UNION ALL
SELECT COUNT(*) AS groupCount
FROM Log_PE
WHERE CONVERT(DATE, dateEsc, 120) >= CONVERT(DATE, CONVERT(VARCHAR(6), DATEADD(month, -2, GETDATE()), 112) + '01', 112)
UNION ALL
SELECT COUNT(*) AS groupCount
FROM Log_PE
WHERE CONVERT(DATE, dateEsc, 120) >= CONVERT(DATE, CONVERT(VARCHAR(6), DATEADD(month, -3, GETDATE()), 112) + '01', 112)
UNION ALL
SELECT COUNT(*) AS groupCount
FROM Log_PE
WHERE CONVERT(DATE, dateEsc, 120) >= CONVERT(DATE, CONVERT(VARCHAR(6), DATEADD(month, -4, GETDATE()), 112) + '01', 112)
UNION ALL
SELECT COUNT(*) AS groupCount
FROM Log_PE
WHERE CONVERT(DATE, dateEsc, 120) >= CONVERT(DATE, CONVERT(VARCHAR(6), DATEADD(month, -5, GETDATE()), 112) + '01', 112)
FOR XML PATH(''), ROOT('ranks')
END
Many thanks for any help with this, Tim.

Your requirements seem to conflict with what you are doing in your SQL
select CONVERT(DATE, CONVERT(VARCHAR(6), GETDATE(), 112) + '01', 112)
will get the first day of the current month
select CONVERT(DATE, CONVERT(VARCHAR(6), GETDATE(), 112) + '02', 112)
will get the second day of the current month
To get the count of the last 6 complete months of records grouped by month
SELECT COUNT(*) AS groupCount
FROM Log_PE
WHERE dateEsc >= CAST(DATEADD(day, 1, DATEADD(month, -6, DATEADD(day, day(GETDATE())*-1, GETDATE()))) as DATE) --the first day of the month, 6 months ago
AND dateEsc < DATEADD(day, (day(GETDATE())*-1)+1, GETDATE()) -- the first day of current month
GROUP BY year(dateEsc), month(dateEsc)
ORDER BY year(dateEsc), month(dateEsc)
FOR XML PATH(''), ROOT('ranks')
Here is a SQL Fiddle: http://www.sqlfiddle.com/#!3/3ff71/7

Related

Why Week conversion failed in SQL Server in my case?

I am trying to convert week of the date based on my criteria.
My date condition: if my #date is less than 4 AM, then #date - 1, else #date
declare #dates datetime
set #dates = '2019-01-01 03:59:59'
select
case
when convert(varchar(26), #dates, 108) <= '04:00:00'
then convert(varchar, dateadd(day, -1, #dates), 103)
else convert(varchar, #dates, 103)
end BusinessDate
Output:
31/12/2018 // as expected
Now I want to find the week number of the output. So I tried
declare #dates datetime
set #dates = '2019-01-01 03:59:59'
select
case
when convert(varchar(26), #dates, 108) <= '04:00:00'
then convert(varchar, dateadd(day, -1, #dates), 103)
else convert(varchar, #dates, 103)
end BusinessDate,
case
when convert(varchar(26), #dates, 108) <= '04:00:00'
then datepart(week, convert(datetime, convert(varchar, dateadd(day, -1, #dates), 103)))
else datepart(week, convert(datetime, convert(varchar, #dates, 103)))
end weeks
But I get this error:
The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
Just subtract four hours:
select datepart(week,
dateadd(hour, -4, #dates)
)

SQL query to select range from specific date/time yesterday to specific date/time current day

SELECT *
FROM services
WHERE service_date BETWEEN (dateadd(DD, -1, getdate())) AND (dateadd(DD, 1, getdate())) **---Gives us a last one day data.**
but what we really need is data from 07:00 AM yesterday to 06:59 AM current day, so I tried the following:
SELECT *
FROM services
WHERE service_date BETWEEN (dateadd(DD, -1,((DatePart(hh, getdate())) >= 7 ))) **-- Expecting to pull from yesterday 07:00**
AND (dateadd(DD, -1,((DatePart(hh, getdate())) <= 6 ))) **-- to current day till 06:59**
but the system didn't like my SQL skills ...PLEASE HELP...!
These will get you the dates you are looking for...
Select DATEADD(HOUR,-17, (DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), 0)))
Select DATEADD(HOUR,7, (DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), 0)))
I'm not sure if this is the most effective way but it works.
Assuming you are on SQL Server:
Select *
From MyTable
Where MyDateColumn Between DATEADD(hh, 7, Cast(Cast(DATEADD(d, -1, CURRENT_TIMESTAMP) As Date) As DateTime))
And DATEADD(hh, 6, Cast(Cast(CURRENT_TIMESTAMP As Date) As DateTime))
UPDATE: I agree with Andy's solution. Similar approach but his solution is much more elegant and efficient.
Try something like that:
SELECT *
FROM services
WHERE service_date
BETWEEN Convert(DateTime, Convert(nchar(4), DatePart(YYYY, GETDATE())) + '-'
+ Convert(nchar(2), DATEPART(MM, GETDATE())) + '-'
+ Convert(nchar(2), DATEPART(DD, Getdate())-1) + ' 7:00:00')
AND Convert(DateTime, Convert(nchar(4), DatePart(YYYY, GETDATE())) + '-'
+ Convert(nchar(2), DATEPART(MM, GETDATE())) + '-'
+ Convert(nchar(2), DATEPART(DD, Getdate())) + ' 6:59:00')
The idea is to take the year, month and day from today (or yesterday), and form a new date by concatenate them.
Beware of the culture settings for date format and do some testing before put this code into production.
You could also use mi (minutes) with DateAdd. Adding 419 minutes to midnight to get 06:59 and a nice round 420 minutes to yesterday midnight for 07:00
Edit: Changed the DateDiff function to work with Sybase.
SELECT *
FROM services
WHERE service_date BETWEEN DATEADD(mi, 420, DATEADD(d, -1, DATEDIFF(d, DATE('1900-01-01'), GETDATE())))
AND DATEADD(mi, 419, DATEADD(d, -0, DATEDIFF(d, DATE('1900-01-01'), GETDATE())));
Edit: Removing DateDiff function
SELECT *
FROM services
WHERE service_date BETWEEN DATEADD(mi, 420, DATEADD(d, - 1, Convert(VARCHAR(10), GETDATE(), 111)))
AND DATEADD(mi, 419, DATEADD(d, - 0, Convert(VARCHAR(10), GETDATE(), 111)));

SQL Server: add ranking to nested selects

I am using the following stored procedure which works fine so far.
Can someone tell me how I have to change this in order to get a ranking for each of the nested Selects ?
What I am looking for is a new rank that gets added to each of them so that every nested Select starts with 1, 2, 3 etc.
My SP:
SELECT *
FROM (
SELECT
(
SELECT policy,
COUNT(*) AS count0
FROM Log_PE
WHERE CONVERT(DATE, dateEsc, 120) >= CONVERT(DATE, CONVERT(VARCHAR(6), DATEADD(month, -1, GETDATE()), 112) + '01', 112)
GROUP BY policy
ORDER BY count0 desc, policy
FOR XML PATH('currentMonth'), ELEMENTS, TYPE
)
UNION ALL
SELECT
(
SELECT policy,
COUNT(*) AS count1
FROM Log_PE
WHERE CONVERT(DATE, dateEsc, 120) >= CONVERT(DATE, CONVERT(VARCHAR(6), DATEADD(month, -1, GETDATE()), 112) + '01', 112)
GROUP BY policy
ORDER BY count1 desc, policy
FOR XML PATH('prevMonth2'), ELEMENTS, TYPE
)
UNION ALL
SELECT
(
SELECT policy,
COUNT(*) AS count2
FROM Log_PE
WHERE CONVERT(DATE, dateEsc, 120) >= CONVERT(DATE, CONVERT(VARCHAR(6), DATEADD(month, -2, GETDATE()), 112) + '01', 112)
GROUP BY policy
ORDER BY count2 desc, policy
FOR XML PATH('prevMonth3'), ELEMENTS, TYPE
)
UNION ALL
SELECT
(
SELECT policy,
COUNT(*) AS count3
FROM Log_PE
WHERE CONVERT(DATE, dateEsc, 120) >= CONVERT(DATE, CONVERT(VARCHAR(6), DATEADD(month, -3, GETDATE()), 112) + '01', 112)
GROUP BY policy
ORDER BY count3 desc, policy
FOR XML PATH('prevMonth4'), ELEMENTS, TYPE
)
UNION ALL
SELECT
(
SELECT policy,
COUNT(*) AS count4
FROM Log_PE
WHERE CONVERT(DATE, dateEsc, 120) >= CONVERT(DATE, CONVERT(VARCHAR(6), DATEADD(month, -4, GETDATE()), 112) + '01', 112)
GROUP BY policy
ORDER BY count4 desc, policy
FOR XML PATH('prevMonth5'), ELEMENTS, TYPE
)
UNION ALL
SELECT
(
SELECT policy,
COUNT(*) AS count5
FROM log_PE
WHERE CONVERT(DATE, dateEsc, 120) >= CONVERT(DATE, CONVERT(VARCHAR(6), DATEADD(month, -5, GETDATE()), 112) + '01', 112)
GROUP BY policy
ORDER BY count5 desc, policy
FOR XML PATH('prevMonth5'), ELEMENTS, TYPE
)
) AS Data(XmlData)
FOR XML PATH(''), ELEMENTS, TYPE
END
Many thanks for any help with this, Tim.
Add this column to each of your nested selects:
rank() over(order by count(*) desc, policy) [Rank]

T-SQL select daily totals BUT daily cut off is not midnight

I need to count number of sales, and sum the totals of sales by date, easy. But the curve pitch is - I need the "cut off" to be 6pm not midnight.
6pm the day before until 6pm day of.
What's throwing me is "grouping". My counts are only pulling only the true date not "6pm" info.
Sort30 Day30 Total Counter
-------- ---------- --------------------- -----------
20120810 08/10/2012 675.32 9
20120809 08/09/2012 1314.68 16
Query:
SELECT top 30 CONVERT(VARCHAR(8), chickendate, 112) AS varSort30,
CONVERT(VARCHAR(10), chickendate, 101) AS varDay30,
SUM(CAST(transAmount AS money)) AS varTotal,
Count(chickendate) AS varCounter
FROM CHICKEN
WHERE
(chickendate >= dateadd(hour, 18, dateadd(day, datediff(day, 0, chickendate), -1)) AND
chickendate < dateadd(hour, 18, dateadd(day, datediff(day, 0, chickendate), 0)))
GROUP BY CONVERT(VARCHAR(8), chickendate, 112),
CONVERT(VARCHAR(10), chickendate, 101)
ORDER BY CONVERT(VARCHAR(8), chickendate, 112) DESC
Going round and round, I feel its something staring me in the face. Thanks.
If my logic is correct, this should give you the correct results:
SELECT TOP 30
CONVERT(VARCHAR(8), modifiedChickenDate, 112) AS varSort30,
CONVERT(VARCHAR(10), modifiedChickenDate, 101) AS varDay30,
SUM(CAST(transAmount AS money)) AS varTotal,
COUNT(modifiedChickenDate) AS varCounter
FROM (
SELECT
transAmount,
DATEADD(HOUR, 6, chickendate) AS modifiedChickenDate
FROM CHICKEN
) sub
GROUP BY
CONVERT(VARCHAR(8), modifiedChickenDate, 112) AS varSort30,
CONVERT(VARCHAR(10), modifiedChickenDate, 101) AS varDay30,
ORDER BY
CONVERT(VARCHAR(8), modifiedChickenDate, 112) AS varSort30
If chickendate is a DATETIME instead of just a DATE, then you can use
GROUP BY CONVERT(VARCHAR(10), DATEADD(hh, 6, chickendate), 101)
to advance the date 6 hours (making your cutoff 6 hours earlier than midnight, or 6pm), then group on the Day of year. This only works if you're storing time information, which I'm not sure you are. Post some schema for the necessary tables. But I think you're looking for...
SELECT TOP 30 CONVERT(VARCHAR(10), DATEADD(hh, 6, chickendate), 101) as Date
, SUM(CAST(transAmount as money)) AS Total
, Count(*) as Counter
FROM Chicken
GROUP BY CONVERT(VARCHAR(10), DATEADD(hh, 6, chickendate), 101)
ORDER BY CONVERT(VARCHAR(10), DATEADD(hh, 6, chickendate), 101) DESC
How about:
GROUP BY CONVERT(VARCHAR(8), dateadd(hour, 6, chickendate), 112)
So '2012-08-09 18:00:00' is grouped by 20120810, and '2012-08-09 17:59:59' is grouped by 20120809.

SQL Server correct way to account for the weekend

I have a few sql statements where I manually enter the date everyday then execute.
I would like to automate the date part. But weekends are giving me trouble. I use excel to run the reports as it is then saved and another member of staff does a mailshot.
This is my test example:
if (datename(dw, getdate()) = 'Friday') BEGIN
select convert(varchar, dateadd(day, 7, DATEADD(dd, 0, DATEDIFF(dd, 0,getdate()))), 103)
+ ',' + convert(varchar, dateadd(day, 9, DATEADD(dd, 0, DATEDIFF(dd, 0,getdate()))), 103) AS [Date] END
ELSE BEGIN select convert(varchar, dateadd(day, 7, DATEADD(dd, 0, DATEDIFF(dd, 0,getdate()))), 103) as [Date] END;
But how would I put this into a sql where statement?
for example
select * from table where (date = )
where Date equals 7 days in advance unless It's Friday when we have to account for the weekend so we want 7,8 and 9 days in advance?
select * from table where date = (case
when datename(dw, getdate()) = 'Friday' then
convert(varchar, dateadd(day, 7, DATEADD(dd, 0, DATEDIFF(dd, 0,getdate()))), 103)
+ ',' + convert(varchar, dateadd(day, 9, DATEADD(dd, 0, DATEDIFF(dd, 0,getdate()))), 103)
else
convert(varchar, dateadd(day, 7, DATEADD(dd, 0, DATEDIFF(dd, 0,getdate()))), 103)
end)