Summing Moving Range and Criteria, Grouping by Day - sql

What I'm trying to do is sum the last 30 days based on criteria and group by the day, one day of code looks like this:
select
sum(case when f.hire_date__c between '2017-08-01 00:00:00' and '2017-09-01
00:00:00'
and t.createddate between '2017-08-01 00:00:00' and '2017-09-01 00:00:00'
and t.name = 'Request' then 1 else 0 end) as Requests
from case_task_c as t
join case_file_c as f
on f.id = t.case_file__c
I could adjust dates accordingly for the 30 day look back based on today's date, etc. What I can't figure out is to have this query group by day for each day, i.e, yesterdays results, the day prior, etc for the adjusted date ranges.
So far I have this:
select
date(cast(f.hire_date__c as date)),
row_number() over (order by f.hire_date__c desc) as rownumber,
rr.Cancels as Cancels,
qq.hires as hires,
sum(rr.Cancels) over (rows between 1 following and 30 following) as
CumulCancel,
sum(qq.Hires) over (rows between 1 following and 30 following) as Hires
from case_file_c as f
left join(
select
cast(f.hire_date__c as date) as date1,
sum(case when
t.name = 'Cancellation Request' then 1 else 0 end) as Cancels
from case_task_c as t
join case_file_c as f
on f.id = t.case_file__c
group by date1)
as rr
on rr.date1 = cast(f.hire_date__c as date)
left join(
select
cast(f.hire_date__c as date) as date2,
sum(case when f.hire_date__c is not null then 1 else 0 end) as
hires
from sf_case_file_c as f
group by date2) as qq
on qq.date2 = cast(f.hire_date__c as date)
where f.hire_date__c is not null
and f.hire_date__c >= '2017-01-01 00:00:00'
and f.hire_date__c between date_add('day',-30,current_date) and current_date
group by f.hire_date__c, rr.Cancels, qq.hires
order by f.hire_date__c desc
Even using 'current_date - interval -30 day' is just looking up.. the current date.
Using Postgres 8.0.2

Use group by like following. You are converting datetime to date in column selection but not in group by
GROUP BY date(cast(f.hire_date__c as date)),rr.Cancels, qq.hires

Related

How to subtract an offset to dates generated through a recursive CTE?

The query below uses a recursive CTE to generate days if there is no data for that specific day. I want to group the daily downtime total starting at 7:15 the previous day until 7:15 the next day and do it over a month. This query works fine but I need to subtract DATEADD(minute, -(7 * 60 + 15) from each day.
WITH dates as (
SELECT CONVERT(date, 'Anydate') as dte
UNION ALL
SELECT DATEADD(day, 1, dte)
FROM dates
WHERE dte < 'Anydate + 1 month later'
)
SELECT CONVERT(datetime,d.dte), ISNULL(SUM(long_stop_minutes), 0) AS downtime
FROM dates d LEFT JOIN
long_stops_table b
ON CAST(t_stamp as DATE) = d.dte AND Type = 'downtime'
GROUP BY CONVERT(datetime, d.dte)
ORDER BY CONVERT(datetime, d.dte) ASC;
Just subtract the appropriate time units. Here is one way:
SELECT d.dte,
COALESCE(SUM(lst.long_stop_minutes), 0) AS downtime
FROM dates d LEFT JOIN
long_stops_table lst
ON CONVERT(date, DATEADD(minute, -(7 * 60 + 15), lst.t_stamp) = d.dte AND
lst.Type = 'downtime'
GROUP BY d.dte
ORDER BY d.dte ASC;
I see no reason to convert dates.dte to a datetime, so I just removed the conversion.

Aggregating table values by dates in table [SQL]

I have a table with cust_id, sku, date, order_number (if it's that customer's 1st, 2nd...) and amount. My end goal is to find the total spent by each customer during their first year (exact dates will be relative to each customer).
The sales data table is rld_sales with the following columns:
cust_id
sku,
date of purchase,
order number (if it's that customer's 1st or 2nd...)
-- CREATES TABLE OF CUSTOMER_ID AND Y1, Y2 [END DATES]
CREATE TABLE customer_timetable AS (SELECT
rld_sales. "customer_id" AS cust_id,
CAST(date(rld_sales."date") + INTERVAL '1 year' AS DATE) as y1,
CAST(date(rld_sales."date") + INTERVAL '2 year' AS DATE) as y2
FROM
rld_sales
WHERE
transaction_order = 1
GROUP BY
rld_sales. "customer_id",
date
);
-- JOINS CUSTOMER_TIMETABLE WITH RLD_SALES
CREATE TABLE t1 AS (
SELECT
customer_timetable.cust_id,
customer_timetable.y1,
customer_timetable.y2,
rld_sales.*
FROM
customer_timetable
JOIN rld_sales ON (customer_timetable.cust_id = rld_sales.customer_id)
);
SELECT
t1. "cust_id",
SUM(t1. "amount"),
SUM(t2. "amount")
FROM
t1
WHERE
CAST(date AS DATE) BETWEEN y1
AND y2
LEFT JOIN (
SELECT
t1. "amount"
FROM
t1
WHERE
CAST(date AS DATE) > t1. "y2") t2 ON t1. "cust_id" = t2. "cust_id"
GROUP BY
t1. "cust_id"
SELECT
*
FROM
customer_timetable;
So far I've had minimal success creating intermediate tables and joining one at a time, but I feel like there has to be a much more elegant way to make it all happen in a single query.
If I guess you are using MySQL database, This following query will give you some guideline of writing standard SQL to achieve your requirement-
SELECT
A.customer_id,
CASE
WHEN A."date" >= DATE_ADD(CAST(NOW() AS DATE), INTERVAL -1 YEAR) THEN 'Last_Year'
WHEN A."date" >= DATE_ADD(CAST(NOW() AS DATE), INTERVAL -2 YEAR) THEN 'Last_Previous_Year'
ELSE 'Before_That'
END 'Year',
SUM(amount) -- Not sure your amount in which table. Add Table Alias before the coulmn name if required
FROM rld_sales A
INNER JOIN customer_timetable B ON A.customer_id = B.cust_id
GROUP BY
A.customer_id,
CASE
WHEN A."date" >= DATE_ADD(CAST(NOW() AS DATE), INTERVAL -1 YEAR) THEN 'Last_Year'
WHEN A."date" >= DATE_ADD(CAST(NOW() AS DATE), INTERVAL -2 YEAR) THEN 'Last_Previous_Year'
ELSE 'Before_That'
END
To get yearly values per column ID wise, use this following query-
SELECT A.customer_id,
SUM(CASE WHEN A."Year" = 'Last_Year' THEN Amount ELSE 0 END) 'Last_Year',
SUM(CASE WHEN A."Year" = 'Last_Previous_Year' THEN Amount ELSE 0 END) 'Last_Previous_Year',
SUM(CASE WHEN A."Year" = 'Before_That' THEN Amount ELSE 0 END) 'Before_That'
FROM
(
SELECT
A.customer_id,
CASE
WHEN A."date" >= DATE_ADD(CAST(NOW() AS DATE), INTERVAL -1 YEAR) THEN 'Last_Year'
WHEN A."date" >= DATE_ADD(CAST(NOW() AS DATE), INTERVAL -2 YEAR) THEN 'Last_Previous_Year'
ELSE 'Before_That'
END 'Year',
SUM(amount) Amount -- Not sure your amount in which table. Add Table Alias before the coulmn name if required
FROM rld_sales A
INNER JOIN customer_timetable B ON A.customer_id = B.cust_id
GROUP BY
A.customer_id,
CASE
WHEN A."date" >= DATE_ADD(CAST(NOW() AS DATE), INTERVAL -1 YEAR) THEN 'Last_Year'
WHEN A."date" >= DATE_ADD(CAST(NOW() AS DATE), INTERVAL -2 YEAR) THEN 'Last_Previous_Year'
ELSE 'Before_That'
END
)A
GROUP BY A.customer_id
I would write this as:
select s.customer_id, sum(s.amount)
from rld_sales s
where s.date < (select min(s2.date) + interval 1 year
from rld_sales s2
where s2.customer_id = s.customer_id
)
group by s.customer_id;
Temporary tables would only seem to make this query more complicated.

SQL Query to show the same data but based on different dates in the same view

I have an SQL query that I use to show the # of work orders created by day within a designated date range. It works well when I query a single date range, such as 3/1/2016 to 3/31/2016.
SELECT DATEADD(dd, DATEDIFF(dd, 0, dateCreated), 0) the_date,
COUNT(*) work_order_count
FROM WorkOrder
where active = 1 and
dateCreated >= '3/1/2016' and
dateCreated <= '3/31/2016 23:59:59'
GROUP BY DATEADD(dd, DATEDIFF(dd, 0, dateCreated), 0)
order by 1;
I want to take this same query a step further so that I can query the same data but show results for multiple date ranges. My specific purpose is to show data side-by-side for previous year comparison. Ideally I want to show the # of work orders created for 3/1-3/31 in 2014, 2015 and 2016, but all within the same view/result.
Is this possible? I've looked into joins but they appear to be when you are using different tables, not the same one.
Just use conditional aggregation (or a pivot):
SELECT CAST(dateCreated as DATE) as the_date,
SUM(CASE WHEN YEAR(dateCreated) = 2014 THEN 1 ELSE 0 END) as cnt_2014,
SUM(CASE WHEN YEAR(dateCreated) = 2015 THEN 1 ELSE 0 END) as cnt_2015,
SUM(CASE WHEN YEAR(dateCreated) = 2016 THEN 1 ELSE 0 END) as cnt_2016
FROM WorkOrder
WHERE active = 1 and
MONTH(dateCreated) = 3 and
YEAR(dateCreated) in (2014, 2015, 2016)
GROUP BY CAST(dateCreated as DATE)
ORDER BY the_date;
Use pivot table:
;with cte1 as (
select dateCreated, datepart(year, dateCreated) as Y
from WorkOrder
where datepart(month, dateCreated) = 3 and datepart(day, dateCreated) between 5 and 25
)
SELECT *
FROM cte1
PIVOT
(
count(dateCreated)
FOR Y in ([2014], [2015], [2016])
) as pv
How about something like:
SELECT
SUM(
CASE
WHEN dateCreated BETWEEN '3/1/2014' AND '3/31/2014 23:59:59'
THEN 1
ELSE 0
END) AS March2014,
SUM(
CASE
WHEN dateCreated BETWEEN '3/1/2015' AND '3/31/2015 23:59:59'
THEN 1
ELSE 0
END) AS March2015,
SUM(
CASE
WHEN dateCreated BETWEEN '3/1/2016' AND '3/31/2016 23:59:59'
THEN 1
ELSE 0
END) AS March2016
FROM WorkOrder
WHERE active = 1;
SELECT datepart(dd, 'yyyy'), datepart(dd, 'MM'), datepart(dd, 'dd'),
COUNT(*) work_order_count
FROM WorkOrder
where active = 1
and dateCreated >= '3/1/2016'
and dateCreated < '4/1/2020'
GROUP BY datepart(dd, 'yyyy'), datepart(dd, 'MM'), datepart(dd, 'dd')
order by datepart(dd, 'yyyy'), datepart(dd, 'MM'), datepart(dd, 'dd');

Postgresql group month wise with missing values

first an example of my table:
id_object;time;value;status
1;2014-05-22 09:30:00;1234;1
1;2014-05-22 09:31:00;2341;2
1;2014-05-22 09:32:00;1234;1
...
1;2014-06-01 00:00:00;4321;1
...
Now i need count all rows with status=1 and id_object=1 monthwise for example. this is my query:
SELECT COUNT(*)
FROM my_table
WHERE id_object=1
AND status=1
AND extract(YEAR FROM time)=2014
GROUP BY extract(MONTH FROM time)
The result for this example is:
2
1
2 for may and 1 for june but i need a output with all 12 months, also months with no data. for this example i need this ouput:
0 0 0 0 2 1 0 0 0 0 0 0
Thx for help.
you can use generate_series() function like this:
select
g.month,
count(m)
from generate_series(1, 12) as g(month)
left outer join my_table as m on
m.id_object = 1 and
m.status = 1 and
extract(year from m.time) = 2014 and
extract(month from m.time) = g.month
group by g.month
order by g.month
sql fiddle demo
Rather than comparing with an extracted value, you'll want to use a range-table instead. Something that looks like this:
month startOfMonth nextMonth
1 '2014-01-01' '2014-02-01'
2 '2014-02-01' '2014-03-01'
......
12 '2014-12-01' '2015-01-01'
As in #Roman's answer, we'll start with generate_series(), this time using it to generate the range table:
WITH Month_Range AS (SELECT EXTRACT(MONTH FROM month) AS month,
month AS startOfMonth,
month + INTERVAL '1 MONTH' AS nextMonth
FROM generate_series(CAST('2014-01-01' AS DATE),
CAST('2014-12-01' AS DATE),
INTERVAL '1 month') AS mr(month))
SELECT Month_Range.month, COUNT(My_Table)
FROM Month_Range
LEFT JOIN My_Table
ON My_Table.time >= Month_Range.startOfMonth
AND My_Table.time < Month_Range.nextMonth
AND my_table.id_object = 1
AND my_table.status = 1
GROUP BY Month_Range.month
ORDER BY Month_Range.month
(As a side note, I'm now annoyed at how PostgreSQL handles intervals)
SQL Fiddle Demo
The use of the range will allow any index including My_Table.time to be used (although not if an index was built over an EXTRACTed column.
EDIT:
Modified query to take advantage of the fact that generate_series(...) will also handle date/time series.
generate_series can generate timestamp series
select
g.month,
count(t)
from
generate_series(
(select date_trunc('year', min(t.time)) from t),
(select date_trunc('year', max(t.time)) + interval '11 months' from t),
interval '1 month'
) as g(month)
left outer join
t on
t.id_object = 1 and
t.status = 1 and
date_trunc('month', t.time) = g.month
where date_trunc('year', g.month) = '2014-01-01'::date
group by g.month
order by g.month

Multiple Counts from the same row with range

I am trying to get number of transactions within the week given start date and end date.The below query works fine for one day 2011-10-14
SELECT COUNT(operationId) AS trans
FROM hwfg_t_Tracking
WHERE hitTime BETWEEN '2011-10-14 00:00:00' AND '2011-10-14 23:59:59.99'
GO
How can I get the count on operationId where hitTime between 14,13,12,11,10,9,8(1 week) with the single SELECT statement. Like number of transactions for 2011-10-14 as a column, 2011-10-13 as another column and so on
Try the following query for one row per day in your specified range:
SELECT cast(hitTime AS date) AS mydate, COUNT(operationId) AS trans
FROM hwfg_t_Tracking
WHERE hitTime >= '2011-10-14 00:00:00' AND hitTime < '2011-10-21 00:00:00'
GROUP BY cast(hitTime AS date)
Or, if you want them all in one row:
SELECT COUNT(*) AS total_sum
,COUNT(CASE WHEN cast(dt As date) = '2011-10-14' THEN 1 ELSE NULL END) AS day14
,COUNT(CASE WHEN cast(dt As date) = '2011-10-15' THEN 1 ELSE NULL END) AS day15
,COUNT(CASE WHEN cast(dt As date) = '2011-10-16' THEN 1 ELSE NULL END) AS day16
-- etc.
FROM hwfg_t_Tracking
WHERE hitTime >= '2011-10-14 00:00:00' AND hitTime < '2011-10-21 00:00:00'