How to query for unique pages views by date, IP - sql

Table
--------
Date CreatedOn
varchar(25) IP
From this table, I want to selected the number of unique IP addresses, by Day.
June 1, 2013 25
June 2, 2013 35
June 3, 2013 0
Note, the third item was added after original question to deal with days with no items. The data will be turned into a chart/graph.
What is the Sql Server 2008R2 SQL for this?
Greg

SELECT Date, COUNT(*)
FROM TableName
GROUP BY Date
ORDER BY date
if you want to have unique count, you need to have DISTINCT, eg. COUNT(DISTINCT IP).
follow-up question: is there a date that has no view? what are you going to do with it?
Since you want to show all dates between two date range according to your comment, you need to have a calendar table or something that generates all the dates between the date range.
-- let's say
-- start date = June 1, 2013
-- end date = June 5, 2013
WITH DateTimeSequence
AS
(
SELECT CAST('20130601' as datetime) AS [datetime] -- Start Date
UNION ALL
SELECT DATEADD(d, 1, [datetime])
FROM DateTimeSequence
WHERE DATEADD(d, 1, [datetime]) <= CAST('20130605' as datetime) -- End Date
)
SELECT a.[datetime],
COUNT(DISTINCT b.[ipaddress]) uniqueIP_Count
FROM DateTimeSequence a
LEFT JOIN TableName b
ON a.[datetime] = b.[date]
GROUP BY a.[datetime]
SQLFiddle Demo

SELECT CreatedOn,
COUNT(DISTINCT IP)
FROM Table
GROUP BY CreatedOn

Related

Get valid orders at the starting day of each year

In a table containing Order information (call it Order) we have the following fields:
OrderId int
OrderDate Date
BindingTime int
Binding time is in months.
An order is called "Active" between its OrderDate and DATEADD(mm, BindingTime, OrderDate).
What I'd like to do is to group the orders by year so that if an order is "active" on the first day of a year it would be taken into account. The aim is to calculate each year's inbound and outbound orders. So the query result will be COUNT of orders and the year. And by year we mean the number of orders which were active on the first day of that year.
Mind that, we would like to have all the years between two given numbers in our results. E.g. If there was no active order on the first day of 2016 we would still like to to have a row for (0, 2016).
I've used a recursive CTE to generate a range of years, so that a 'zero' year will not be omitted
declare #YEAR1 as date = '20110101';
declare #YEAR2 as date = '20190101';
WITH YEARS AS (SELECT #YEAR1 y
UNION ALL
SELECT dateadd(year,1,y) FROM YEARS WHERE y < #YEAR2)
SELECT YEARS.y,count(0) YearStartActiveOrders FROM YourTable
CROSS JOIN YEARS
WHERE YEARS.y BETWEEN CAST(orderdate as date)
AND CAST(DATEADD(mm, BindingTime, OrderDate) as date)
GROUP BY Years.y
Seems like what you need is a Date table (having a list of all days per year) and left joining that table with your grouped data (active order count, per day of the year).
You can use this date table from Aaron Bertrand. I generated the #dim table with the following params, to only generate two years data (2015, 2016):
DECLARE #StartDate DATE = '20150101', #NumberOfYears INT = 2;
Then you can do the following:
with ordertable as
(
select 1 as orderid, '20160101' as orderdate, 2 as bindingtime union all
select 2, '20160305', 3 union all
select 3, '20160305', 5 union all
select 4, '20150305', 5
)
select d.year, isnull(count(orderid), 0) nrActiveOrdersFirstDayOfYear
from #dim d
left join ordertable g on d.year = year(g.orderdate)
and g.orderdate = d.date
and d.FirstOfYear between g.orderdate and DATEADD(mm, g.bindingtime, OrderDate)
group by d.year
With the sample data I took as an example, you would get the result:
year nrActiveOrdersFirstDayOfYear
2015 0
2016 1
Working demo here.

To find from a table if there is any nearer date values for the same key

I have an sql table (t_accountdetails) with an account column called AccountId and effective date column for that account. An account can have multiple effective date. I have a requirement to get all the entries for the accounts which has very close effective date entries.(an offset of +/-14 days)
Say for eg:
AccountId: 12345 has got 2 entries with effective date 12/11/2017 and 12/18/2017
So my query should return above case where we have an entry of effective dates within offset of +/-14days
Please note I am actually not looking for date +-14 from today. I am looking for effective date which +/- 14 days of another effective date for the same account
You want all records where exists another effective date within 14 days, so use WHERE EXISTS:
select *
from t_accountdetails t
where exists
(
select *
from t_accountdetails other
where other.accountid = t.accountid
and other.id <> t.id
and abs(datediff(day, other.effective_date, t.effective_date)) <= 14
)
order by accountid, effective_date;
You can use the DATEADD function to make it work
select * from t_accountdetails where AccountId = 12345 and effectiveDate >= DATEADD(day, -14, getdate()) and effectiveDate <= DATEADD(day, 14, getdate())
This will return all records with AccountID = 12345 and an effective date between today - 14 days and today + 14 days.
Note: if more than one record match the criteria then all matching records will be returned.
I would be inclined to use lag() and lead():
select ad.*
from (select ad.*,
lag(effective_date) over (partition by accountid order by effective_date) as prev_ed,
lead(effective_date) over (partition by accountid order by effective_date) as next_ed
from t_accountdetails ad
) ad
where effective_date <= dateadd(day, 14, prev_ed) or
effective_date >= dateadd(day, -14, next_ed);
It would be interesting to compare the performance of this version to the exists version with an index on t_accountdetails(accountid, effective_date).

How To Select Records in a Status Between Timestamps? T-SQL

I have a T-SQL Quotes table and need to be able to count how many quotes were in an open status during past months.
The dates I have to work with are an 'Add_Date' timestamp and an 'Update_Date' timestamp. Once a quote is put into a 'Closed_Status' of '1' it can no longer be updated. Therefore, the 'Update_Date' effectively becomes the Closed_Status timestamp.
I'm stuck because I can't figure out how to select all open quotes that were open in a particular month.
Here's a few example records:
Quote_No Add_Date Update_Date Open_Status Closed_Status
001 01-01-2016 NULL 1 0
002 01-01-2016 3-1-2016 0 1
003 01-01-2016 4-1-2016 0 1
The desired result would be:
Year Month Open_Quote_Count
2016 01 3
2016 02 3
2016 03 2
2016 04 1
I've hit a mental wall on this one, I've tried to do some case when filtering but I just can't seem to figure this puzzle out. Ideally I wouldn't be hard-coding in dates because this spans years and I don't want to maintain this once written.
Thank you in advance for your help.
You are doing this by month. So, three options come to mind:
A list of all months using left join.
A recursive CTE.
A number table.
Let me show the last:
with n as (
select row_number() over (order by (select null)) - 1 as n
from master..spt_values
)
select format(dateadd(month, n.n, q.add_date), 'yyyy-MM') as yyyymm,
count(*) as Open_Quote_Count
from quotes q join
n
on (closed_status = 1 and dateadd(month, n.n, q.add_date) <= q.update_date) or
(closed_status = 0 and dateadd(month, n.n, q.add_date) <= getdate())
group by format(dateadd(month, n.n, q.add_date), 'yyyy-MM')
order by yyyymm;
This does assume that each month has at least one open record. That seems reasonable for this purpose.
You can use datepart to extract parts of a date, so something like:
select datepart(year, add_date) as 'year',
datepart(month, date_date) as 'month',
count(1)
from theTable
where open_status = 1
group by datepart(year, add_date), datepart(month, date_date)
Note: this counts for the starting month and primarily to show the use of datepart.
Updated as misunderstood the initial request.
Consider following test data:
DECLARE #test TABLE
(
Quote_No VARCHAR(3),
Add_Date DATE,
Update_Date DATE,
Open_Status INT,
Closed_Status INT
)
INSERT INTO #test (Quote_No, Add_Date, Update_Date, Open_Status, Closed_Status)
VALUES ('001', '20160101', NULL, 1, 0)
, ('002', '20160101', '20160301', 0, 1)
, ('003', '20160101', '20160401', 0, 1)
Here is a recursive solution, that doesn't rely on system tables BUT also performs poorer. As we are talking about months and year combinations, the number of recursions will not get overhand.
;WITH YearMonths AS
(
SELECT YEAR(MIN(Add_Date)) AS [Year]
, MONTH(MIN(Add_Date)) AS [Month]
, MIN(Add_Date) AS YMDate
FROM #test
UNION ALL
SELECT YEAR(DATEADD(MONTH,1,YMDate))
, MONTH(DATEADD(MONTH,1,YMDate))
, DATEADD(MONTH,1,YMDate)
FROM YearMonths
WHERE YMDate <= SYSDATETIME()
)
SELECT [Year]
, [Month]
, COUNT(*) AS Open_Quote_Count
FROM YearMonths ym
INNER JOIN #test t
ON (
[Year] * 100 + [Month] <= CAST(FORMAT(t.Update_Date, 'yyyyMM') AS INT)
AND t.Closed_Status = 1
)
OR (
[Year] * 100 + [Month] <= CAST(FORMAT(SYSDATETIME(), 'yyyyMM') AS INT)
AND t.Closed_Status = 0
)
GROUP BY [Year], [Month]
ORDER BY [Year], [Month]
Statement is longer, also more readable and lists all year/month combinations to date.
Take a look at Date and Time Data Types and Functions for SQL-Server 2008+
and Recursive Queries Using Common Table Expressions

Data appear at least once for every month in the last X month

My problem:
Table: trans_detail:
PhoneNo | Datetime
01234 | 2013-01-05 20:40:10
01245 | 2013-04-02 21:00:13
05678 | 2013-04-16 01:24:07
04567 | 2013-07-23 07:00:00
etc | etc
I want to get all phoneNo that appears at least once for every month in the last X month (X month can be any month between 1-12).
For example: get all phone no. that appears at least once for Every Month in the last 3 months.
I am using SQL Server 2005.
Here is a quick query that comes close to what you want:
select PhoneNo
from trans_detail d
where d.datetime >= dateadd(mm, -#X, getdate())
group by PhoneNo
having count(distinct year(datetime)*12+month(datetime)) = #X
The where clause filters the data to only include rows in the last #X months. the having clause checks that each month is in the data, by counting the number of distinct months.
The above version of the query assumes that you mean calendar months. So, it has boundary condition problems. If you run it on June 16th, then it looks back one month and makes sure that the phone number appears at least once since May 16th. I am unclear on whether you want to insist that the number appear twice (once in May and once in June) or if once (once during the time period). The solution to this is to move the current date back to the end of the previous month:
select PhoneNo
from trans_detail d cross join
(select cast(getdate() - day(getdate) + 1 as date) as FirstOfMonth const
where d.datetime >= dateadd(mm, -#X, FirstOfMonth) and
d.datetime < FirstOfMonth
group by PhoneNo
having count(distinct year(datetime)*12+month(datetime)) = #X
Here it is. First two CTEs are to find and prepare last X months, third CTE is to group your data by phones and months. At the end just join the two and return where number of matching rows are equal to number of months.
DECLARE #months INT
SET #Months = 3
;WITH CTE_Dates AS
(
SELECT GETDATE() AS Dt
UNION ALL
SELECT DATEADD(MM,-1,Dt) FROM CTE_Dates
WHERE DATEDIFF(MM, Dt,GETDATE()) < #months-1
)
, CTE_Months AS
(
SELECT MONTH(Dt) AS Mn, YEAR(Dt) AS Yr FROM CTE_Dates
)
, CTE_Trans AS
(
SELECT PhoneNo, MONTH([Datetime]) AS Mn, YEAR([Datetime]) AS Yr FROM dbo.trans_detail
GROUP BY PhoneNo, MONTH([Datetime]), YEAR([Datetime])
)
SELECT PhoneNo FROM CTE_Months m
LEFT JOIN CTE_Trans t ON m.Mn = t.Mn AND m.Yr = t.Yr
GROUP BY PhoneNo
HAVING COUNT(*) = #months
SQLFiddle Demo - with added some more data that will match for last 3 months

Having clause that filters out by max date

I'm trying to get the maximum date for an area_id from a 'review' table and return only those area_id's that have a max date_finished greater than one year (from today's date). The table structure is something like this
1) the 'review' table can have one or more reviews (multiple date_finished) for
each area_id
2) each region_id can have one or more area_ids
OBJECTIVE: Return the number of reviews per region_id with a date_finished greater than a year
Sample data:
region_id area_id date_finished
abc area_3 '01-01-2010 12:00:00 AM'
abc area_3 '06-01-2009 12:00:00 AM'
abc area_3 '02-01-2008 12:00:00 AM'
The expected result above should be return region abc and count should be 1 (because the max date_finished is january 1, 2010 and that is greater than a year from today's date (2013)
The sql I have is below. For some reason it returns all of the dates I have described above. Instead of returning the max date it returns 3 dates. I'm using SQL Server 2008. Thanks!
select count(*)
from review group by area_id
having datediff(day,max(date_finished), getdate()) > 365
I think you can get what you want through two levels of aggregation, first by region and area, the second by region:
select r.region_id, count(*) as NumAreas, SUM(cnt) as NumReviews
from (select r.region_id, r.area_id, count(*) as cnt,
MAX(date_finished)as maxDF
from review r
group by r.region_id, r.area_id
) r
where DATEDIFF(day, maxDF, getdate()) > 365
group by r.region_id
You can just use WHERE clause instead of HAVING
Your question isn't making a lot of sense to me.
You want a count of the maximum date that meets a certain criteria.
That logic resolves to a boolean.
Because a date that meets your criteria either exists or it doesn't.
;WITH CTE (region_id, area_id, date_finished) AS
(
SELECT 'abc', 'area_3', CAST('2010-1-1' AS DATETIME) UNION ALL
SELECT 'abc', 'area_3', '2009-3-1' UNION ALL
SELECT 'abc', 'area_3', '2008-6-1'
)
SELECT region_id
,HasQualifyingVal = CASE WHEN COUNT(*) > 0 THEN 1 ELSE 0 END
FROM CTE
WHERE date_finished < DATEADD(YEAR, 1, date_finished)
GROUP BY region_id