Get Sum of all Distinct in a week - sql

I am wondering where exactly i am not clear with this query. I want to get the count of all distinct RepIDs that worked in a particular week. This is In SQL Server 2005. Thank you!!
This query gives me distinct RepID's for the whole week. I want to count RepID twice if he has records on 2 different days but count only once even if he has more than 1 record for any partiular day.. I hope i am clear. I am sorry that i was not clear before! Thank you!
Select count(distinct(RepID)) as SalesPeople from DailyInfo
where Date > DATEADD(dd, -(DATEPART(dw, #Date)-1), #Date)
and Date < DATEADD(dd, 7-(DATEPART(dw, #Date)), #Date)

You can make unique combinations of the RepID+Date to make it unique (SQLFiddle):
SELECT COUNT(distinct RIGHT(DateDiff(d,0,Date),10)
+RIGHT(RepID,10)) as SalesPeople
FROM DailyInfo
WHERE Date > DATEADD(dd, -(DATEPART(dw, #Date)-1), #Date)
AND Date < DATEADD(dd, 7-(DATEPART(dw, #Date)), #Date);
I have assumed DailyInfo.Date can contain time information. You can swap DateDiff(d,0,Date) above for just Date. Similarly, CAST(DateDiff(d,0,Date) as datetime) below can be just `Date.
Below is the query if you needed to see the breakdown for each day.
SELECT CAST(DateDiff(d,0,Date) as datetime) TheDay,
COUNT(distinct RepID) as SalesPeople
FROM DailyInfo
WHERE Date > DATEADD(dd, -(DATEPART(dw, #Date)-1), #Date)
AND Date < DATEADD(dd, 7-(DATEPART(dw, #Date)), #Date)
GROUP BY CAST(DateDiff(d,0,Date) as datetime) -- by day
ORDER BY TheDay

Let me answer this by suggesting how you should think about the problem. You are looking for the number of reps per day. So, your query should have a summary (subquery) at this level. Then, you can count the number of days per week.
Assuming that your date does not have any time component, you can use the following:
select count(*)
from (select RepId, date as thedate, count(*) as NumOnDay
from DailyInfo
group by RepId, date
where Date > DATEADD(dd, -(DATEPART(dw, #Date)-1), #Date)
and Date < DATEADD(dd, 7-(DATEPART(dw, #Date)), #Date)
) rd
Alternatively, you could count the number of days that a rep worked during a week and then add these up:
select sum(numdates)
from (select RepId, count(distinct date) as numdates
from DailyInfo
group by RepId
where Date > DATEADD(dd, -(DATEPART(dw, #Date)-1), #Date)
and Date < DATEADD(dd, 7-(DATEPART(dw, #Date)), #Date)
) rd
If your date field has a time component, then you need to remove the time component for this to work. Or use some trick such as day(date), since the day function will returns a different value for each date in a week. In later versions of SQL Server, you can just cast(date as date), if the original date is datetime.

Select count(1)
from DailyInfo
group by convert(varchar(10),[date], 120)
where [put your condition here]

You could use a CTE, but might be over kill. And have the group by the day in the CTE and you need to do is a sum of the totals.
WITH cte ([day], total) as
(
Select DATENAME(DW,[Date]), count(distinct(RepID)) as SalesPeople from DailyInfo
where [Date] > DATEADD(dd, -(DATEPART(dw, #Date)-1), #Date) and [Date] < DATEADD(dd, 7-(DATEPART(dw, #Date)), #Date)
GROUP BY DATENAME(DW,[Date])
)
select SUM(total) FROM cte;

To do what I think you want you need to group by day, and filter on the week, and then do a distinct on the result:
Select Distinct(RepID)
From (Select RepID
Group By DateDiff(day, 0, Date)
From DailyInfo
Where Date > DateAdd(dd, -(DATEPART(dw, #Date)-1), #Date)
And Date < DateAdd(dd, 7-(DATEPART(dw, #Date)), #Date)

Related

Getting the week number as alias in sql analysis

this is my first question here. Hopefully I´m clear enough what I´m searching for.
My problem is following:
On this analysis I want to get from the last 7 weeks, the summarized prices of each week. Its working with out any problems, but now I would like to add the weeks number of each week as alias.
In my tests I was using for example something like this:
DECLARE #week7 varchar(10)
SET #week7 = DATEPART(wk, GetDate())
One of my problems is, that I´m not allowed to work with "EXEC".
This is just an example of my analysis:
SELECT DISTINCT(
SELECT SUM(Price)
FROM tblBookingdata
WHERE(Datum BETWEEN DATEADD(wk, -7, DATEADD(DAY, 1 - DATEPART(WEEKDAY, GETDATE()), DATEDIFF(dd, 0, GETDATE()))) AND DATEADD(wk, -6, DATEADD(DAY, 1 - DATEPART(WEEKDAY, GETDATE()), DATEDIFF(dd, 0, GETDATE()))))) AS '7 weeks ago', (
SELECT SUM(Price)
FROM tblBookingdata
WHERE(Datum BETWEEN DATEADD(wk, -6, DATEADD(DAY, 1 - DATEPART(WEEKDAY, GETDATE()), DATEDIFF(dd, 0, GETDATE()))) AND DATEADD(wk, -5, DATEADD(DAY, 1 - DATEPART(WEEKDAY, GETDATE()), DATEDIFF(dd, 0, GETDATE()))))) AS '6 weeks ago'
I would like the column name to show the week number from each sub select. That the output would be for example for this week: 40 (as column name) and 900 as price summary.
So I tried to work here with DECLARE and assign #week7 for example with the current week number. But here I got stuck, due it seems like I need to work here with EXEC.
Is this only possible with "EXEC" or are there any other solutions to solve this? I was looking in the www, but currently I´m stucking a bit. Thankful for every help! :)
I think the DateDiff function is your friend here. Are you using SQL Server? This won't display a row for the week if there are zero records in that week, but this should be close to what you want.
select WeeksAgo, sum(Price) as Price from (
select
Price
,Datediff(wk, Datum, getDate()) as WeeksAgo
,Datum --not used
from
tblBookingdata
)DataByWeek
where WeeksAgo between 0 and 7 --should this be 0-6?
group by WeeksAgo
I think you're looking for something like this. The prior 7 weeks are calculated from GETDATE based on a numbers table with 1, 2, 3, ... 7. Then the booking Prices are summarized by week where the Datum is within the prior 7 weeks. This will display NULL in price_sum if there were no sales that week.
drop table if exists #tblBookingdata;
go
create table #tblBookingdata(
Datum date not null,
Price int not null);
go
;with
weeks_cte(wk) as (
select datepart(wk, dateadd(wk, w*-1, getdate()))
from (values (1),(2),(3),(4),(5),(6),(7)) v(w)),
bookings_cte(wk, price_sum) as (
select datepart(wk, Datum), sum(Price)
from #tblBookingdata
where Datum>dateadd(wk, -7, getdate())
group by datepart(wk, Datum))
select *
from weeks_cte wc
left join bookings_cte b on wc.wk=b.wk;

Roll weekend counts into monday counts

I have a query like this:
select date, count(*)
from inflow
where date >= dateadd(year, -2, getdate())
group by date
order by date
I need to exclude Saturday and Sunday dates, and instead add their counts into the following Monday. What would be the best way to do this? Do I need to exclude Saturday, Sunday, and Mondays, then add them on with a join to a different query? The query above is a simplified version, this is a relatively big query, so I need to keep efficiency in mind.
Well, this is a somewhat brute-force approach:
select date,
(case when datename(weekday, date) = 'Monday')
then cnt + cnt1 + cnt2
else cnt
end) as cnt
from (select date, count(*) as cnt,
lag(count(*), 1, 0) over (order by date) as prev_cnt,
lag(count(*), 2, 0) over (order by date) as prev_cnt2
from inflow
where date >= dateadd(year, -2, getdate())
group by date
) d
where datename(weekday, date) not in ('Saturday', 'Sunday')
order by date;
Note: This is assuming English-language settings so the datename() logic works.
An alternative method without subqueries;
select v.dte, count(*) as cnt
from inflow i cross apply
(values (case when datename(weekday, i.date) = 'Saturday'
then dateadd(day, 2, i.date)
when datename(weekday, i.date) = 'Sunday'
then dateadd(day, 1, 9.date)
else i.date
end)
) v.dte
where i.date >= dateadd(year, -2, getdate())
group by v.dte
order by date;
You state for performance, however without knowing the full picture it's quite hard to understand how to optimise the query.
While I've been working on this, I noticed Gordon Linoff's answer, however I'll continue to write my version up as well, we both following the same path, but get to the answer a little different.
WITH DateData (date, datetoapply)
AS
(
SELECT
[date],
CASE DATEPART(w, [date])
WHEN 5 THEN DATEADD(d, 2, [date])
WHEN 6 THEN DATEADD(d, 1, [date])
ELSE date
END as 'datetoapply'
FROM inflow
WHERE [date] >= dateadd(year, -2, getdate())
)
SELECT datetoapply, count(*)
FROM DateData
GROUP BY datetoapply
ORDER BY datetoapply
While I could not get Gordon's query working as expected, I can confirm that "DATEPART(w, [date])" performs much better than "DATENAME(weekday, [date])", which if replaced in the query above increases the server processing time from 87ms to 181ms based on a table populated with 10k rows in Azure.

select all from table using date with time

how can I get the last recorded data of the time 23:59 from yesterday and the day before?
my code doesn't have a filter of the time yet so it only shows all the data from yesterday and the day before.
select *
from tbl_Total
where date between DATEADD(day, -3, GETDATE()) AND DATEADD(day, -1, GETDATE())
In your case,
select * from tbl_Total as of timestamp timestamp '2017-07-19 23:59:59'
and
select * from tbl_Total as of timestamp timestamp '2017-07-18 23:59:59'
Try this
select *
from tbl_Total
where date between dateadd(day,-3,convert(varchar(10),getdate(),112)) AND dateadd(day,-3,convert(varchar(10),getdate(),112)+ ' 23:59:59:997' )
This query will return yestarday date with time 23:59:59.
SELECT CAST(CAST(CAST(DATEADD(day, -1, GETDATE()) as DATE) as varchar(12)) +' 23:59:59' as datetime2)
So you can use it in your query:
select *
from tbl_Total
where date between DATEADD(day, -3, GETDATE()) AND CAST(CAST(CAST(DATEADD(day, -1, GETDATE()) as DATE) as varchar(12)) +' 23:59:59' as datetime2)
EDIT: More elegant way:
SELECT DATEADD(second, -1, DATEADD(dd, DATEDIFF(dd,0,GETDATE()),0))
This query returns yesterday date with time 23:59:59.
EDIT2: If you want to return the day before with time 23:59:59 you need to use this query:
SELECT DATEADD(second, -1, DATEADD(dd, DATEDIFF(dd,1,GETDATE()),0))
If you want to obtain any other day you can change number 2 and test it.
Assuming you don't know the exact time you can get the latest rows using ROW_NUMBER:
with cte as
( select *,
row_number() -- for each day sorted descending
over (partition by DATEADD(dd, DATEDIFF(dd,0,GETDATE()),0)
order by date desc) as rn
from tbl_Total
where -- yesterday between 23:59 and 23:59:99.999
( date >= DATEADD(dd, DATEDIFF(dd,0,GETDATE()),0) - (1.0/1440)
and date < DATEADD(dd, DATEDIFF(dd,0,GETDATE()),0)
)
or -- day before yesterday between 23:59 and 23:59:99.999
( date >= DATEADD(dd, DATEDIFF(dd,1,GETDATE()),0) - (1.0/1440)
and date < DATEADD(dd, DATEDIFF(dd,1,GETDATE()),0)
)
)
select * from cte
where rn = 1 --latest row only

How to select the past three months data from a date parameter

I would like to display the name of the month along with a count for each of these months. But I only want the past 3 months from a date parameter that the user will select.
This is what I have currently:
SELECT
DATENAME(month, DateDue),
COUNT(SiteAudit.SiteAuditID) AS SiteAuditID
FROM
SiteAudit
WHERE
DateDue >= Dateadd(month, -3, #Date)
GROUP BY
DATENAME(month, DateDue)
I'm not sure where I am going wrong because now I am getting more than 3 months returned.
If anyone could please help I would greatly appreciate it.
Thank you
You should use DateDue between DateAdd(month, -3, #Date) and #Date
Your original query will select data which due date after the #Date
Complete query:
SELECT
DATENAME(month, DateDue),
COUNT(SiteAudit.SiteAuditID) AS SiteAuditID
FROM
SiteAudit
WHERE
DateDue BETWEEN Dateadd(month, -3, #Date) AND #Date
GROUP BY
DATENAME(month, DateDue)

SQL Server / T-SQL: Selecting a specific interval ( group by )

I want to write a select that aggregates over data (which has a DATETIME column as ID) with ANY interval theoretically possible (like 1hr, 1hr and 22seconds, 1year and 3minutes, etc. ).
This select should be able to aggregate by 1hr, 12min, 14seconds and should return 3 rows
SELECT DATEPART(YEAR,id) as year,
DATEPART(MONTH,id) as month,
DATEPART(DAY,id) as day,
DATEPART(HOUR,id) as hour,
DATEPART(MINUTE,id) as minute,
AVG([Open]),
AVG([Close]),
AVG([Min]),
AVG([Max])
FROM QuoteHistory
where id between '2000-02-06 17:00:00.000' and '2000-02-06 20:36:42.000'
GROUP BY
DATEPART(YEAR,id),
DATEPART(MONTH,id),
DATEPART(DAY,id),
DATEPART(HOUR,id),
DATEPART(MINUTE,id)
ORDER BY 1,2,3,4,5;
I am kind of stuck here and can't get my head around this problem.. For "simple intervals" like "30 minutes" i could just add a modulo
DATEPART(MINUTE,id)%2
but when the interval "touches" more than 1 part of the date, I'm stuck.
Any help appreciated, thx!
Assuming some parameters here:
;WITH Date_Ranges AS (
SELECT
#min_datetime AS start_datetime,
DATEADD(SECOND, #seconds,
DATEADD(MINUTE, #minutes,
DATEADD(HOUR, #hours,
DATEADD(DAY, #days,
DATEADD(WEEK, #weeks,
DATEADD(MONTH, #months,
DATEADD(YEAR, #years, #min_datetime))))))) AS end_datetime
UNION ALL
SELECT
DATEADD(SECOND, 1, end_datetime),
DATEADD(SECOND, #seconds,
DATEADD(MINUTE, #minutes,
DATEADD(HOUR, #hours,
DATEADD(DAY, #days,
DATEADD(WEEK, #weeks,
DATEADD(MONTH, #months,
DATEADD(YEAR, #years, end_datetime)))))))
FROM
Date_Ranges
WHERE
DATEADD(SECOND, 1, end_datetime) < #max_datetime
)
SELECT
DR.min_datetime,
DR.max_datetime,
AVG([Open]),
AVG([Close]),
AVG([Min]),
AVG([Max])
FROM
Date_Ranges DR
LEFT OUTER JOIN Quote_History QH ON
QH.id BETWEEN DR.min_datetime AND DR.max_datetime
GROUP BY
DR.min_datetime,
DR.max_datetime
ORDER BY
DR.min_datetime,
DR.max_datetime
You might need to fiddle with how to handle the edge cases (that 1 second range between date ranges could be a problem depending on your data). This should hopefully point you in the right direction though.