select repeat visitors given two time periods - sql

I would like to find the percent of repeat visitors to my site. Currently I am selecting users for one month and dividing that month by the following month. to calculate those that have returned. Is this the best way to write that query?
it is yielding results that seem to be accurate, but wondering if there is a more elegant solution.
SELECT (
SELECT COUNT(table.user) as Total
FROM table
WHERE table.event IN ('event1','event2','event3')
AND table.month IN ('october')
) /
(
SELECT COUNT(table.user) as newTotal
FROM table
WHERE table.event IN ('event1','event2','event3')
AND (table.month IN ('october') OR table.month IN ('november'))
) AS percent_return
table structure looks like this, you have the same user purchasing multiple events for the same month or the same time period.
order_number user month event gross
1 jack october event2 30
2 jack november event3 20
3 jack november event3 20
4 jack november event2 30
5 sam november event2 30
6 john october event3 20
7 john october non_event 20

You don't need two subqueries to do what you want. Just use conditional aggregation:
SELECT (SUM(s.month IN (october)) / SUM(s.month IN (october, november))
) as percent_return
FROM sales s
WHERE s.event IN ('event1', 'event2', 'event3') AND
s.gross > 0;
Also, there is no need for single quotes around numeric constants.

Related

SQL Server window function for date diffs

Consider I have this table:
// Orders
OrderId Customer OrderDate
------------------------------
1 Jack 2018/05/01
2 Jack 2018/05/05
3 Jack 2018/05/15
4 Jack 2018/05/18
5 Jack 2018/05/21
6 Alex 2018/06/11
7 Alex 2018/06/12
8 Alex 2018/06/17
9 Alex 2018/06/18
I want to have a query to show the gap days between their orders in a single column like this:
Customer Gaps GapAverage
---------------------------------------
Jack 4, 10, 3, 3 5
Alex 1, 5, 3 3
So for Jack, his 2nd order was 4 days after his 1st order, his 3rd was 10 days after his 2nd order and ...
And his average gaps is 5 days.
How can I write a query to achieve such result in SQL server?
I got such query
select q1.Customer,
STRING_AGG(q1.diff, ',') as Gaps,
AVG(diff) as GapAverage
from ( select Customer as Customer,
DATEDIFF(dd, [OrderDate], LEAD([OrderDate]) OVER (PARTITION BY Customer ORDER BY Customer)) as diff
from OrderT) as q1
group by q1.Customer
I am using a LEAD function for the access to the row that follows the current row. Using the OVER argument of this function I am dividing the result into groups by the Customer field. Using the function DATEDIFF I get the gap in days.

how to calculate sum value from group by

I have a table like
person type date
Tom day 4/1/2018
Tom night 3/2/2018
Tom rest 4/3/2018
Jack day 4/1/2018
Jack day 4/2/2018
Jack night 4/3/2018
Peter day 4/1/2018
Peter day 3/2/2018
Peter day 4/3/2018
I want to count each one's working hours in April. Day shift is 8 hours and night is 11 hours. so the result is like
Person hours
Tom 8
Jack 27
Peter 16
I tried a SQL like
select person,count(Type),
case type when 'day' then count(type)*8 when 'night' then count(type)*11
from table where date>'3/30/2018'
group by person,type
and it work
then I try to treat it like a table and add a group by outside like
select *
from (select person,count(Type),
case type when 'day' then count(type)*8 when 'night' then count(type)*11
from table where date>'3/30/2018' group by person,type)
and it doesn't work. Why? any help apppreciated.
You could use the following case-when to convert the type to the corrisponding number of hours:
case
when type='day' then 8
when type='night' then 11
end
then you can just sum the corresponding number of hours:
select
person,
sum(
case
when type='day' then 8
when type='night' then 11
end
) as hours
from
table_name
where
date>='4/01/2018' and date<'5/01/2018'
group by
person
try this:
SELECT person,
SUM(Case WHEN type='day' THEN 8
WHEN type='night' THEN 11
ELSE 0 END)
FROM Table1
WHERE [date]>'2018/03/30'
Group by person
SQL Fiddle: http://sqlfiddle.com/#!18/5b946/1
The key to get your result is to use case statement to "convert" type to appropriate amount of hours. Try this:
select person, SUM([hrs]), [date] from (
select person,
case [TYPE_ID] when 'day' then 8
when 'night' then 11
else 0 end [hrs],
[date]
from MY_TABLE
where date between '4/01/2018' and '5/01/2018'
) [a] group by person

T-SQL Running Monthly Totals Including Missing Months

I know this sounds really simple but I just cannot seem to get my head around it.
I have a temporary table that holds for example, Handler, MonthName, MonthNumber and MTD, which is a total for that month. What I need to do with that data is then create a running total for each Handler, from April to March. Now, here is the bit I am struggling with. Not all Handlers will have data for all months.
For example.
Handler MonthName MonthNo MTD
Julian Slaughter April 1 10000
Julian Slaughter June 3 12000
Julian Slaughter July 4 10000
Julian Slaughter September 6 12000
Bob Monkhouse April 1 5000
Bob Monkhouse July 4 5000
So I want the results to look like this
Julian Slaughter April 1 10000
Julian Slaughter May 2 10000
Julian Slaughter June 3 22000
Julian Slaughter July 4 32000
Julian Slaughter August 5 32000
Julian Slaughter September 6 44000
...and so on until March
Bob Monkhouse April 1 5000
Bob Monkhouse May 2 5000
Bob Monkhouse June 3 5000
Bob Monkhouse July 4 10000
...and so on until March
I have tried LEFT JOIN onto a table of the Month Names\Numbers and I have had an attempt at
OVER(PARTITION ..... ORDER BY ..... RANGE\ROWS)
but can't get the missing months.
Thanks in advance, sorry for the poor formatting, not sure how to do tables on here.
EDIT - Here is my LEFT JOIN attempt
SELECT
Months.MonthNo,
Department,
Executive,
#8.MonthNo,
MTD = SUM([TY MTD Prem]) OVER (PARTITION BY Department, Executive, [Exec Code] ORDER BY #8.MonthNo RANGE UNBOUNDED PRECEDING)
FROM Months
LEFT JOIN #8 ON Months.MonthNo = #8.MonthNo
For one Executive, I only get 4 rows, not the 12 I need. Can't show you the results for Data Protection purposes.
DECLARE #start_date date, #end_date date
SELECT #start_date='2012-04-01',#end_date='2013-03-31'
;WITH xo AS
(
SELECT #start_date AS cte_start_date
UNION ALL
SELECT DATEADD(MONTH, 1, cte_start_date)
FROM xo
WHERE DATEADD(MONTH, 1, cte_start_date) <= #end_date
), x as (
select *,row_number() over (order by cte_start_date) monthno
from xo
)
, y as (
select distinct handler from test
)
SELECT y.handler, datename(mm,x.cte_start_date), x.monthno
,(select sum(mtd) from test a where a.handler=y.handler and a.monthno<=x.monthno) mtd
FROM y
cross join x
order by 1,3
see example on SQLFiddle http://sqlfiddle.com/#!3/7d483/15
Sorry for the delay. The proposed solution worked a treat. I had to use the same code several times in various other parts of my giant query but it worked great.

Using sum function with a condition based on a returned value

I have a set of given month with a number of hours related to each of it
DATE HOURS
8/1/2013 3
9/1/2013 8
10/1/2013 2
11/1/2013 4
12/1/2013 1
I need to return the sum of hours for everything that is in the past including current month, in the example below, starting in august, sum would be august only. For september, I'd need august + september
DATE HOURS SUM
8/1/2013 3 3
9/1/2013 8 11
10/1/2013 2 13
11/1/2013 4 17
12/1/2013 1 18
I am not sure how to proceed, since the date condition is different for each line.
If anyone can help on this, it'd be greatly appreciated
You can do this in most SQL dialects using a correlated subquery (or a non-equijoin, but I find the subquery cleaner):
select date, hours,
(select sum(t2.hours)
from t t2
where t2.date <= t.date
) as cum
from t;
Many SQL engines also support the cumulative sum function, which would typically look like this:
select date, hours sum(hours) over (order by date) as cum
from t

SQL - Grouping results by custom 24 hour period

I need to create an Oracle 11g SQL report showing daily productivity: how many units were shipped during a 24 hour period. Each period starts at 6am and finishes at 5:59am the next day.
How could I group the results in such a way as to display this 24 hour period? I've tried grouping by day, but, a day is 00:00 - 23:59 and so the results are inaccurate.
The results will cover the past 2 months.
Many thanks.
group by trunc(your_date - 1/4)
Days are whole numbers in oracle so 6 am will be 0.25 of a day
so :
select
trunc(date + 0.25) as period, count(*) as number
from table
group by trunc(date + 0.25 )
I havent got an oracle to try it on at the moment.
Well, you could group by a calculated date.
So, add 6 hours to the dates and group by that which would then technically group your dates correctly and produce the correct results.
Assuming that you have a units column or similar on your table, perhaps something like this:
SQL Fiddle
SELECT
TRUNC(us.shipping_datetime - 0.25) + 0.25 period_start
, TRUNC(us.shipping_datetime - 0.25) + 1 + (1/24 * 5) + (1/24/60 * 59) period_end
, SUM(us.units) units
FROM units_shipped us
GROUP BY TRUNC(us.shipping_datetime - 0.25)
ORDER BY 1
This simply subtracts 6 hours (0.25 of a day) from each date. If the time is earlier than 6am, the subtraction will make it fall prior to midnight, and when the resultant value is truncated (time element is removed, the date at midnight is returned), it falls within the grouping for the previous day.
Results:
| PERIOD_START | PERIOD_END | UNITS |
-----------------------------------------------------------------------
| April, 22 2013 06:00:00+0000 | April, 23 2013 05:59:00+0000 | 1 |
| April, 23 2013 06:00:00+0000 | April, 24 2013 05:59:00+0000 | 3 |
| April, 24 2013 06:00:00+0000 | April, 25 2013 05:59:00+0000 | 1 |
The bit of dynamic maths in the SELECT is just to help readability of the results. If you don't have a units column to SUM() up, i.e. each row represents a single unit, then substitute COUNT(*) instead.