I can get a single count row from a specified date range like this:
SELECT table.[EVENT NAME], Count(*) AS [Count]
FROM table
WHERE [EVENT]='alphabetical' And table.DATE>=#11/20/2010# And (table.DATE)<=#11/26/2010#
GROUP BY table.[EVENT NAME];
but how could I add multiple rows with different date ranges?
[EVENT NAME],[DATE 11/20-11/26],[DATE 11/27-12/3], etc...
EDIT
the data would look something like this
event1;1/11/2010
event1;1/11/2010
event2;1/11/2010
event2;1/11/2010
event2;1/11/2010
event3;1/11/2010
event1;1/12/2010
event1;1/12/2010
event2;1/12/2010
event2;1/12/2010
event4;1/12/2010
event4;1/12/2010
etc.
and would like something like this (preferably with more columns) :
event1;2;2
event2;3;2
event3;1;0
event4;0;2
You'd use a group by clause and group by the date.
You didn't provide example records with expected results, that helps us help you :).
In other words post more information..
But from what I can tell you want a count based on a date range.
So if you had 1/1/2010 with 10 rows
and 1/2/2010 with 20 referenced rows
and 1/3/2010 with 6 reference rows...you'd want output like this:
1/1/2010 10
1/2/2010 20
1/3/2010 6
So SELECT COUNT(*), MyDate FROM MyTable GROUP BY MyDate
To answer your question about a date range, think of how group by works, it works by grouping a set of data by combining all sets that match a criteria. So when you say group by date it groups by a single date. You want a date range, so each row should know about or understand a range (Start to End). So you need to include these columns in each of your rows by generating them via SQL.
Edit
For instance
SELECT Count(*), DATEADD(day, -10, GetDate()) AS StartDate, DATEADD(day, 10, GetDate()) AS EndDate
FROM MyTable GROUP BY StartDate, EndDate
Access has similiar functions to add days to dates so look that up for MS Access. Then just generate a start and end date for each column.
Related
I am using Microsoft SQL Server Management Studio. I am trying to measure the customer retention rate of an eCommerce site.
For this, I need four values:
customer_id
order_purchase_timestamp
age_by_month
first_purchase
The values of age_by_month and first_purchase are not in my database. I want to calculate them.
In my database, I have customer_id and order_purchase_timestamp.
The first_purchase should be the earliest instance of order_purchase_timestamp. I only want the month and year.
The age_by_month should be the difference of months from first_purchase to order_purchase_timestamp.
I only want to measure the retention of the customer for each month so if two purchases are made in the same month it shouldn't be shown.
the dates are between 2016-10-01 to 2018-09-30. it should be ordered by order_purchase_timestamp
An example
customer_id
order_purchase_timestamp
1
2016-09-04
2
2016-09-05
3
2016-09-05
3
2016-09-15
1
2016-10-04
to
customer_id
first_purchase
age_by_month
order_purchase_timestamp
1
2016-09
0
2016-09-04
2
2016-09
0
2016-09-05
3
2016-09
0
2016-09-05
1
2016-09
1
2016-10-04
What I have done
SELECT
customer_id, order_purchase_timestamp
FROM
orders
WHERE
(order_purchase_timestamp BETWEEN '2016-10-01' AND '2016-12-31')
OR (order_purchase_timestamp BETWEEN '2017-01-01' AND '2017-03-31')
OR (order_purchase_timestamp BETWEEN '2017-04-01' AND '2017-06-30')
OR (order_purchase_timestamp BETWEEN '2017-07-01' AND '2017-09-30')
OR (order_purchase_timestamp BETWEEN '2017-10-01' AND '2017-12-31')
OR (order_purchase_timestamp BETWEEN '2018-01-01' AND '2018-03-31')
OR (order_purchase_timestamp BETWEEN '2018-04-01' AND '2018-06-30')
OR (order_purchase_timestamp BETWEEN '2018-07-01' AND '2018-09-30')
ORDER BY
order_purchase_timestamp
Originally I was going to do it by quarters but I want to do it in months now.
The following approach is designed to be relatively easy to understand. There are other ways (e.g., windowed functions) that may be marginally more efficient; but this makes it easy to maintain at your current SQL skill level.
Note that the SQL commands below build on one another (so the answer is at the end). To follow along, here is a db<>fiddle with the working.
It's based around a simple query (which we'll use as a sub-query) that finds the first order_purchase_timestamp for each customer.
SELECT customer_id, MIN(order_purchase_timestamp) AS first_purchase_date
FROM orders
GROUP BY customer_id
The next thing is DATEDIFF to find the difference between 2 dates.
Then, you can use the above as a subquery to get the first date onto each row - then find the date difference e.g.,
SELECT orders.customer_id,
orders.order_purchase_timestamp,
first_purchases.first_purchase_date,
DATEDIFF(month, first_purchases.first_purchase_date, orders.order_purchase_timestamp) AS age_by_month
FROM orders
INNER JOIN
(SELECT customer_id, MIN(order_purchase_timestamp) AS first_purchase_date
FROM orders
GROUP BY customer_id
) AS first_purchases ON orders.customer_id = first_purchases.customer_id
Note - DATEDIFF has a 'gotcha' that gets most people but is good for you - when comparing months, it ignores the day component e.g., if finding the difference in months, there is 0 difference in months between 1 Jan and 31 Jan. On the other hand, there will be a difference on 1 month between 31 Jan and 1 Feb. However, I think this is actually what you want!
The above, however, repeats when a customer has multiple purchases within the month (it has one row per purchase). Instead, we can GROUP BY to group by the month it's in, then only take the first purchase for that month.
A 'direct' approach to this would be to group on YEAR(orders.order_purchase_timestamp) AND MONTH(orders.order_purchase_timestamp). However, I use a little trick below - using EOMONTH which finds the last day of the month. EOMONTH returns the same date for any date in that month; therefore, we can group by that.
Finally, you can add the WHERE expression and ORDER BY to get the results you asked for (between the two dates)
SELECT orders.customer_id,
MIN(orders.order_purchase_timestamp) AS order_purchase_timestamp,
first_purchases.first_purchase_date,
DATEDIFF(month, first_purchases.first_purchase_date, EOMONTH(orders.order_purchase_timestamp)) AS age_by_month
FROM orders
INNER JOIN
(SELECT customer_id, MIN(order_purchase_timestamp) AS first_purchase_date
FROM orders AS orders_ref
GROUP BY customer_id
) AS first_purchases ON orders.customer_id = first_purchases.customer_id
WHERE orders.order_purchase_timestamp BETWEEN '20161001' AND '20180930'
GROUP BY orders.customer_id, first_purchases.first_purchase_date, EOMONTH(orders.order_purchase_timestamp)
ORDER BY order_purchase_timestamp;
Results - note they are different from yours because you wanted the earliest date to be 1/10/2016.
customer_id order_purchase_timestamp first_purchase_date age_by_month
1 2016-10-04 00:00:00.000 2016-09-04 00:00:00.000 1
Edit: Because someone else will do it like this otherwise!
You can do this with a single read-through that will potentially run a little faster. It is also a bit shorter - but harder to understand imo.
The below uses windows functions to calculate both the customer's earliest purchase, and the earliest purchase for each month (and uses DISTINCT rather than a GROUP BY). With that, it just does the DATEDIFF to calculate the difference.
WITH monthly_orders AS
(SELECT DISTINCT orders.customer_id,
MIN(orders.order_purchase_timestamp) OVER (PARTITION BY orders.customer_id, EOMONTH(orders.order_purchase_timestamp)) AS order_purchase_timestamp,
MIN(orders.order_purchase_timestamp) OVER (PARTITION BY orders.customer_id) AS first_purchase_date
FROM orders)
SELECT *, DATEDIFF(month, first_purchase_date, order_purchase_timestamp) AS age_by_month
FROM monthly_orders
WHERE order_purchase_timestamp BETWEEN '20161001' AND '20180930';
Note however this has one difference in the results. If you have 2 orders in a month, and your lowest date filter is between the to (e.g., orders on 15/10 and 20/10, and your minimum date is 16/10) then the row won't be included as the earliest purchase in the month is outside the filter range.
Also beware with both of these and what type of date or datetime field you are using - if you have datetimes rather than just dates, BETWEEN '20161001' AND '20180930' is not the same as >= '20161001' AND < '20181001'
Here is short query that achieves all you want (descriptions of methods used are inline):
declare #test table (
customer_id int,
order_purchase_timestamp date
)
-- some test data
insert into #test values
(1, '2016-09-04'),
(2, '2016-09-05'),
(3, '2016-09-05'),
(3, '2016-09-15'),
(1, '2016-10-04');
select
customer_id,
-- takes care of correct display of first_purchase
format(first_purchase, 'yyyy-MM') first_purchase,
-- used to get the difference in months
datediff(m, first_purchase, order_purchase_timestamp) age_by_month,
order_purchase_timestamp
from (
select
*,
-- window function used to find min value for given column within group
-- for each row
min(order_purchase_timestamp) over (partition by customer_id) first_purchase
from #test
) a
Trying to use the :datebucket filter but it doesn't seem to work.
select date, address from database.table where address = 'xyz' group by :datebucket(date)
This returns the error that date isn't in the group by statement, but it is. If it add it separately to the group by statement, it just groups by the individual date instead of respecting the date bucket selection.
Not finding anything in the Snowflake documentation about how this filter is suppose to work, just that it exists.
In this site: https://www.webagesolutions.com/blog/querying-data-in-snowflake was example like this about databucket function
SELECT COUNT(ORDER_DATE) as COUNT_ORDER_DATE, ORDER_DATE
FROM ORDERS
GROUP BY :datebucket(ORDER_DATE), ORDER_DATE
ORDER BY COUNT_ORDER_DATE DESC;
So could your query work if it was modified like this:
SELECT
date,
address
FROM
database.table
WHERE
address = 'xyz'
GROUP BY :datebucket(date), date
Datebucket is truncating the date, to buckets. But you have selected the raw date.
This is like grouping by decade '60,'70,'80 of what great years, but want the actual year.
SELECT column1 as year,
truncate(year,-1) as decade
FROM VALUES (1),(2),(3),(14),(15),(16),(27),(28),(29);
gives:
YEAR
DECADE
1
0
2
0
3
0
14
10
15
10
16
10
27
20
28
20
29
20
so if I try select
SELECT column1 as year
FROM VALUES (1),(2),(3),(14),(15),(16),(27),(28),(29)
GROUP BY truncate(year,-1)
ORDER BY 1;
gives the error
Error: 'VALUES.COLUMN1' in select clause is neither an aggregate nor in the group by clause. (line 15)
So if we move the decade into the selection, it makes sense:
SELECT truncate(column1,-1) as decade
FROM VALUES (1),(2),(3),(14),(15),(16),(27),(28),(29)
GROUP BY decade
ORDER BY 1;
and we get the
DECADE
0
10
20
So the problem is not :datebucket(date) but the fact while :datebucket(date) and date are related, from the perspective of GROUPING they are unrelated.
I've been trying to use datebucket(date) and daterange, and I also needed the results in a Snowflake graph.
It was a bit trick, because the value returned by datebucket(date) is actually a truncated date based on the selected date part. For that, I had to convert it to a char, and it worked!
select
to_char(:datebucket(start_time), 'YYYY.MM.DD # HH24') as start_time_bucket,
sum(credits_used) as credits_used
from snowflake.account_usage.warehouse_metering_history wmh
where
start_time = :daterange
group by :datebucket(start_time)
And if you're an ACCOUNTADMIN, you can now use the query to get the total credits usage by date :)
Last, to answer the main query by Tony, the query should be:
select date, address
from database.table
where address = 'xyz'
group by :datebucket(date), date, address
// or
select :datebucket(date), address
from database.table
where address = 'xyz'
group by :datebucket(date), address
Try adding the :datebucket(date) in the select part as well (not only in group by). Also, you will probably need an aggregate function for the field address (for example any_value(address):
select :datebucket(date), any_value(address)
from database.table
where address = 'xyz'
group by :datebucket(date)
I have a Database table that has all the information I need arranged like so:
Inventory_ID | Dealer_ID | LastModifiedDate
Each Dealer_ID is attached to multiple Inventory_ID's. What I need is a query that calculates the Max Value LastModifiedDate for each dealer ID and then gives me a list of all the Dealer_ID's that have a last modified date beyond the last 30 days.
Getting The max last modified date for each Dealer_ID is simple, of course:
Select Dealer_ID, Max(LastModifiedDate)as MostRecentUpdate
from Inventory group by Dealer_ID order by MAX(LastModifiedDate)
The condition for records older than 30 day is also fairly simple:
LastModifiedDate < getdate() - 30
Somehow, I just can't figure out a way to combine the two that works properly.
Use HAVING:
Select Dealer_ID, Max(LastModifiedDate)as MostRecentUpdate
from Inventory
group by Dealer_ID
having LastModifiedDate < getdate() - 30
order by MAX(LastModifiedDate)
Check this query:
Select DT.DealerID, DT.MostRecentUpdate
(Select DealerID, Max(LastModifiedDate)as MostRecentUpdate
From YourTable
Group BY DealerID) DT
where DT.MostRecentUpdate < GETDATE() - 30
Very new to SOQL. Looking to write a query (to be used in Apex Data Loader) that pulls records with a CreatedDate <= 14 days ago. None of the predefined date options (LAST_N_DAYS, etc.) seem to cover what I'm looking for. I'm guessing/hoping there is something similar to DATEADD(D, -14, DATE()) that can dynamically calculate 14 days ago, so that the criteria would ultimately look like CreatedDate <= DATEADD(D, -14, DATE()).
Thanks in advance!
There isn't any Date Add. However looking at your criteria the LAST_N_DAYS builtin should do the trick for you
Lets say I want to select Data that is older than 14 days ago (including that day) I would do
Select Id, CreatedDate from Account where CreatedDate <= LAST_N_DAYS:14
if I need the opposite ie data created in the last 14 days
Select Id, CreatedDate from Account where CreatedDate >= LAST_N_DAYS:14
Example Table:
(Column Heading) Total Interviews of everyone --- (Row Heading) Interview Requested
(Column Heading) Number of interviews this week -- (Row Value) Count all the dates scheduled this week that have the status: "Interview Requested" e.g 4
(Column Heading) Number of interviews next week -- (Row Value) Count all the dates scheduled next week that have the status: "Interview Requested" e.g 6
First of all i'll like to aploigise about my makeshift table, it seems that i cant use pictures since im a new member.
I am also new to using reportbuilder and sql and have been trying to figure out how i can count the amount of dates that fall between certain date ranges in a report.
Like ive stated in the makeshift field called "Number of interviews this week" I wish to count all the dates scheduled for the current week, the week after, the week before and so on based on a date field. I then plan to break this table down to show all the interviews a single person has schedueled.
The issue I am having is that there will be many dates spanning different months/days. So Im not sure how i can represent this with DateDiff or other date features, because the table is meant to be a live representation of the current interviews.
Is what i'm trying to do even possible using report builder?? If so any tips would be great, if not then thanks for taking the time to look at my question.
As Requested Dataset Fields:
[UserName] (User table), [InterviewStatus] (Interview table), [DateUpdated] (Interview Table)
Those are the main ones that will be used in the report
If you just want to count the number of columns of each field, you can use the sql statement COUNT() see COUNT().
You can do the common query like this:
SELECT COUNT(fieldName1), COUNT(fieldName2), ... , COUNT(fieldNamen) FROM <tableName>
Also try to show your codes so that your question will be more clarified.
You can use the generic select statement from your interview table filtering in the where clause for dates between the beginning of the current week and the end of the next.
Then the two fields would be a sum of 1 if the date falls within the current week or next.
EDIT:
Example below will give the number of dates within this week. Taken from another question:
Select InterviewStatus,
Sum(Case when DateUpdated >= DATEADD(dd, -(DATEPART(dw, GETDATE()) -1), GETDATE())
AND DateUpdated < CAST(CONVERT(VARCHAR(10), DATEADD(dd, (8 - DATEPART(dw, GETDATE())), GETDATE()), 120) AS DATETIME)
Then 1
Else 0 end)
as NumberInCurrentWeek
From InterviewTable
Where DateUpdated >= DATEADD(dd, -(DATEPART(dw, GETDATE()) -1), GETDATE())
AND DateUpdated < CAST(CONVERT(VARCHAR(10), DATEADD(dd, (8 - DATEPART(dw, GETDATE())), GETDATE()), 120) AS DATETIME)
Group by InterviewStatus
This just needs to be tweaked to your exact circumstance. For example alter the 8 by 7 will capture the current fortnight.
Then produce a simple table based on the query.