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

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.

Related

T-SQL date pickup

I am looking for some T-SQL code that should pick the date which is "One Year back from current date (at the same time last Sunday in the month of January)".
I have some T-SQL code which is being used in SQL Server 2014:
select
convert(varchar(10), DATEADD(day, DATEDIFF(day, '19000107', DATEADD(month, DATEDIFF(MONTH, 0, CONVERT(date, CONVERT(VARCHAR(4), (CASE WHEN MONTH(GetDate()) = 1 THEN CONVERT(VARCHAR(4), GetDate(), 112) - 1 ELSE CONVERT(VARCHAR(4), GetDate(), 112) END), 112) + '0101')), 30)) / 7 * 7, '19000107'), 120)
The above code picks the date for current year's (last Sunday in January month). But I want T-SQL code to pick last year's (last Sunday's date in January month) date.
In detail - I want T-SQL code to produce expected result from below table
Current day Expected result
---------------------------------------
2017-02-05 2016-01-31
2017-01-05 2015-01-25
2018-02-19 2017-01-29
2018-01-19 2016-01-31
2019-02-28 2018-01-28
Please note always year starts from "Last Sunday in January month".
There will be more concise solutions, but when we assume that your code is time-tested and robust, I would simply substitute the GETDATE() by an expression that is now minus one year:
DATEADD(year, -1, GETDATE())
Thus:
SELECT
convert(varchar(10), DATEADD(day, DATEDIFF(day, '19000107', DATEADD(month, DATEDIFF(MONTH, 0, CONVERT(date, CONVERT(VARCHAR(4), (CASE WHEN MONTH(DATEADD(year,-1,GetDate())) = 1 THEN CONVERT(VARCHAR(4), DATEADD(year,-1,GetDate()), 112) - 1 ELSE CONVERT(VARCHAR(4), DATEADD(year,-1,GetDate()), 112) END), 112) + '0101')), 30)) / 7 * 7, '19000107'), 120)
If you have a calendar table, you can skip the first CTE (and probably the MAXRECURSION option) and just use the calendar table. Hopefully this is clearly correct:
declare #today date
set #today = CURRENT_TIMESTAMP
;With Dates as (
select CONVERT(date,'19000101') as d
union all
select DATEADD(day,1,d) from Dates where d < '21000101'
), ApplicableSundays as (
select d,ROW_NUMBER() OVER (ORDER BY d desc) as rn
from Dates
where d < #today and
DATEPART(month,d) = 1 and
DATEPART(weekday,d) = DATEPART(weekday,'20150913') and --Any known Sunday
DATEPART(day,d) between 25 and 31
)
select d
from ApplicableSundays where rn = 2
option (maxrecursion 0)
Dates generates all dates in the 20th and 21st centuries, which is hopefully flexible enough for your purposes.
ApplicableSundays filters these rows down to dates which occur before #today, are in January, are a Sunday (using a known-good date rather than relying on any particular DATEFIRST setting) and falls between the 25th and the 31st of that month.
We then pick the second most recent of these dates, which must be the start of last year, if our years start on the last Sunday of each January.
If you're working against a table full of dates for which you wish to find this "start of last year" value, you would introduce it as a join in the ApplicableDates CTE and partition the ROW_NUMBER() aggregate using those values, so that you can find all of the Sundays in parallel.
This will seclude the columns with the data .
Please give it a try let me know. Thanks.
select
convert(varchar(10), DATEADD(day, DATEDIFF(day, '19000107', DATEADD(month, DATEDIFF(MONTH, 0, CONVERT(date, CONVERT(VARCHAR(4), (CASE WHEN MONTH(GetDate()) = 1 THEN CONVERT(VARCHAR(4), GetDate(), 112) - 1 ELSE CONVERT(VARCHAR(4),
GetDate(), 112) END), 112) + '0101')), 30)) / 7 * 7, '19000107'), 120) AS CurrentDay , convert(varchar(10), DATEADD(day, DATEDIFF(day, '19000107', DATEADD(month, DATEDIFF(MONTH, 0, CONVERT(date, CONVERT(VARCHAR(4), (CASE WHEN MONTH(DATEADD(year,-1,GetDate())) = 1 THEN CONVERT(VARCHAR(4), DATEADD(year,-1,GetDate()), 112) - 1 ELSE CONVERT(VARCHAR(4),
DATEADD(year,-1,GetDate()), 112) END), 112) + '0101')), 30)) / 7 * 7, '19000107'), 120) as ExpectedResult

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: better / faster way of combining multiple counts in select

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

how can I get last 7 days of data being my date field a number?

I need to get last 7 days data excluding Sunday being my date field a number. How can I do it? Field structure 20140425. For example is I run the statement today it should give me date range between 20140424 - 20140417 excluding 20140420.
The hitch is of course converting the number based date to a real date. This seems to work:
select convert(datetime, convert(char(10), 20140425))
To expand, your query would look like this:
select *
from [Table]
where convert(datetime, convert(char(10), [columnname])) between convert(varchar, getdate() - 8, 101) and convert(varchar, getdate() - 1, 101)
and datepart(DW, convert(datetime, convert(char(10), [columnname]))) <> 1
The convert(varchar, getdate - 1, 101) will return you 12:00am yesterday morning. My first pass didn't include that and would've only given a 6 day range.
SELECT *
FROM Table_Name
WHERE CAST(DateField AS DATE) >= DATEADD(DAY, -7, GETDATE())
AND CAST(DateField AS DATE) <= GETDATE()
AND DATEPART(DW,CAST(DateField AS DATE)) <> 1

nested select statement with SUM

I would like to know if its possible to write a nested select statment?
I have the following which calculates the booked time:
SELECT description, SUM(ts.booked_time) AS booked_time_total,
CONVERT(VARCHAR(11), #testDate, 106) AS month_name, #week_ref AS week_ref
FROM timesheets ts
WHERE #testDate <= convert(datetime, end_dtm, 120) and
dateadd(wk, 1, #testDate) > convert(datetime, start_dtm, 120)
But the booked time appears to be wrong. Isnt the SUM supposed to calculate the total for each row which are within the start_dtm and end_dtm. So if I have 10 rows with 1 in the booked time you would expect the SUM to be 10.
test data:
SUM calculates the total value of the fields while COUNT is the total number of records.
SELECT description,
COUNT(ts.booked_time) AS booked_time_total,
CONVERT(VARCHAR(11), #testDate, 106) AS month_name,
#week_ref AS week_ref
FROM timesheets ts
WHERE #testDate <= convert(datetime, end_dtm, 120) and
dateadd(wk, 1, #testDate) > convert(datetime, start_dtm, 120)
I think you are looking to use COUNT rather than SUM.